xref: /plan9/sys/src/cmd/gs/src/gdevxxf.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1993, 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gdevxxf.c,v 1.9 2004/08/04 19:36:12 stefan Exp $ */
18 /* External font (xfont) implementation for X11. */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "x_.h"
22 #include "gx.h"
23 #include "gxdevice.h"
24 #include "gdevx.h"
25 #include "gsstruct.h"
26 #include "gsutil.h"
27 #include "gserrors.h"
28 #include "gxxfont.h"
29 
30 /* Define the smallest point size that we trust X to render reasonably well. */
31 #define min_X_font_size 6
32 /* Define the largest point size where X will do a better job than we can. */
33 #define max_X_font_size 35
34 
35 extern gx_device_X gs_x11_device;
36 
37 extern const byte gs_map_std_to_iso[256];
38 extern const byte gs_map_iso_to_std[256];
39 
40 /* Declare the xfont procedures */
41 private xfont_proc_lookup_font(x_lookup_font);
42 private xfont_proc_char_xglyph(x_char_xglyph);
43 private xfont_proc_char_metrics(x_char_metrics);
44 private xfont_proc_render_char(x_render_char);
45 private xfont_proc_release(x_release);
46 private const gx_xfont_procs x_xfont_procs =
47 {
48     x_lookup_font,
49     x_char_xglyph,
50     x_char_metrics,
51     x_render_char,
52     x_release
53 };
54 
55 /* Return the xfont procedure record. */
56 const gx_xfont_procs *
gdev_x_get_xfont_procs(gx_device * dev)57 gdev_x_get_xfont_procs(gx_device * dev)
58 {
59     return &x_xfont_procs;
60 }
61 
62 /* Define a X11 xfont. */
63 typedef struct x_xfont_s x_xfont;
64 struct x_xfont_s {
65     gx_xfont_common common;
66     gx_device_X *xdev;
67     XFontStruct *font;
68     int encoding_index;
69     int My;
70     int angle;
71 };
72 
73 gs_private_st_dev_ptrs1(st_x_xfont, x_xfont, "x_xfont",
74 			x_xfont_enum_ptrs, x_xfont_reloc_ptrs, xdev);
75 
76 /* ---------------- Utilities ---------------- */
77 
78 /* Search one set of font maps for a font with a given name. */
79 private x11fontmap *
find_fontmap(x11fontmap * fmps,const byte * fname,uint len)80 find_fontmap(x11fontmap *fmps, const byte *fname, uint len)
81 {
82     x11fontmap *fmp = fmps;
83 
84     while (fmp) {
85 	if (len == strlen(fmp->ps_name) &&
86 	    strncmp(fmp->ps_name, (const char *)fname, len) == 0)
87 	    break;
88 	fmp = fmp->next;
89     }
90     return fmp;
91 }
92 
93 /* Find an X font with a given name, encoding, and size. */
94 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)95 find_x_font(gx_device_X *xdev, char x11template[256], x11fontmap *fmp,
96 	    const char *encoding_name, x11fontlist *fls, int xheight,
97 	    bool *scalable_font)
98 {
99     int i;
100     char *x11fontname = 0;
101     int len1 = strlen(fmp->x11_name) + 1;
102 
103     if (fls->count == -1) {
104 	sprintf(x11template, "%s-*-*-*-*-*-*-%s", fmp->x11_name,
105 		encoding_name);
106 	fls->names = XListFonts(xdev->dpy, x11template, 32, &fls->count);
107     }
108     *scalable_font = false;
109     for (i = 0; i < fls->count; i++) {
110 	const char *szp = fls->names[i] + len1;
111 	int size = 0;
112 
113 	while (*szp >= '0' && *szp <= '9')
114 	    size = size * 10 + *szp++ - '0';
115 	if (size == 0) {
116 	    *scalable_font = true;
117 	    continue;
118 	}
119 	if (size == xheight)
120 	    return fls->names[i];
121     }
122     if (*scalable_font && xdev->useScalableFonts) {
123 	sprintf(x11template, "%s-%d-0-0-0-*-0-%s", fmp->x11_name,
124 		xheight, encoding_name);
125 	x11fontname = x11template;
126     }
127     return x11fontname;
128 }
129 
130 /* ---------------- xfont procedures ---------------- */
131 
132 /* Look up a font. */
133 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)134 x_lookup_font(gx_device * dev, const byte * fname, uint len,
135 	    int encoding_index, const gs_uid * puid, const gs_matrix * pmat,
136 	      gs_memory_t * mem)
137 {
138     gx_device_X *xdev = (gx_device_X *) dev;
139     x_xfont *xxf;
140     char x11template[256];
141     char *x11fontname = NULL;
142     XFontStruct *x11font;
143     x11fontmap *fmp;
144     double height;
145     int xwidth, xheight, angle;
146     Boolean My;
147     bool scalable_font;
148 
149     if (!xdev->useXFonts)
150 	return NULL;
151 
152     if (pmat->xy == 0 && pmat->yx == 0) {
153 	xwidth = fabs(pmat->xx * 1000) + 0.5;
154 	xheight = fabs(pmat->yy * 1000) + 0.5;
155 	height = fabs(pmat->yy * 1000);
156 	angle = (pmat->xx > 0 ? 0 : 180);
157 	My = (pmat->xx > 0 && pmat->yy > 0) || (pmat->xx < 0 && pmat->yy < 0);
158     } else if (pmat->xx == 0 && pmat->yy == 0) {
159 	xwidth = fabs(pmat->xy * 1000) + 0.5;
160 	xheight = fabs(pmat->yx * 1000) + 0.5;
161 	height = fabs(pmat->yx * 1000);
162 	angle = (pmat->yx < 0 ? 90 : 270);
163 	My = (pmat->yx > 0 && pmat->xy < 0) || (pmat->yx < 0 && pmat->xy > 0);
164     } else {
165 	return NULL;
166     }
167 
168     /* Don't do very small fonts, where font metrics are way off */
169     /* due to rounding and the server does a very bad job of scaling, */
170     /* or very large fonts, where we can do just as good a job and */
171     /* the server may lock up the entire window system while rasterizing */
172     /* the whole font. */
173     if (xwidth < min_X_font_size || xwidth > max_X_font_size ||
174 	xheight < min_X_font_size || xheight > max_X_font_size
175 	)
176 	return NULL;
177 
178     if (!xdev->useFontExtensions && (My || angle != 0))
179 	return NULL;
180 
181     switch (encoding_index) {
182     case 0:
183 	fmp = find_fontmap(xdev->regular_fonts, fname, len);
184 	if (fmp == NULL)
185 	    return NULL;
186 	x11fontname =
187 	    find_x_font(xdev, x11template, fmp, "Adobe-fontspecific",
188 			&fmp->std, xheight, &scalable_font);
189 	if (!x11fontname) {
190 	    x11fontname =
191 		find_x_font(xdev, x11template, fmp, "ISO8859-1",
192 			    &fmp->iso, xheight, &scalable_font);
193 	    encoding_index = 1;
194 	}
195 	break;
196     case 1:
197 	fmp = find_fontmap(xdev->regular_fonts, fname, len);
198 	if (fmp == NULL)
199 	    return NULL;
200 	x11fontname =
201 	    find_x_font(xdev, x11template, fmp, "ISO8859-1",
202 			&fmp->iso, xheight, &scalable_font);
203 	if (!x11fontname) {
204 	    x11fontname =
205 		find_x_font(xdev, x11template, fmp, "Adobe-fontspecific",
206 			    &fmp->std, xheight, &scalable_font);
207 	    encoding_index = 0;
208 	}
209 	break;
210     case 2:
211 	fmp = xdev->symbol_fonts;
212 	goto sym;
213     case 3:
214 	fmp = xdev->dingbat_fonts;
215 sym:	fmp = find_fontmap(fmp, fname, len);
216 	if (fmp == NULL)
217 	    return NULL;
218 	x11fontname =
219 	    find_x_font(xdev, x11template, fmp, "Adobe-fontspecific",
220 			&fmp->std, xheight, &scalable_font);
221     default:
222 	return NULL;
223     }
224     if (!x11fontname)
225 	return NULL;
226 
227     if (xwidth != xheight || angle != 0 || My) {
228 	if (!xdev->useScalableFonts || !scalable_font)
229 	    return NULL;
230 	sprintf(x11template, "%s%s+%d-%d+%d-0-0-0-*-0-%s",
231 		fmp->x11_name, (My ? "+My" : ""),
232 		angle * 64, xheight, xwidth,
233 		(encoding_index == 1 ? "ISO8859-1" : "Adobe-fontspecific"));
234 	x11fontname = x11template;
235     }
236     x11font = XLoadQueryFont(xdev->dpy, x11fontname);
237     if (x11font == NULL)
238 	return NULL;
239     /* Don't bother with 16-bit or 2 byte fonts yet */
240     if (x11font->min_byte1 || x11font->max_byte1) {
241 	XFreeFont(xdev->dpy, x11font);
242 	return NULL;
243     }
244     xxf = gs_alloc_struct(mem, x_xfont, &st_x_xfont, "x_lookup_font");
245     if (xxf == NULL)
246 	return NULL;
247     xxf->common.procs = &x_xfont_procs;
248     xxf->xdev = xdev;
249     xxf->font = x11font;
250     xxf->encoding_index = encoding_index;
251     xxf->My = (My ? -1 : 1);
252     xxf->angle = angle;
253     if (xdev->logXFonts) {
254 	dprintf3("Using %s\n  for %s at %g pixels.\n", x11fontname,
255 		 fmp->ps_name, height);
256 	dflush();
257     }
258     return (gx_xfont *) xxf;
259 }
260 
261 /* Convert a character name or index to an xglyph code. */
262 private gx_xglyph
x_char_xglyph(gx_xfont * xf,gs_char chr,int encoding_index,gs_glyph glyph,const gs_const_string * glyph_name)263 x_char_xglyph(gx_xfont * xf, gs_char chr, int encoding_index,
264 	      gs_glyph glyph, const gs_const_string *glyph_name)
265 {
266     const x_xfont *xxf = (x_xfont *) xf;
267 
268     if (chr == gs_no_char)
269 	return gx_no_xglyph;	/* can't look up names yet */
270     if (encoding_index != xxf->encoding_index) {
271 	if (encoding_index == 0 && xxf->encoding_index == 1)
272 	    chr = gs_map_std_to_iso[chr];
273 	else if (encoding_index == 1 && xxf->encoding_index == 0)
274 	    chr = gs_map_iso_to_std[chr];
275 	else
276 	    return gx_no_xglyph;
277 	if (chr == 0)
278 	    return gx_no_xglyph;
279     }
280     if (chr < xxf->font->min_char_or_byte2 ||
281 	chr > xxf->font->max_char_or_byte2)
282 	return gx_no_xglyph;
283     if (xxf->font->per_char) {
284 	int i = chr - xxf->font->min_char_or_byte2;
285 	const XCharStruct *xc = &xxf->font->per_char[i];
286 
287 	if ((xc->lbearing == 0) && (xc->rbearing == 0) &&
288 	    (xc->ascent == 0) && (xc->descent == 0))
289 	    return gx_no_xglyph;
290     }
291     return (gx_xglyph) chr;
292 }
293 
294 /* Get the metrics for a character. */
295 private int
x_char_metrics(gx_xfont * xf,gx_xglyph xg,int wmode,gs_point * pwidth,gs_int_rect * pbbox)296 x_char_metrics(gx_xfont * xf, gx_xglyph xg, int wmode,
297 	       gs_point * pwidth, gs_int_rect * pbbox)
298 {
299     const x_xfont *xxf = (const x_xfont *) xf;
300     int width;
301 
302     if (wmode != 0)
303 	return gs_error_undefined;
304     if (xxf->font->per_char == NULL) {
305 	width = xxf->font->max_bounds.width;
306 	pbbox->p.x = xxf->font->max_bounds.lbearing;
307 	pbbox->q.x = xxf->font->max_bounds.rbearing;
308 	pbbox->p.y = -xxf->font->max_bounds.ascent;
309 	pbbox->q.y = xxf->font->max_bounds.descent;
310     } else {
311 	int i = xg - xxf->font->min_char_or_byte2;
312 	const XCharStruct *xc = &xxf->font->per_char[i];
313 
314 	width = xc->width;
315 	pbbox->p.x = xc->lbearing;
316 	pbbox->q.x = xc->rbearing;
317 	pbbox->p.y = -xc->ascent;
318 	pbbox->q.y = xc->descent;
319     }
320     switch (xxf->angle) {
321     case 0:
322 	pwidth->x = width, pwidth->y = 0; break;
323     case 90:
324 	pwidth->x = 0, pwidth->y = -xxf->My * width; break;
325     case 180:
326 	pwidth->x = -width, pwidth->y = 0; break;
327     case 270:
328 	pwidth->x = 0, pwidth->y = xxf->My * width; break;
329     }
330     return 0;
331 }
332 
333 /* Render a character. */
334 private int
x_render_char(gx_xfont * xf,gx_xglyph xg,gx_device * dev,int xo,int yo,gx_color_index color,int required)335 x_render_char(gx_xfont * xf, gx_xglyph xg, gx_device * dev,
336 	      int xo, int yo, gx_color_index color, int required)
337 {
338     x_xfont *xxf = (x_xfont *) xf;
339     char chr = (char)xg;
340     gs_point wxy;
341     gs_int_rect bbox;
342     int x, y, w, h;
343     int code;
344 
345     if (dev->dname == gs_x11_device.dname && !((gx_device_X *)dev)->is_buffered) {
346 	gx_device_X *xdev = (gx_device_X *)dev;
347 
348 	code = (*xf->common.procs->char_metrics) (xf, xg, 0, &wxy, &bbox);
349 	if (code < 0)
350 	    return code;
351 	/* Buffer text for more efficient X interaction. */
352 	if (xdev->text.item_count == MAX_TEXT_ITEMS ||
353 	    xdev->text.char_count == MAX_TEXT_CHARS ||
354 	    (IN_TEXT(xdev) &&
355 	     (yo != xdev->text.origin.y || color != xdev->fore_color ||
356 	      xxf->font->fid != xdev->fid))
357 	    ) {
358 	    DRAW_TEXT(xdev);
359 	    xdev->text.item_count = xdev->text.char_count = 0;
360 	}
361 	if (xdev->text.item_count == 0) {
362 	    X_SET_FILL_STYLE(xdev, FillSolid);
363 	    X_SET_FORE_COLOR(xdev, color);
364 	    X_SET_FUNCTION(xdev, GXcopy);
365 	    xdev->text.origin.x = xdev->text.x = xo;
366 	    xdev->text.origin.y = yo;
367 	    xdev->text.items[0].font = xdev->fid = xxf->font->fid;
368 	}
369 	/*
370 	 * The following is wrong for rotated text, but it doesn't matter,
371 	 * because the next call of x_render_char will have a different Y.
372 	 */
373 	{
374 	    int index = xdev->text.item_count;
375 	    XTextItem *item = &xdev->text.items[index];
376 	    char *pchar = &xdev->text.chars[xdev->text.char_count++];
377 	    int delta = xo - xdev->text.x;
378 
379 	    *pchar = chr;
380 	    if (index > 0 && delta == 0) {
381 		/* Continue the same item. */
382 		item[-1].nchars++;
383 	    } else {
384 		/* Start a new item. */
385 		item->chars = pchar;
386 		item->nchars = 1;
387 		item->delta = delta;
388 		if (index > 0)
389 		    item->font = None;
390 		xdev->text.item_count++;
391 	    }
392 	    xdev->text.x = xo + wxy.x;
393 	}
394 	if (xdev->bpixmap != (Pixmap) 0) {
395 	    x = xo + bbox.p.x;
396 	    y = yo + bbox.p.y;
397 	    w = bbox.q.x - bbox.p.x;
398 	    h = bbox.q.y - bbox.p.y;
399 	    fit_fill(dev, x, y, w, h);
400 	    x_update_add(xdev, x, y, w, h);
401 	}
402 	return 0;
403     } else if (!required)
404 	return -1;		/* too hard */
405     else {
406 	/* Display on an intermediate bitmap, then copy the bits. */
407 	gx_device_X *xdev = xxf->xdev;
408 	int wbm, raster;
409 	int i;
410 	XImage *xim;
411 	Pixmap xpm;
412 	GC fgc;
413 	byte *bits;
414 
415 	dev_proc_copy_mono((*copy_mono)) = dev_proc(dev, copy_mono);
416 
417 	code = (*xf->common.procs->char_metrics) (xf, xg, 0, &wxy, &bbox);
418 	if (code < 0)
419 	    return code;
420 	w = bbox.q.x - bbox.p.x;
421 	h = bbox.q.y - bbox.p.y;
422 	wbm = ROUND_UP(w, align_bitmap_mod * 8);
423 	raster = wbm >> 3;
424 	bits = (byte *) gs_malloc(xdev->memory, h, raster, "x_render_char");
425 	if (bits == 0)
426 	    return gs_error_limitcheck;
427 	xpm = XCreatePixmap(xdev->dpy, xdev->win, w, h, 1);
428 	fgc = XCreateGC(xdev->dpy, xpm, None, NULL);
429 	XSetForeground(xdev->dpy, fgc, 0);
430 	XFillRectangle(xdev->dpy, xpm, fgc, 0, 0, w, h);
431 	XSetForeground(xdev->dpy, fgc, 1);
432 	XSetFont(xdev->dpy, fgc, xxf->font->fid);
433 	XDrawString(xdev->dpy, xpm, fgc, -bbox.p.x, -bbox.p.y, &chr, 1);
434 	xim = XGetImage(xdev->dpy, xpm, 0, 0, w, h, 1, ZPixmap);
435 	i = 0;
436 	for (y = 0; y < h; y++) {
437 	    char b = 0;
438 
439 	    for (x = 0; x < wbm; x++) {
440 		b = b << 1;
441 		if (x < w)
442 		    b += XGetPixel(xim, x, y);
443 		if ((x & 7) == 7)
444 		    bits[i++] = b;
445 	    }
446 	}
447 	code = (*copy_mono) (dev, bits, 0, raster, gx_no_bitmap_id,
448 			     xo + bbox.p.x, yo + bbox.p.y, w, h,
449 			     gx_no_color_index, color);
450 	gs_free(xdev->memory, (char *)bits, h, raster, "x_render_char");
451 	XFreePixmap(xdev->dpy, xpm);
452 	XFreeGC(xdev->dpy, fgc);
453 	XDestroyImage(xim);
454 	return (code < 0 ? code : 0);
455     }
456 }
457 
458 /* Release an xfont. */
459 private int
x_release(gx_xfont * xf,gs_memory_t * mem)460 x_release(gx_xfont * xf, gs_memory_t * mem)
461 {
462 #if 0
463     /* The device may not be open.  Cannot reliably free the font. */
464     x_xfont *xxf = (x_xfont *) xf;
465 
466     XFreeFont(xxf->xdev->dpy, xxf->font);
467 #endif
468     if (mem != NULL)
469 	gs_free_object(mem, xf, "x_release");
470     return 0;
471 }
472