/* Copyright (C) 1993, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevxxf.c,v 1.5.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */ /* External font (xfont) implementation for X11. */ #include "math_.h" #include "memory_.h" #include "x_.h" #include "gx.h" #include "gxdevice.h" #include "gdevx.h" #include "gsstruct.h" #include "gsutil.h" #include "gserrors.h" #include "gxxfont.h" /* Define the smallest point size that we trust X to render reasonably well. */ #define min_X_font_size 6 /* Define the largest point size where X will do a better job than we can. */ #define max_X_font_size 35 extern gx_device_X gs_x11_device; extern const byte gs_map_std_to_iso[256]; extern const byte gs_map_iso_to_std[256]; /* Declare the xfont procedures */ private xfont_proc_lookup_font(x_lookup_font); private xfont_proc_char_xglyph(x_char_xglyph); private xfont_proc_char_metrics(x_char_metrics); private xfont_proc_render_char(x_render_char); private xfont_proc_release(x_release); private const gx_xfont_procs x_xfont_procs = { x_lookup_font, x_char_xglyph, x_char_metrics, x_render_char, x_release }; /* Return the xfont procedure record. */ const gx_xfont_procs * gdev_x_get_xfont_procs(gx_device * dev) { return &x_xfont_procs; } /* Define a X11 xfont. */ typedef struct x_xfont_s x_xfont; struct x_xfont_s { gx_xfont_common common; gx_device_X *xdev; XFontStruct *font; int encoding_index; int My; int angle; }; gs_private_st_dev_ptrs1(st_x_xfont, x_xfont, "x_xfont", x_xfont_enum_ptrs, x_xfont_reloc_ptrs, xdev); /* ---------------- Utilities ---------------- */ /* Search one set of font maps for a font with a given name. */ private x11fontmap * find_fontmap(x11fontmap *fmps, const byte *fname, uint len) { x11fontmap *fmp = fmps; while (fmp) { if (len == strlen(fmp->ps_name) && strncmp(fmp->ps_name, (const char *)fname, len) == 0) break; fmp = fmp->next; } return fmp; } /* Find an X font with a given name, encoding, and size. */ private char * find_x_font(gx_device_X *xdev, char x11template[256], x11fontmap *fmp, const char *encoding_name, x11fontlist *fls, int xheight, bool *scalable_font) { int i; char *x11fontname = 0; int len1 = strlen(fmp->x11_name) + 1; if (fls->count == -1) { sprintf(x11template, "%s-*-*-*-*-*-*-%s", fmp->x11_name, encoding_name); fls->names = XListFonts(xdev->dpy, x11template, 32, &fls->count); } *scalable_font = false; for (i = 0; i < fls->count; i++) { const char *szp = fls->names[i] + len1; int size = 0; while (*szp >= '0' && *szp <= '9') size = size * 10 + *szp++ - '0'; if (size == 0) { *scalable_font = true; continue; } if (size == xheight) return fls->names[i]; } if (*scalable_font && xdev->useScalableFonts) { sprintf(x11template, "%s-%d-0-0-0-*-0-%s", fmp->x11_name, xheight, encoding_name); x11fontname = x11template; } return x11fontname; } /* ---------------- xfont procedures ---------------- */ /* Look up a font. */ private gx_xfont * x_lookup_font(gx_device * dev, const byte * fname, uint len, int encoding_index, const gs_uid * puid, const gs_matrix * pmat, gs_memory_t * mem) { gx_device_X *xdev = (gx_device_X *) dev; x_xfont *xxf; char x11template[256]; char *x11fontname = NULL; XFontStruct *x11font; x11fontmap *fmp; double height; int xwidth, xheight, angle; Boolean My; bool scalable_font; if (!xdev->useXFonts) return NULL; if (pmat->xy == 0 && pmat->yx == 0) { xwidth = fabs(pmat->xx * 1000) + 0.5; xheight = fabs(pmat->yy * 1000) + 0.5; height = fabs(pmat->yy * 1000); angle = (pmat->xx > 0 ? 0 : 180); My = (pmat->xx > 0 && pmat->yy > 0) || (pmat->xx < 0 && pmat->yy < 0); } else if (pmat->xx == 0 && pmat->yy == 0) { xwidth = fabs(pmat->xy * 1000) + 0.5; xheight = fabs(pmat->yx * 1000) + 0.5; height = fabs(pmat->yx * 1000); angle = (pmat->yx < 0 ? 90 : 270); My = (pmat->yx > 0 && pmat->xy < 0) || (pmat->yx < 0 && pmat->xy > 0); } else { return NULL; } /* Don't do very small fonts, where font metrics are way off */ /* due to rounding and the server does a very bad job of scaling, */ /* or very large fonts, where we can do just as good a job and */ /* the server may lock up the entire window system while rasterizing */ /* the whole font. */ if (xwidth < min_X_font_size || xwidth > max_X_font_size || xheight < min_X_font_size || xheight > max_X_font_size ) return NULL; if (!xdev->useFontExtensions && (My || angle != 0)) return NULL; switch (encoding_index) { case 0: fmp = find_fontmap(xdev->regular_fonts, fname, len); if (fmp == NULL) return NULL; x11fontname = find_x_font(xdev, x11template, fmp, "Adobe-fontspecific", &fmp->std, xheight, &scalable_font); if (!x11fontname) { x11fontname = find_x_font(xdev, x11template, fmp, "ISO8859-1", &fmp->iso, xheight, &scalable_font); encoding_index = 1; } break; case 1: fmp = find_fontmap(xdev->regular_fonts, fname, len); if (fmp == NULL) return NULL; x11fontname = find_x_font(xdev, x11template, fmp, "ISO8859-1", &fmp->iso, xheight, &scalable_font); if (!x11fontname) { x11fontname = find_x_font(xdev, x11template, fmp, "Adobe-fontspecific", &fmp->std, xheight, &scalable_font); encoding_index = 0; } break; case 2: fmp = xdev->symbol_fonts; goto sym; case 3: fmp = xdev->dingbat_fonts; sym: fmp = find_fontmap(fmp, fname, len); if (fmp == NULL) return NULL; x11fontname = find_x_font(xdev, x11template, fmp, "Adobe-fontspecific", &fmp->std, xheight, &scalable_font); default: return NULL; } if (!x11fontname) return NULL; if (xwidth != xheight || angle != 0 || My) { if (!xdev->useScalableFonts || !scalable_font) return NULL; sprintf(x11template, "%s%s+%d-%d+%d-0-0-0-*-0-%s", fmp->x11_name, (My ? "+My" : ""), angle * 64, xheight, xwidth, (encoding_index == 1 ? "ISO8859-1" : "Adobe-fontspecific")); x11fontname = x11template; } x11font = XLoadQueryFont(xdev->dpy, x11fontname); if (x11font == NULL) return NULL; /* Don't bother with 16-bit or 2 byte fonts yet */ if (x11font->min_byte1 || x11font->max_byte1) { XFreeFont(xdev->dpy, x11font); return NULL; } xxf = gs_alloc_struct(mem, x_xfont, &st_x_xfont, "x_lookup_font"); if (xxf == NULL) return NULL; xxf->common.procs = &x_xfont_procs; xxf->xdev = xdev; xxf->font = x11font; xxf->encoding_index = encoding_index; xxf->My = (My ? -1 : 1); xxf->angle = angle; if (xdev->logXFonts) { dprintf3("Using %s\n for %s at %g pixels.\n", x11fontname, fmp->ps_name, height); dflush(); } return (gx_xfont *) xxf; } /* Convert a character name or index to an xglyph code. */ private gx_xglyph x_char_xglyph(gx_xfont * xf, gs_char chr, int encoding_index, gs_glyph glyph, gs_proc_glyph_name_t glyph_name_proc) { const x_xfont *xxf = (x_xfont *) xf; if (chr == gs_no_char) return gx_no_xglyph; /* can't look up names yet */ if (encoding_index != xxf->encoding_index) { if (encoding_index == 0 && xxf->encoding_index == 1) chr = gs_map_std_to_iso[chr]; else if (encoding_index == 1 && xxf->encoding_index == 0) chr = gs_map_iso_to_std[chr]; else return gx_no_xglyph; if (chr == 0) return gx_no_xglyph; } if (chr < xxf->font->min_char_or_byte2 || chr > xxf->font->max_char_or_byte2) return gx_no_xglyph; if (xxf->font->per_char) { int i = chr - xxf->font->min_char_or_byte2; const XCharStruct *xc = &xxf->font->per_char[i]; if ((xc->lbearing == 0) && (xc->rbearing == 0) && (xc->ascent == 0) && (xc->descent == 0)) return gx_no_xglyph; } return (gx_xglyph) chr; } /* Get the metrics for a character. */ private int x_char_metrics(gx_xfont * xf, gx_xglyph xg, int wmode, gs_point * pwidth, gs_int_rect * pbbox) { const x_xfont *xxf = (const x_xfont *) xf; int width; if (wmode != 0) return gs_error_undefined; if (xxf->font->per_char == NULL) { width = xxf->font->max_bounds.width; pbbox->p.x = xxf->font->max_bounds.lbearing; pbbox->q.x = xxf->font->max_bounds.rbearing; pbbox->p.y = -xxf->font->max_bounds.ascent; pbbox->q.y = xxf->font->max_bounds.descent; } else { int i = xg - xxf->font->min_char_or_byte2; const XCharStruct *xc = &xxf->font->per_char[i]; width = xc->width; pbbox->p.x = xc->lbearing; pbbox->q.x = xc->rbearing; pbbox->p.y = -xc->ascent; pbbox->q.y = xc->descent; } switch (xxf->angle) { case 0: pwidth->x = width, pwidth->y = 0; break; case 90: pwidth->x = 0, pwidth->y = -xxf->My * width; break; case 180: pwidth->x = -width, pwidth->y = 0; break; case 270: pwidth->x = 0, pwidth->y = xxf->My * width; break; } return 0; } /* Render a character. */ private int x_render_char(gx_xfont * xf, gx_xglyph xg, gx_device * dev, int xo, int yo, gx_color_index color, int required) { x_xfont *xxf = (x_xfont *) xf; char chr = (char)xg; gs_point wxy; gs_int_rect bbox; int x, y, w, h; int code; if (dev->dname == gs_x11_device.dname && !((gx_device_X *)dev)->is_buffered) { gx_device_X *xdev = (gx_device_X *)dev; code = (*xf->common.procs->char_metrics) (xf, xg, 0, &wxy, &bbox); if (code < 0) return code; /* Buffer text for more efficient X interaction. */ if (xdev->text.item_count == MAX_TEXT_ITEMS || xdev->text.char_count == MAX_TEXT_CHARS || (IN_TEXT(xdev) && (yo != xdev->text.origin.y || color != xdev->fore_color || xxf->font->fid != xdev->fid)) ) { DRAW_TEXT(xdev); xdev->text.item_count = xdev->text.char_count = 0; } if (xdev->text.item_count == 0) { X_SET_FILL_STYLE(xdev, FillSolid); X_SET_FORE_COLOR(xdev, color); X_SET_FUNCTION(xdev, GXcopy); xdev->text.origin.x = xdev->text.x = xo; xdev->text.origin.y = yo; xdev->text.items[0].font = xdev->fid = xxf->font->fid; } /* * The following is wrong for rotated text, but it doesn't matter, * because the next call of x_render_char will have a different Y. */ { int index = xdev->text.item_count; XTextItem *item = &xdev->text.items[index]; char *pchar = &xdev->text.chars[xdev->text.char_count++]; int delta = xo - xdev->text.x; *pchar = chr; if (index > 0 && delta == 0) { /* Continue the same item. */ item[-1].nchars++; } else { /* Start a new item. */ item->chars = pchar; item->nchars = 1; item->delta = delta; if (index > 0) item->font = None; xdev->text.item_count++; } xdev->text.x = xo + wxy.x; } if (xdev->bpixmap != (Pixmap) 0) { x = xo + bbox.p.x; y = yo + bbox.p.y; w = bbox.q.x - bbox.p.x; h = bbox.q.y - bbox.p.y; fit_fill(dev, x, y, w, h); x_update_add(xdev, x, y, w, h); } return 0; } else if (!required) return -1; /* too hard */ else { /* Display on an intermediate bitmap, then copy the bits. */ gx_device_X *xdev = xxf->xdev; int wbm, raster; int i; XImage *xim; Pixmap xpm; GC fgc; byte *bits; dev_proc_copy_mono((*copy_mono)) = dev_proc(dev, copy_mono); code = (*xf->common.procs->char_metrics) (xf, xg, 0, &wxy, &bbox); if (code < 0) return code; w = bbox.q.x - bbox.p.x; h = bbox.q.y - bbox.p.y; wbm = ROUND_UP(w, align_bitmap_mod * 8); raster = wbm >> 3; bits = (byte *) gs_malloc(h, raster, "x_render_char"); if (bits == 0) return gs_error_limitcheck; xpm = XCreatePixmap(xdev->dpy, xdev->win, w, h, 1); fgc = XCreateGC(xdev->dpy, xpm, None, NULL); XSetForeground(xdev->dpy, fgc, 0); XFillRectangle(xdev->dpy, xpm, fgc, 0, 0, w, h); XSetForeground(xdev->dpy, fgc, 1); XSetFont(xdev->dpy, fgc, xxf->font->fid); XDrawString(xdev->dpy, xpm, fgc, -bbox.p.x, -bbox.p.y, &chr, 1); xim = XGetImage(xdev->dpy, xpm, 0, 0, w, h, 1, ZPixmap); i = 0; for (y = 0; y < h; y++) { char b = 0; for (x = 0; x < wbm; x++) { b = b << 1; if (x < w) b += XGetPixel(xim, x, y); if ((x & 7) == 7) bits[i++] = b; } } code = (*copy_mono) (dev, bits, 0, raster, gx_no_bitmap_id, xo + bbox.p.x, yo + bbox.p.y, w, h, gx_no_color_index, color); gs_free((char *)bits, h, raster, "x_render_char"); XFreePixmap(xdev->dpy, xpm); XFreeGC(xdev->dpy, fgc); XDestroyImage(xim); return (code < 0 ? code : 0); } } /* Release an xfont. */ private int x_release(gx_xfont * xf, gs_memory_t * mem) { #if 0 /* The device may not be open. Cannot reliably free the font. */ x_xfont *xxf = (x_xfont *) xf; XFreeFont(xxf->xdev->dpy, xxf->font); #endif if (mem != NULL) gs_free_object(mem, xf, "x_release"); return 0; }