1 /* pangox.c: Routines for handling X fonts
2  *
3  * Copyright (C) 1999 Red Hat Software
4  * Copyright (C) 2000 SuSE Linux Ltd
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 #include <string.h>
23 #include <stdlib.h>
24 #include <math.h>
25 
26 #include <X11/Xlib.h>
27 
28 
29 #include "pango-impl-utils.h"
30 #include "pangox-private.h"
31 
32 #define PANGO_TYPE_X_FONT              (pango_x_font_get_type ())
33 #define PANGO_X_FONT(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_X_FONT, PangoXFont))
34 #define PANGO_X_FONT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_X_FONT, PangoXFontClass))
35 #define PANGO_X_IS_FONT(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_TYPE_X_FONT))
36 #define PANGO_X_IS_FONT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_X_FONT))
37 #define PANGO_X_FONT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_X_FONT, PangoXFontClass))
38 
39 typedef struct _PangoXFontClass   PangoXFontClass;
40 typedef struct _PangoXMetricsInfo PangoXMetricsInfo;
41 typedef struct _PangoXContextInfo PangoXContextInfo;
42 
43 struct _PangoXSubfontInfo
44 {
45   char *xlfd;
46   XFontStruct *font_struct;
47   gboolean     is_1byte;
48   int          range_byte1;
49   int          range_byte2;
50 };
51 
52 struct _PangoXMetricsInfo
53 {
54   const char *sample_str;
55   PangoFontMetrics *metrics;
56 };
57 
58 struct _PangoXContextInfo
59 {
60   PangoGetGCFunc  get_gc_func;
61   PangoFreeGCFunc free_gc_func;
62 };
63 
64 struct _PangoXFontClass
65 {
66   PangoFontClass parent_class;
67 };
68 
69 static PangoFontClass *parent_class;	/* Parent class structure for PangoXFont */
70 
71 static void pango_x_font_class_init (PangoXFontClass *class);
72 static void pango_x_font_init       (PangoXFont      *xfont);
73 static void pango_x_font_dispose    (GObject         *object);
74 static void pango_x_font_finalize   (GObject         *object);
75 
76 static PangoFontDescription *pango_x_font_describe          (PangoFont        *font);
77 static PangoCoverage *       pango_x_font_get_coverage      (PangoFont        *font,
78 							     PangoLanguage    *language);
79 static PangoEngineShape *    pango_x_font_find_shaper       (PangoFont        *font,
80 							     PangoLanguage    *language,
81 							     guint32           ch);
82 static void                  pango_x_font_get_glyph_extents (PangoFont        *font,
83 							     PangoGlyph        glyph,
84 							     PangoRectangle   *ink_rect,
85 							     PangoRectangle   *logical_rect);
86 static PangoFontMetrics *    pango_x_font_get_metrics       (PangoFont        *font,
87 							     PangoLanguage    *language);
88 static PangoFontMap *        pango_x_font_get_font_map      (PangoFont        *font);
89 
90 static PangoXSubfontInfo * pango_x_find_subfont    (PangoFont          *font,
91 						    PangoXSubfont       subfont_index);
92 static XCharStruct *       pango_x_get_per_char    (PangoFont          *font,
93 						    PangoXSubfontInfo  *subfont,
94 						    guint16             char_index);
95 static gboolean            pango_x_find_glyph      (PangoFont          *font,
96 						    PangoGlyph          glyph,
97 						    PangoXSubfontInfo **subfont_return,
98 						    XCharStruct       **charstruct_return);
99 static XFontStruct *       pango_x_get_font_struct (PangoFont          *font,
100 						    PangoXSubfontInfo  *info);
101 
102 static void     pango_x_get_item_properties (PangoItem      *item,
103 					     PangoUnderline *uline,
104 					     PangoAttrColor *fg_color,
105 					     gboolean       *fg_set,
106 					     PangoAttrColor *bg_color,
107 					     gboolean       *bg_set);
108 
109 static inline PangoXSubfontInfo *
pango_x_find_subfont(PangoFont * font,PangoXSubfont subfont_index)110 pango_x_find_subfont (PangoFont  *font,
111 		      PangoXSubfont subfont_index)
112 {
113   PangoXFont *xfont = (PangoXFont *)font;
114 
115   if (subfont_index < 1 || subfont_index > xfont->n_subfonts)
116     return NULL;
117 
118   return xfont->subfonts[subfont_index-1];
119 }
120 
121 static void
pango_x_make_font_struct(PangoFont * font,PangoXSubfontInfo * info)122 pango_x_make_font_struct (PangoFont *font, PangoXSubfontInfo *info)
123 {
124   PangoXFont *xfont = (PangoXFont *)font;
125   PangoXFontCache *cache;
126 
127   cache = pango_x_font_map_get_font_cache (xfont->fontmap);
128 
129   info->font_struct = pango_x_font_cache_load (cache, info->xlfd);
130   if (!info->font_struct)
131     {
132       g_warning ("Cannot load font for XLFD '%s\n", info->xlfd);
133 
134       /* Prevent a segfault, but probably not much more */
135       info->font_struct = pango_x_font_cache_load (cache, "fixed");
136     }
137 
138   info->is_1byte = (info->font_struct->min_byte1 == 0 && info->font_struct->max_byte1 == 0);
139   info->range_byte1 = info->font_struct->max_byte1 - info->font_struct->min_byte1 + 1;
140   info->range_byte2 = info->font_struct->max_char_or_byte2 - info->font_struct->min_char_or_byte2 + 1;
141 }
142 
143 static inline XFontStruct *
pango_x_get_font_struct(PangoFont * font,PangoXSubfontInfo * info)144 pango_x_get_font_struct (PangoFont *font, PangoXSubfontInfo *info)
145 {
146   if (!info->font_struct)
147     pango_x_make_font_struct (font, info);
148 
149   return info->font_struct;
150 }
151 
152 static void
free_context_info(PangoXContextInfo * info)153 free_context_info (PangoXContextInfo *info)
154 {
155   g_slice_free (PangoXContextInfo, info);
156 }
157 
158 static PangoXContextInfo *
get_context_info(PangoContext * context)159 get_context_info (PangoContext *context)
160 {
161   PangoXContextInfo *info;
162   static GQuark quark = 0;
163 
164   if (G_UNLIKELY (!quark))
165     quark = g_quark_from_static_string ("pango-x-info");
166 
167   info =  g_object_get_qdata (G_OBJECT (context), quark);
168 
169   if (G_UNLIKELY (!info))
170     {
171       info = g_slice_new (PangoXContextInfo);
172       info->get_gc_func = NULL;
173       info->free_gc_func = NULL;
174       g_object_set_qdata_full (G_OBJECT (context),
175 			       quark,
176 			       info, (GDestroyNotify)free_context_info);
177     }
178 
179   return info;
180 }
181 
182 /**
183  * pango_x_get_context:
184  * @display: an X display (As returned by XOpenDisplay().)
185  *
186  * Retrieves a #PangoContext appropriate for rendering with X fonts on the
187  * given display.
188  *
189  * Return value: the new #PangoContext.
190  *
191  * Deprecated: 1.22: Use pango_x_font_map_for_display() followed by
192  * pango_font_map_create_context() instead.
193  **/
194 PangoContext *
pango_x_get_context(Display * display)195 pango_x_get_context (Display *display)
196 {
197   return pango_font_map_create_context (pango_x_font_map_for_display (display));
198 }
199 
200 /**
201  * pango_x_context_set_funcs:
202  * @context: a #PangoContext.
203  * @get_gc_func: function called to create a new GC for a given color.
204  * @free_gc_func: function called to free a GC created with @get_gc_func.
205  *
206  * Sets the functions that will be used to get GC's in various colors when
207  * rendering layouts with this context.
208  **/
209 void
pango_x_context_set_funcs(PangoContext * context,PangoGetGCFunc get_gc_func,PangoFreeGCFunc free_gc_func)210 pango_x_context_set_funcs  (PangoContext     *context,
211 			    PangoGetGCFunc    get_gc_func,
212 			    PangoFreeGCFunc   free_gc_func)
213 {
214   PangoXContextInfo *info;
215 
216   g_return_if_fail (context != NULL);
217 
218   info = get_context_info (context);
219 
220   info->get_gc_func = get_gc_func;
221   info->free_gc_func = free_gc_func;
222 }
223 
224 static GType
pango_x_font_get_type(void)225 pango_x_font_get_type (void)
226 {
227   static GType object_type = 0;
228 
229   if (G_UNLIKELY (!object_type))
230     {
231       const GTypeInfo object_info =
232       {
233 	sizeof (PangoXFontClass),
234 	(GBaseInitFunc) NULL,
235 	(GBaseFinalizeFunc) NULL,
236 	(GClassInitFunc) pango_x_font_class_init,
237 	NULL,           /* class_finalize */
238 	NULL,           /* class_data */
239 	sizeof (PangoXFont),
240 	0,              /* n_preallocs */
241 	(GInstanceInitFunc) pango_x_font_init,
242 	NULL            /* value_table */
243       };
244 
245       object_type = g_type_register_static (PANGO_TYPE_FONT,
246 					    I_("PangoXFont"),
247 					    &object_info, 0);
248     }
249 
250   return object_type;
251 }
252 
253 static void
pango_x_font_init(PangoXFont * xfont)254 pango_x_font_init (PangoXFont *xfont)
255 {
256   xfont->subfonts_by_charset = g_hash_table_new (g_str_hash, g_str_equal);
257 
258   xfont->n_subfonts = 0;
259   xfont->max_subfonts = 1;
260 
261   xfont->subfonts = g_new (PangoXSubfontInfo *, xfont->max_subfonts);
262 
263   xfont->metrics_by_lang = NULL;
264 
265   xfont->size = -1;
266   xfont->xface = NULL;
267 }
268 
269 static void
pango_x_font_class_init(PangoXFontClass * class)270 pango_x_font_class_init (PangoXFontClass *class)
271 {
272   GObjectClass *object_class = G_OBJECT_CLASS (class);
273   PangoFontClass *font_class = PANGO_FONT_CLASS (class);
274 
275   parent_class = g_type_class_peek_parent (class);
276 
277   object_class->finalize = pango_x_font_finalize;
278   object_class->dispose = pango_x_font_dispose;
279 
280   font_class->describe = pango_x_font_describe;
281   font_class->get_coverage = pango_x_font_get_coverage;
282   /* font_class->find_shaper = pango_x_font_find_shaper; */
283   font_class->get_glyph_extents = pango_x_font_get_glyph_extents;
284   font_class->get_metrics = pango_x_font_get_metrics;
285   font_class->get_font_map = pango_x_font_get_font_map;
286 }
287 
288 PangoXFont *
pango_x_font_new(PangoFontMap * fontmap,const char * spec,int size)289 pango_x_font_new (PangoFontMap *fontmap, const char *spec, int size)
290 {
291   PangoXFont *result;
292 
293   g_return_val_if_fail (fontmap != NULL, NULL);
294   g_return_val_if_fail (spec != NULL, NULL);
295 
296   result = g_object_new (PANGO_TYPE_X_FONT, NULL);
297 
298   g_assert (result->fontmap == NULL);
299   result->fontmap = fontmap;
300   g_object_add_weak_pointer (G_OBJECT (result->fontmap), (gpointer *) (gpointer) &result->fontmap);
301 
302   result->display = pango_x_fontmap_get_display (fontmap);
303 
304   result->fonts = g_strsplit(spec, ",", -1);
305   for (result->n_fonts = 0; result->fonts[result->n_fonts]; result->n_fonts++)
306     ; /* Nothing */
307 
308   result->size = size;
309 
310   return result;
311 }
312 
313 /**
314  * pango_x_load_font:
315  * @display: the X display.
316  * @spec:    a comma-separated list of XLFD's.
317  *
318  * Loads up a logical font based on a "fontset" style text
319  * specification. This is not remotely useful (Pango API's generally
320  * work in terms of #PangoFontDescription) and the result may not
321  * work correctly in all circumstances. Use of this function should
322  * be avoided.
323  *
324  * Returns: a new #PangoFont.
325  */
326 PangoFont *
pango_x_load_font(Display * display,const char * spec)327 pango_x_load_font (Display    *display,
328 		   const char *spec)
329 {
330   PangoXFont *result;
331 
332   g_return_val_if_fail (display != NULL, NULL);
333   g_return_val_if_fail (spec != NULL, NULL);
334 
335   result = pango_x_font_new (pango_x_font_map_for_display (display), spec, -1);
336 
337   return (PangoFont *)result;
338 }
339 
340 
341 #define FLUSH						\
342   G_STMT_START {					\
343     if (charcount)					\
344       {							\
345 	XDrawString16 (display, d, gc,			\
346 		       glyph_x0, glyph_y0,		\
347 		       xcharbuffer, charcount);		\
348 	charcount = 0;					\
349       }							\
350   } G_STMT_END
351 
352 
353 /**
354  * pango_x_render:
355  * @display: the X display.
356  * @d:       the drawable on which to draw string.
357  * @gc:      the graphics context.
358  * @font:    the font in which to draw the string.
359  * @glyphs:  the glyph string to draw.
360  * @x:       the x position of start of string (in pixels).
361  * @y:       the y position of baseline (in pixels).
362  *
363  * Renders a #PangoGlyphString onto an X drawable.
364  */
365 void
pango_x_render(Display * display,Drawable d,GC gc,PangoFont * font,PangoGlyphString * glyphs,int x,int y)366 pango_x_render  (Display           *display,
367 		 Drawable           d,
368 		 GC                 gc,
369 		 PangoFont         *font,
370 		 PangoGlyphString  *glyphs,
371 		 int                x,
372 		 int                y)
373 {
374   Font old_fid = None;
375   XFontStruct *fs;
376   int i;
377   int x_off = 0;
378 
379   /*
380    * We collect the characters in this buffer as long as the font does not
381    * change.  At that time, or when the buffer runs full, or at the end,
382    * then we empty the buffer.
383    */
384   XChar2b xcharbuffer[1000];
385   int glyph_x0 = 0, expected_x = 0; /* x/y initializations are to quiet GCC */
386   int glyph_y0 = 0;
387   int charcount = 0;
388 
389   g_return_if_fail (display != NULL);
390   g_return_if_fail (glyphs != NULL);
391 
392   for (i=0; i<glyphs->num_glyphs; i++)
393     {
394       PangoGlyph glyph = glyphs->glyphs[i].glyph;
395       int glyph_x = x + PANGO_PIXELS (x_off + glyphs->glyphs[i].geometry.x_offset);
396       int glyph_y = y + PANGO_PIXELS (glyphs->glyphs[i].geometry.y_offset);
397 
398       /* Clip glyphs into the X coordinate range; we really
399        * want to clip glyphs with an ink rect outside the
400        * [0,32767] x [0,32767] rectangle but looking up
401        * the ink rect here would be a noticeable speed hit.
402        * This is close enough.
403        */
404       if (!(glyph != PANGO_GLYPH_EMPTY &&
405 	    glyph_x >= -16384 && glyph_x <= 32767 &&
406 	    glyph_y >= -16384 && glyph_y <= 32767))
407 	goto next_glyph;
408 
409       if (G_LIKELY ((glyph & PANGO_GLYPH_UNKNOWN_FLAG) == 0))
410 	{
411 	  guint16 index = PANGO_X_GLYPH_INDEX (glyph);
412 	  guint16 subfont_index = PANGO_X_GLYPH_SUBFONT (glyph);
413 	  PangoXSubfontInfo *subfont;
414 
415 	  subfont = pango_x_find_subfont (font, subfont_index);
416 	  if (subfont)
417 	    {
418 	      fs = pango_x_get_font_struct (font, subfont);
419 	      if (!fs)
420 		continue;
421 
422 	      if (fs->fid != old_fid)
423 		{
424 		  FLUSH;
425 		  XSetFont (display, gc, fs->fid);
426 		  old_fid = fs->fid;
427 		}
428 
429 	      if (charcount == G_N_ELEMENTS (xcharbuffer) ||
430 		  (charcount > 0 && (glyph_y != glyph_y0 ||
431 				     glyph_x != expected_x)))
432 		FLUSH;
433 
434 	      if (charcount == 0)
435 		{
436 		  glyph_x0 = glyph_x;
437 		  glyph_y0 = glyph_y;
438 		}
439 	      xcharbuffer[charcount].byte1 = index / 256;
440 	      xcharbuffer[charcount].byte2 = index % 256;
441 
442 	      expected_x = glyph_x + XTextWidth16 (fs, &xcharbuffer[charcount], 1);
443 
444 	      charcount++;
445 	    } else
446 	      goto unknown_glyph;
447 	} else {
448 	  PangoFontMetrics *metrics;
449 	  int x1, y1, x2, y2; /* rectangle the character should go inside. */
450 	  int baseline;
451 	  int stroke_thick;
452 	  gunichar wc;
453 	  gboolean invalid_input;
454 
455 	unknown_glyph:
456 	  FLUSH;
457 
458 	  if (font)
459 	    metrics = pango_font_get_metrics (font, NULL);
460 	  else
461 	    metrics = NULL;
462 
463 	  if (metrics)
464 	    {
465 	      y1 = glyph_y - PANGO_PIXELS (metrics->ascent);
466 	      y2 = y1 + PANGO_PIXELS (metrics->ascent + metrics->descent);
467 	    }
468 	  else
469 	    {
470 	      y2 = glyph_y;
471 	      y1 = y2 - PANGO_UNKNOWN_GLYPH_HEIGHT;
472 	    }
473 
474 	  x1 = glyph_x;
475 	  x2 = x1 + PANGO_PIXELS (glyphs->glyphs[i].geometry.width);
476 
477 	  baseline = glyph_y;
478 	  stroke_thick = MAX ((int) (0.5 + 0.025 * (y2 - y1)), 1);
479 
480 	  if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
481 	    wc = glyph & ~PANGO_GLYPH_UNKNOWN_FLAG;
482 	  else
483 	    wc = 0;
484 	  invalid_input = glyph == PANGO_GLYPH_INVALID_INPUT || wc > 0x10FFFF;
485 
486 	  switch (wc)
487 	    {
488 	    case '\n':
489 	    case '\r':
490 	    case 0x2028: /* Line separator */
491 	    case 0x2029: /* Paragraph separator */
492 	      {
493 		/* Draw a carriage-return thingy */
494 		PangoRectangle up_stroke;
495 		PangoRectangle across_stroke;
496 
497 		int hborder = (x2 - x1) * 0.1;
498 		int arrow_height = 0.25 * (y2 - y1);
499 		int top_border = (y2 - y1) * 0.25;
500 
501 		int arrow_x, arrow_width, tmp_height;
502 
503 		/* Draw the arrow-head */
504 
505 		tmp_height = (stroke_thick % 2 == 0) ? 2 : 1; /* Starting height */
506 		arrow_height = 2 * ((1 + arrow_height - tmp_height) / 2) + tmp_height; /* Force symmetry */
507 		arrow_width = 2 + arrow_height - tmp_height;
508 
509 		for (arrow_x = x1 + hborder; arrow_x < x1 + hborder + arrow_width; arrow_x++)
510 		  {
511 		    XDrawLine (display, d, gc,
512 			       arrow_x,
513 			       baseline - stroke_thick + (stroke_thick - tmp_height) / 2,
514 			       arrow_x,
515 			       baseline - stroke_thick + (stroke_thick - tmp_height) / 2 + tmp_height - 1);
516 
517 		    if ((arrow_x - x1 - hborder) % 2 == 1)
518 		      tmp_height += 2;
519 		  }
520 
521 		across_stroke.x = arrow_x;
522 		across_stroke.width = x2 - hborder - arrow_x - stroke_thick;
523 		across_stroke.y = baseline - stroke_thick;
524 		across_stroke.height = stroke_thick;
525 
526 		XFillRectangle (display, d, gc,
527 				across_stroke.x, across_stroke.y,
528 				across_stroke.width, across_stroke.height);
529 
530 		up_stroke.x = across_stroke.x + across_stroke.width;
531 		up_stroke.width = stroke_thick;
532 		up_stroke.y = y1 + top_border;
533 		up_stroke.height = baseline - up_stroke.y;
534 
535 		XFillRectangle (display, d, gc,
536 				up_stroke.x, up_stroke.y,
537 				up_stroke.width, up_stroke.height);
538 	      }
539 	      break;
540 
541 	    default:
542 	      {
543 		/* Perhaps we should draw the box-with-numbers as in the
544 		 * other backends, though we have no guarantee of having
545 		 * an appropriate size of font. Right now, we just
546 		 * draw an empty box. (To draw the box-with-numbers.
547 		 * the backends would have to be changed to use
548 		 * pango_x_font_get_unknown_glyph() rather than
549 		 * pango_x_get_unknown_glyph().
550 		 */
551 
552 		int xspace = MAX ((int) (0.5 + 0.1 * (x2 - x1)), 1);
553 		int yspace = MAX ((int) (0.5 + 0.1 * (y2 - y1)), 1);
554 
555 		x1 += xspace;
556 		x2 -= xspace;
557 		y1 += yspace;
558 		y2 -= yspace;
559 
560 		XFillRectangle (display, d, gc,
561 				x1, y1,
562 				x2 - x1, stroke_thick);
563 		XFillRectangle (display, d, gc,
564 				x1, y1 + stroke_thick,
565 				stroke_thick, y2 - y1 - 2 * stroke_thick);
566 		XFillRectangle (display, d, gc,
567 				x2 - stroke_thick, y1 + stroke_thick,
568 				stroke_thick, y2 - y1 - 2 * stroke_thick);
569 		XFillRectangle (display, d, gc,
570 				x1, y2 - stroke_thick,
571 				x2 - x1, stroke_thick);
572 		if (invalid_input)
573 		  {
574 		    XDrawLine (display, d, gc,
575 			       x1, y1,
576 			       x2-1, y2-1);
577 		    XDrawLine (display, d, gc,
578 			       x2-1, y1,
579 			       x1, y2-1);
580 		  }
581 
582 		break;
583 	      }
584 	    }
585 
586 	  pango_font_metrics_unref (metrics);
587 	}
588 
589     next_glyph:
590       x_off += glyphs->glyphs[i].geometry.width;
591     }
592   FLUSH;
593 }
594 
595 #undef FLUSH
596 
597 static void
pango_x_font_get_glyph_extents(PangoFont * font,PangoGlyph glyph,PangoRectangle * ink_rect,PangoRectangle * logical_rect)598 pango_x_font_get_glyph_extents  (PangoFont      *font,
599 				 PangoGlyph      glyph,
600 				 PangoRectangle *ink_rect,
601 				 PangoRectangle *logical_rect)
602 {
603   XCharStruct *cs;
604   PangoXSubfontInfo *subfont;
605 
606   if (glyph == PANGO_GLYPH_EMPTY)
607     {
608       if (ink_rect)
609 	ink_rect->x = ink_rect->width = ink_rect->y = ink_rect->height = 0;
610       if (logical_rect)
611 	logical_rect->x = logical_rect->width = logical_rect->y = logical_rect->height = 0;
612       return;
613     }
614   if ((glyph & PANGO_GLYPH_UNKNOWN_FLAG) == 0 && pango_x_find_glyph (font, glyph, &subfont, &cs))
615     {
616       if (ink_rect)
617 	{
618 	  ink_rect->x = PANGO_SCALE * cs->lbearing;
619 	  ink_rect->width = PANGO_SCALE * (cs->rbearing - cs->lbearing);
620 	  ink_rect->y = PANGO_SCALE * -cs->ascent;
621 	  ink_rect->height = PANGO_SCALE * (cs->ascent + cs->descent);
622 	}
623       if (logical_rect)
624 	{
625 	  logical_rect->x = 0;
626 	  logical_rect->width = PANGO_SCALE * cs->width;
627 	  logical_rect->y = - PANGO_SCALE * subfont->font_struct->ascent;
628 	  logical_rect->height = PANGO_SCALE * (subfont->font_struct->ascent + subfont->font_struct->descent);
629 	}
630     }
631   else
632     {
633       PangoFontMetrics *metrics;
634       gunichar wc;
635       gdouble width_factor;
636       int w;
637 
638       if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
639 	wc = glyph & (~PANGO_GLYPH_UNKNOWN_FLAG);
640       else
641 	wc = 0;
642 
643       switch (wc)
644 	{
645 	case '\n':
646 	case '\r':
647 	case 0x2028: /* Line separator */
648 	case 0x2029: /* Paragraph separator */
649 	  {
650 #define MAGIC_FACTOR 1.2
651 
652 	    /* carriage-return thingy */
653 	    width_factor = MAGIC_FACTOR;
654 	    break;
655 	  }
656 	default:
657 	  {
658 	    /* Unknown glyph square */
659 	    width_factor = 1.0;
660 	  }
661 	}
662 
663       metrics = pango_font_get_metrics (font, NULL);
664 
665       if (metrics)
666 	{
667 	  w = metrics->approximate_char_width * width_factor;
668 	  w = PANGO_SCALE * PANGO_PIXELS (w);
669 
670 	  if (ink_rect)
671 	    {
672 	      ink_rect->x = PANGO_SCALE;
673 	      ink_rect->width = w - 2 * PANGO_SCALE;
674 	      ink_rect->y = - (metrics->ascent - PANGO_SCALE);
675 	      ink_rect->height = metrics->ascent + metrics->descent - 2 * PANGO_SCALE;
676 	    }
677 	  if (logical_rect)
678 	    {
679 	      logical_rect->x = 0;
680 	      logical_rect->width = w;
681 	      logical_rect->y = - metrics->ascent;
682 	      logical_rect->height = metrics->ascent + metrics->descent;
683 	    }
684 
685 	  pango_font_metrics_unref (metrics);
686 	}
687       else
688 	{
689 	  if (ink_rect)
690 	    ink_rect->x = ink_rect->y = ink_rect->height = ink_rect->width = 0;
691 	  if (logical_rect)
692 	    logical_rect->x = logical_rect->y = logical_rect->height = logical_rect->width = 0;
693 	}
694     }
695 }
696 
697 static gboolean
get_int_prop(Atom atom,XFontStruct * fs,int * val)698 get_int_prop (Atom         atom,
699 	      XFontStruct *fs,
700 	      int         *val)
701 {
702   int i;
703 
704   *val = 0;
705 
706   i = 0;
707   while (i < fs->n_properties)
708     {
709       if (fs->properties[i].name == atom)
710 	{
711 	  *val = fs->properties[i].card32;
712 	  return TRUE;
713 	}
714 
715       ++i;
716     }
717 
718   return FALSE;
719 }
720 
721 /* Call @func with each glyph resulting from shaping @string with each
722  * glyph. This duplicates quite a bit of code from pango_itemize. This
723  * function should die and we should simply add the ability to specify
724  * particular fonts when itemizing.
725  */
726 static void
itemize_string_foreach(PangoFont * font,PangoLanguage * language,const char * str,void (* func)(PangoFont * font,PangoGlyphInfo * glyph_info,gpointer data),gpointer data)727 itemize_string_foreach (PangoFont     *font,
728 			PangoLanguage *language,
729 			const char    *str,
730 			void         (*func) (PangoFont      *font,
731 					      PangoGlyphInfo *glyph_info,
732 					      gpointer        data),
733 			gpointer       data)
734 {
735   const char *start, *p;
736   PangoGlyphString *glyph_str = pango_glyph_string_new ();
737   PangoEngineShape *shaper, *last_shaper;
738   int last_level;
739   int i;
740   guint8 *embedding_levels;
741   PangoDirection base_dir = PANGO_DIRECTION_LTR;
742   gboolean finished = FALSE;
743 
744   embedding_levels = pango_log2vis_get_embedding_levels (str, -1, &base_dir);
745 
746   last_shaper = NULL;
747   last_level = 0;
748 
749   i = 0;
750   p = start = str;
751   while (*p || !finished)
752     {
753       gunichar wc;
754 
755       if (*p)
756 	{
757 	  wc = g_utf8_get_char (p);
758 	  shaper = pango_font_find_shaper (font, language, wc);
759 	}
760       else
761 	{
762 	  finished = TRUE;
763 	  shaper = NULL;
764 	}
765 
766       if (p > start &&
767 	  (finished ||
768 	   (shaper != last_shaper || last_level != embedding_levels[i])))
769 	{
770 	  PangoAnalysis analysis = { NULL };
771 	  int j;
772 
773 	  analysis.shape_engine = last_shaper;
774 	  analysis.font = font;
775 	  analysis.language = language;
776 	  analysis.level = last_level;
777 
778 	  pango_shape (start, p - start, &analysis, glyph_str);
779 
780 	  for (j = 0; j < glyph_str->num_glyphs; j++)
781 	    (*func) (font, &glyph_str->glyphs[j], data);
782 
783 	  start = p;
784 	}
785 
786       if (!finished)
787 	{
788 	  p = g_utf8_next_char (p);
789 
790 	  last_shaper = shaper;
791 	  last_level = embedding_levels[i];
792 	  i++;
793 	}
794     }
795 
796   pango_glyph_string_free (glyph_str);
797   g_free (embedding_levels);
798 }
799 
800 /* Get composite font metrics for all subfonts in list
801  */
802 static void
get_font_metrics_from_subfonts(PangoFont * font,GSList * subfonts,PangoFontMetrics * metrics)803 get_font_metrics_from_subfonts (PangoFont        *font,
804 				GSList           *subfonts,
805 				PangoFontMetrics *metrics)
806 {
807   PangoXFont *xfont = (PangoXFont *)font;
808   GSList *tmp_list = subfonts;
809   gboolean first = TRUE;
810   int total_avg_widths = 0;
811   int n_avg_widths = 0;
812   Atom avg_width_atom;
813 
814   avg_width_atom = pango_x_fontmap_atom_from_name (xfont->fontmap,
815 						   "AVERAGE_WIDTH");
816 
817   metrics->ascent = 0;
818   metrics->descent = 0;
819 
820   while (tmp_list)
821     {
822       PangoXSubfontInfo *subfont = pango_x_find_subfont (font, GPOINTER_TO_UINT (tmp_list->data));
823 
824       if (subfont)
825 	{
826 	  XFontStruct *fs = pango_x_get_font_struct (font, subfont);
827 	  gint avg_width = 0;
828 
829 	  if (fs)
830 	    {
831 	      if (first)
832 		{
833 		  metrics->ascent = fs->ascent * PANGO_SCALE;
834 		  metrics->descent = fs->descent * PANGO_SCALE;
835 		  first = FALSE;
836 		}
837 	      else
838 		{
839 		  metrics->ascent = MAX (fs->ascent * PANGO_SCALE, metrics->ascent);
840 		  metrics->descent = MAX (fs->descent * PANGO_SCALE, metrics->descent);
841 		}
842 
843 	      if (get_int_prop (avg_width_atom, fs, &avg_width))
844 		{
845 		  /* convert decipoints --> Pango units.
846 		   * Resolution is in (points * PANGO_SCALE) / pixel,
847 		   * avg_width in decipoints.
848 		   * We want pixels * PANGO_SCALE
849 		   */
850 
851 		  /* Convert to points * PANGO_SCALE */
852 		  avg_width *= PANGO_SCALE / (double) 10.0;
853 		  /* Convert to pixels * PANGO_SCALE */
854 		  avg_width *= (PANGO_SCALE / PANGO_X_FONT_MAP (PANGO_X_FONT (font)->fontmap)->resolution);
855 		}
856 	      else
857 		{
858 		  avg_width = PANGO_SCALE * ((fs->min_bounds.width + fs->max_bounds.width) / 2);
859 		}
860 	    }
861 
862 	  if (avg_width)
863 	    {
864 	      total_avg_widths += avg_width;
865 	      n_avg_widths += 1;
866 	    }
867 	}
868       else
869 	g_warning ("Invalid subfont %d in get_font_metrics_from_subfonts", GPOINTER_TO_UINT (tmp_list->data));
870 
871       tmp_list = tmp_list->next;
872     }
873 
874   /* This is pretty darn bogus. */
875   if (n_avg_widths)
876     metrics->approximate_char_width = total_avg_widths / n_avg_widths;
877   else
878     metrics->approximate_char_width = PANGO_UNKNOWN_GLYPH_WIDTH * PANGO_SCALE;
879   if (metrics->ascent + metrics->descent == 0)
880     {
881       metrics->ascent = PANGO_UNKNOWN_GLYPH_HEIGHT * PANGO_SCALE;
882       metrics->descent = 0;
883     }
884 }
885 
886 static void
get_subfonts_foreach(PangoFont * font,PangoGlyphInfo * glyph_info,gpointer data)887 get_subfonts_foreach (PangoFont      *font,
888 		      PangoGlyphInfo *glyph_info,
889 		      gpointer        data)
890 {
891   GSList **subfonts = data;
892   PangoGlyph glyph = glyph_info->glyph;
893   PangoXSubfont subfont;
894 
895   if (glyph == PANGO_GLYPH_EMPTY)
896     return;
897 
898   /* Use an arbitrary subfont for unknown glyphs...*/
899   if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
900     {
901     if (((PangoXFont *)font)->n_subfonts > 0)
902       glyph = PANGO_X_MAKE_GLYPH (1, 0);
903     else
904       return;
905     }
906 
907   subfont = PANGO_X_GLYPH_SUBFONT (glyph);
908   if (!g_slist_find (*subfonts, GUINT_TO_POINTER ((guint)subfont)))
909     *subfonts = g_slist_prepend (*subfonts, GUINT_TO_POINTER ((guint)subfont));
910 }
911 
912 /* Get composite font metrics for all subfonts resulting from shaping
913  * string str with the given font
914  */
915 static void
get_font_metrics_from_string(PangoFont * font,PangoLanguage * language,const char * str,PangoFontMetrics * metrics)916 get_font_metrics_from_string (PangoFont        *font,
917 			      PangoLanguage    *language,
918 			      const char       *str,
919 			      PangoFontMetrics *metrics)
920 {
921   GSList *subfonts = NULL;
922 
923   itemize_string_foreach (font, language, str, get_subfonts_foreach, &subfonts);
924   get_font_metrics_from_subfonts (font, subfonts, metrics);
925   g_slist_free (subfonts);
926 }
927 
928 static void
average_width_foreach(PangoFont * font G_GNUC_UNUSED,PangoGlyphInfo * glyph_info,gpointer data)929 average_width_foreach (PangoFont      *font G_GNUC_UNUSED,
930 		       PangoGlyphInfo *glyph_info,
931 		       gpointer        data)
932 {
933   int *width = data;
934 
935   *width += glyph_info->geometry.width;
936 }
937 
938 /* Get composite font metrics for all subfonts resulting from shaping
939  * string str with the given font
940  */
941 static gdouble
get_total_width_for_string(PangoFont * font,PangoLanguage * language,const char * str)942 get_total_width_for_string (PangoFont        *font,
943 			    PangoLanguage    *language,
944 			    const char       *str)
945 {
946   int width = 0;
947 
948   itemize_string_foreach (font, language, str, average_width_foreach, &width);
949 
950   return width;
951 }
952 
953 static PangoFontMetrics *
pango_x_font_get_metrics(PangoFont * font,PangoLanguage * language)954 pango_x_font_get_metrics (PangoFont        *font,
955 			  PangoLanguage    *language)
956 {
957   PangoXMetricsInfo *info = NULL; /* Quiet gcc */
958   PangoXFont *xfont = (PangoXFont *)font;
959   GSList *tmp_list;
960 
961   const char *sample_str = pango_language_get_sample_string (language);
962 
963   tmp_list = xfont->metrics_by_lang;
964   while (tmp_list)
965     {
966       info = tmp_list->data;
967 
968       if (info->sample_str == sample_str)    /* We _don't_ need strcmp */
969 	break;
970 
971       tmp_list = tmp_list->next;
972     }
973 
974   if (!tmp_list)
975     {
976       PangoFontMetrics *metrics;
977 
978       info = g_slice_new0 (PangoXMetricsInfo);
979 
980       xfont->metrics_by_lang = g_slist_prepend (xfont->metrics_by_lang, info);
981 
982       info->sample_str = sample_str;
983       metrics = pango_font_metrics_new ();
984 
985       get_font_metrics_from_string (font, language, sample_str, metrics);
986 
987       metrics->approximate_digit_width = get_total_width_for_string (font, language, "0123456789") / 10;
988 
989       info->metrics = metrics;
990     }
991 
992   return pango_font_metrics_ref (info->metrics);
993 }
994 
995 static PangoFontMap *
pango_x_font_get_font_map(PangoFont * font)996 pango_x_font_get_font_map (PangoFont *font)
997 {
998   PangoXFont *xfont = (PangoXFont *)font;
999 
1000   return xfont->fontmap;
1001 }
1002 
1003 /* Compare the tail of a to b */
1004 static gboolean
match_end(const char * a,const char * b)1005 match_end (const char *a, const char *b)
1006 {
1007   size_t len_a = strlen (a);
1008   size_t len_b = strlen (b);
1009 
1010   if (len_b > len_a)
1011     return FALSE;
1012   else
1013     return (strcmp (a + len_a - len_b, b) == 0);
1014 }
1015 
1016 /* Substitute in a charset into an XLFD. Return the
1017  * (g_malloc'd) new name, or %NULL if the XLFD cannot
1018  * match the charset
1019  */
1020 static char *
name_for_charset(char * xlfd,char * charset)1021 name_for_charset (char *xlfd, char *charset)
1022 {
1023   char *p;
1024   char *dash_charset = g_strconcat ("-", charset, NULL);
1025   char *result = NULL;
1026   int ndashes = 0;
1027 
1028   for (p = xlfd; *p; p++)
1029     if (*p == '-')
1030       ndashes++;
1031 
1032   if (ndashes == 14) /* Complete XLFD */
1033     {
1034       if (match_end (xlfd, "-*-*"))
1035 	{
1036 	  result = g_malloc (strlen (xlfd) - 4 + strlen (dash_charset) + 1);
1037 	  strncpy (result, xlfd, strlen (xlfd) - 4);
1038 	  strcpy (result + strlen (xlfd) - 4, dash_charset);
1039 	}
1040       if (match_end (xlfd, dash_charset))
1041 	result = g_strdup (xlfd);
1042     }
1043   else if (ndashes == 13)
1044     {
1045       if (match_end (xlfd, "-*"))
1046 	{
1047 	  result = g_malloc (strlen (xlfd) - 2 + strlen (dash_charset) + 1);
1048 	  strncpy (result, xlfd, strlen (xlfd) - 2);
1049 	  strcpy (result + strlen (xlfd) - 2, dash_charset);
1050 	}
1051       if (match_end (xlfd, dash_charset))
1052 	result = g_strdup (xlfd);
1053     }
1054   else
1055     {
1056       if (match_end (xlfd, "*"))
1057 	{
1058 	  result = g_malloc (strlen (xlfd) + strlen (dash_charset) + 1);
1059 	  strcpy (result, xlfd);
1060 	  strcpy (result + strlen (xlfd), dash_charset);
1061 	}
1062       if (match_end (xlfd, dash_charset))
1063 	result = g_strdup (xlfd);
1064     }
1065 
1066   g_free (dash_charset);
1067   return result;
1068 }
1069 
1070 static PangoXSubfont
pango_x_insert_subfont(PangoFont * font,const char * xlfd)1071 pango_x_insert_subfont (PangoFont *font, const char *xlfd)
1072 {
1073   PangoXFont *xfont = (PangoXFont *)font;
1074   PangoXSubfontInfo *info;
1075 
1076   info = g_slice_new (PangoXSubfontInfo);
1077 
1078   info->xlfd = g_strdup (xlfd);
1079   info->font_struct = NULL;
1080 
1081   xfont->n_subfonts++;
1082 
1083   if (xfont->n_subfonts > xfont->max_subfonts)
1084     {
1085       xfont->max_subfonts *= 2;
1086       xfont->subfonts = g_renew (PangoXSubfontInfo *, xfont->subfonts, xfont->max_subfonts);
1087     }
1088 
1089   xfont->subfonts[xfont->n_subfonts - 1] = info;
1090 
1091   return xfont->n_subfonts;
1092 }
1093 
1094 /**
1095  * pango_x_list_subfonts:
1096  * @font: a #PangoFont.
1097  * @charsets: the charsets to list subfonts for.
1098  * @n_charsets: the number of charsets in @charsets.
1099  * @subfont_ids: location to store a pointer to an array of subfont IDs for each found subfont;
1100  *               the result must be freed using g_free().
1101  * @subfont_charsets: location to store a pointer to an array of subfont IDs for each found subfont;
1102  *               the result must be freed using g_free().
1103  *
1104  * Lists the subfonts of a given font. The result is ordered first by charset,
1105  * and then within each charset, by the order of fonts in the font specification.
1106  *
1107  * Return value: length of the arrays stored in @subfont_ids and
1108  * @subfont_charsets.
1109  **/
1110 int
pango_x_list_subfonts(PangoFont * font,char ** charsets,int n_charsets,PangoXSubfont ** subfont_ids,int ** subfont_charsets)1111 pango_x_list_subfonts (PangoFont        *font,
1112 		       char            **charsets,
1113 		       int               n_charsets,
1114 		       PangoXSubfont   **subfont_ids,
1115 		       int             **subfont_charsets)
1116 {
1117   PangoXFont *xfont = (PangoXFont *)font;
1118   PangoXSubfont **subfont_lists;
1119   PangoFontMap *fontmap;
1120   int i, j;
1121   int n_subfonts = 0;
1122 
1123   g_return_val_if_fail (font != NULL, 0);
1124   g_return_val_if_fail (n_charsets == 0 || charsets != NULL, 0);
1125 
1126   fontmap = pango_x_font_map_for_display (xfont->display);
1127 
1128   subfont_lists = g_new (PangoXSubfont *, n_charsets);
1129 
1130   for (j=0; j<n_charsets; j++)
1131     {
1132       subfont_lists[j] = g_hash_table_lookup (xfont->subfonts_by_charset, charsets[j]);
1133       if (!subfont_lists[j])
1134 	{
1135 	  subfont_lists[j] = g_new (PangoXSubfont, xfont->n_fonts);
1136 
1137 	  for (i = 0; i < xfont->n_fonts; i++)
1138 	    {
1139 	      PangoXSubfont subfont = 0;
1140 	      char *xlfd;
1141 
1142 	      if (xfont->size == -1)
1143 		{
1144 		  xlfd = name_for_charset (xfont->fonts[i], charsets[j]);
1145 
1146 		  if (xlfd)
1147 		    {
1148 		      int count;
1149 		      char **names = XListFonts (xfont->display, xlfd, 1, &count);
1150 		      if (count > 0)
1151 			subfont = pango_x_insert_subfont (font, names[0]);
1152 
1153 		      XFreeFontNames (names);
1154 		      g_free (xlfd);
1155 		    }
1156 		}
1157 	      else
1158 		{
1159 		  xlfd = pango_x_make_matching_xlfd (fontmap, xfont->fonts[i], charsets[j], xfont->size);
1160 		  if (xlfd)
1161 		    {
1162 		      subfont = pango_x_insert_subfont (font, xlfd);
1163 		      g_free (xlfd);
1164 		    }
1165 		}
1166 
1167 	      subfont_lists[j][i] = subfont;
1168 	    }
1169 
1170 	  g_hash_table_insert (xfont->subfonts_by_charset, g_strdup (charsets[j]), subfont_lists[j]);
1171 	}
1172 
1173       for (i = 0; i < xfont->n_fonts; i++)
1174 	if (subfont_lists[j][i])
1175 	  n_subfonts++;
1176     }
1177 
1178   *subfont_ids = g_new (PangoXSubfont, n_subfonts);
1179   *subfont_charsets = g_new (int, n_subfonts);
1180 
1181   n_subfonts = 0;
1182 
1183   for (j=0; j<n_charsets; j++)
1184     for (i=0; i<xfont->n_fonts; i++)
1185       if (subfont_lists[j][i])
1186 	{
1187 	  (*subfont_ids)[n_subfonts] = subfont_lists[j][i];
1188 	  (*subfont_charsets)[n_subfonts] = j;
1189 	  n_subfonts++;
1190 	}
1191 
1192   g_free (subfont_lists);
1193 
1194   return n_subfonts;
1195 }
1196 
1197 /**
1198  * pango_x_has_glyph:
1199  * @font: a #PangoFont which must be from the X backend.
1200  * @glyph: the index of a glyph in the font. (Formed
1201  *         using the #PANGO_X_MAKE_GLYPH macro)
1202  *
1203  * Checks if the given glyph is present in a X font.
1204  *
1205  * Return value: %TRUE if the glyph is present.
1206  **/
1207 gboolean
pango_x_has_glyph(PangoFont * font,PangoGlyph glyph)1208 pango_x_has_glyph (PangoFont  *font,
1209 		   PangoGlyph  glyph)
1210 {
1211   PangoXSubfontInfo *subfont;
1212   XCharStruct *cs;
1213 
1214   guint16 char_index = PANGO_X_GLYPH_INDEX (glyph);
1215   guint16 subfont_index = PANGO_X_GLYPH_SUBFONT (glyph);
1216 
1217   subfont = pango_x_find_subfont (font, subfont_index);
1218   if (!subfont)
1219     return FALSE;
1220 
1221   cs = pango_x_get_per_char (font, subfont, char_index);
1222 
1223   if (cs && (cs->lbearing != cs->rbearing || cs->width != 0))
1224     return TRUE;
1225   else
1226     return FALSE;
1227 }
1228 
1229 /**
1230  * pango_x_font_subfont_xlfd:
1231  * @font: a #PangoFont which must be from the X backend.
1232  * @subfont_id: the id of a subfont within the font.
1233  *
1234  * Determines the X Logical Font Description for the specified
1235  * subfont.
1236  *
1237  * Return value: A newly-allocated string containing the XLFD for the
1238  * subfont. This string must be freed with g_free().
1239  **/
1240 char *
pango_x_font_subfont_xlfd(PangoFont * font,PangoXSubfont subfont_id)1241 pango_x_font_subfont_xlfd (PangoFont     *font,
1242 			   PangoXSubfont  subfont_id)
1243 {
1244   PangoXSubfontInfo *subfont;
1245 
1246   g_return_val_if_fail (font != NULL, NULL);
1247   g_return_val_if_fail (PANGO_X_IS_FONT (font), NULL);
1248 
1249   subfont = pango_x_find_subfont (font, subfont_id);
1250   if (!subfont)
1251     {
1252       g_warning ("pango_x_font_subfont_xlfd: Invalid subfont_id specified");
1253       return NULL;
1254     }
1255 
1256   return g_strdup (subfont->xlfd);
1257 }
1258 
1259 static void
pango_x_font_dispose(GObject * object)1260 pango_x_font_dispose (GObject *object)
1261 {
1262   PangoXFont *xfont = PANGO_X_FONT (object);
1263 
1264   /* If the font is not already in the freed-fonts cache, add it,
1265    * if it is already there, do nothing and the font will be
1266    * freed.
1267    */
1268   if (!xfont->in_cache && xfont->fontmap)
1269     pango_x_fontmap_cache_add (xfont->fontmap, xfont);
1270 
1271   G_OBJECT_CLASS (parent_class)->dispose (object);
1272 }
1273 
1274 
1275 static void
subfonts_foreach(gpointer key,gpointer value,gpointer data G_GNUC_UNUSED)1276 subfonts_foreach (gpointer key, gpointer value, gpointer data G_GNUC_UNUSED)
1277 {
1278   g_free (key);
1279   g_free (value);
1280 }
1281 
1282 static void
free_metrics_info(PangoXMetricsInfo * info)1283 free_metrics_info (PangoXMetricsInfo *info)
1284 {
1285   pango_font_metrics_unref (info->metrics);
1286   g_slice_free (PangoXMetricsInfo, info);
1287 }
1288 
1289 static void
pango_x_font_finalize(GObject * object)1290 pango_x_font_finalize (GObject *object)
1291 {
1292   PangoXFont *xfont = (PangoXFont *)object;
1293   PangoXFontCache *cache = pango_x_font_map_get_font_cache (xfont->fontmap);
1294 
1295   int i;
1296 
1297   for (i=0; i<xfont->n_subfonts; i++)
1298     {
1299       PangoXSubfontInfo *info = xfont->subfonts[i];
1300 
1301       g_free (info->xlfd);
1302 
1303       if (info->font_struct)
1304 	pango_x_font_cache_unload (cache, info->font_struct);
1305 
1306       g_slice_free (PangoXSubfontInfo, info);
1307     }
1308 
1309   g_free (xfont->subfonts);
1310 
1311   g_hash_table_foreach (xfont->subfonts_by_charset, subfonts_foreach, NULL);
1312   g_hash_table_destroy (xfont->subfonts_by_charset);
1313 
1314   g_slist_foreach (xfont->metrics_by_lang, (GFunc)free_metrics_info, NULL);
1315   g_slist_free (xfont->metrics_by_lang);
1316 
1317   if (xfont->xface)
1318     pango_x_face_remove (xfont->xface, (PangoFont *)xfont);
1319 
1320   g_assert (xfont->fontmap != NULL);
1321   g_object_remove_weak_pointer (G_OBJECT (xfont->fontmap), (gpointer *) (gpointer) &xfont->fontmap);
1322   xfont->fontmap = NULL;
1323 
1324   g_strfreev (xfont->fonts);
1325 
1326   G_OBJECT_CLASS (parent_class)->finalize (object);
1327 }
1328 
1329 static PangoFontDescription *
pango_x_font_describe(PangoFont * font)1330 pango_x_font_describe (PangoFont *font)
1331 {
1332   /* FIXME: this doesn't work for fonts from pango_x_font_load()
1333    */
1334   PangoXFont *xfont = (PangoXFont *)font;
1335 
1336   if (xfont->xface)
1337     {
1338       PangoFontDescription *desc = pango_font_face_describe (PANGO_FONT_FACE (xfont->xface));
1339       pango_font_description_set_size (desc, xfont->size);
1340 
1341       return desc;
1342     }
1343   else
1344     return NULL;
1345 }
1346 
1347 PangoMap *
pango_x_get_shaper_map(PangoLanguage * language)1348 pango_x_get_shaper_map (PangoLanguage *language)
1349 {
1350   static guint engine_type_id = 0;
1351   static guint render_type_id = 0;
1352 
1353   if (engine_type_id == 0)
1354     {
1355       engine_type_id = g_quark_from_static_string (PANGO_ENGINE_TYPE_SHAPE);
1356       render_type_id = g_quark_from_static_string (PANGO_RENDER_TYPE_X);
1357     }
1358 
1359   return pango_find_map (language, engine_type_id, render_type_id);
1360 }
1361 
1362 static PangoCoverage *
pango_x_font_get_coverage(PangoFont * font,PangoLanguage * language)1363 pango_x_font_get_coverage (PangoFont     *font,
1364 			   PangoLanguage *language)
1365 {
1366   PangoXFont *xfont = (PangoXFont *)font;
1367 
1368   return pango_x_face_get_coverage (xfont->xface, font, language);
1369 }
1370 
1371 static PangoEngineShape *
pango_x_font_find_shaper(PangoFont * font G_GNUC_UNUSED,PangoLanguage * language,guint32 ch)1372 pango_x_font_find_shaper (PangoFont     *font G_GNUC_UNUSED,
1373 			  PangoLanguage *language,
1374 			  guint32        ch)
1375 {
1376   static PangoEngineShape *shaper;
1377   if (g_once_init_enter (&shaper))
1378     g_once_init_leave (&shaper,
1379 		       _pango_basic_x_script_engine_create("BasicScriptEngineXCompat"));
1380   return shaper;
1381 }
1382 
1383 /* Utility functions */
1384 
1385 static XCharStruct *
pango_x_get_per_char(PangoFont * font,PangoXSubfontInfo * subfont,guint16 char_index)1386 pango_x_get_per_char (PangoFont         *font,
1387 		      PangoXSubfontInfo *subfont,
1388 		      guint16            char_index)
1389 {
1390   XFontStruct *fs;
1391 
1392   int index;
1393   int byte1;
1394   int byte2;
1395 
1396   fs = pango_x_get_font_struct (font, subfont);
1397   if (!fs)
1398     return NULL;
1399 
1400   if (subfont->is_1byte)
1401     {
1402       index = (int)char_index - fs->min_char_or_byte2;
1403       if (index < 0 || index >= subfont->range_byte2)
1404 	return NULL;
1405     }
1406   else
1407     {
1408       byte1 = (int)(char_index / 256) - fs->min_byte1;
1409       if (byte1 < 0 || byte1 >= subfont->range_byte1)
1410 	return NULL;
1411 
1412       byte2 = (int)(char_index % 256) - fs->min_char_or_byte2;
1413       if (byte2 < 0 || byte2 >= subfont->range_byte2)
1414 	return NULL;
1415 
1416       index = byte1 * subfont->range_byte2 + byte2;
1417     }
1418 
1419   if (fs->per_char)
1420     return &fs->per_char[index];
1421   else
1422     return &fs->min_bounds;
1423 }
1424 
1425 static gboolean
pango_x_find_glyph(PangoFont * font,PangoGlyph glyph,PangoXSubfontInfo ** subfont_return,XCharStruct ** charstruct_return)1426 pango_x_find_glyph (PangoFont *font,
1427 		    PangoGlyph glyph,
1428 		    PangoXSubfontInfo **subfont_return,
1429 		    XCharStruct **charstruct_return)
1430 {
1431   PangoXSubfontInfo *subfont;
1432   XCharStruct *cs;
1433 
1434   guint16 char_index = PANGO_X_GLYPH_INDEX (glyph);
1435   guint16 subfont_index = PANGO_X_GLYPH_SUBFONT (glyph);
1436 
1437   subfont = pango_x_find_subfont (font, subfont_index);
1438   if (!subfont)
1439     return FALSE;
1440 
1441   cs = pango_x_get_per_char (font, subfont, char_index);
1442 
1443   if (cs && (cs->lbearing != cs->rbearing || cs->width != 0))
1444     {
1445       if (subfont_return)
1446 	*subfont_return = subfont;
1447 
1448       if (charstruct_return)
1449 	*charstruct_return = cs;
1450 
1451       return TRUE;
1452     }
1453   else
1454     return FALSE;
1455 }
1456 
1457 /**
1458  * pango_x_get_unknown_glyph:
1459  * @font: a #PangoFont.
1460  *
1461  * Returns the index of a glyph suitable for drawing unknown characters;
1462  * you should generally use PANGO_GET_UNKNOWN_GLYPH() instead,
1463  * since that may return a glyph that provides a better representation
1464  * of a particular char. (E.g., by showing hex digits, or a glyph
1465  * representative of a certain Unicode range.)
1466  *
1467  * Return value: a glyph index into @font.
1468  **/
1469 PangoGlyph
pango_x_get_unknown_glyph(PangoFont * font G_GNUC_UNUSED)1470 pango_x_get_unknown_glyph (PangoFont *font G_GNUC_UNUSED)
1471 {
1472   return PANGO_GET_UNKNOWN_GLYPH (0);
1473 }
1474 
1475 /**
1476  * pango_x_render_layout_line:
1477  * @display:   the X display.
1478  * @drawable:  the drawable on which to draw.
1479  * @gc:        GC to use for uncolored drawing.
1480  * @line:      a #PangoLayoutLine.
1481  * @x:         the x position of start of string (in pixels).
1482  * @y:         the y position of baseline (in pixels).
1483  *
1484  * Renders a #PangoLayoutLine onto an X drawable.
1485  */
1486 void
pango_x_render_layout_line(Display * display,Drawable drawable,GC gc,PangoLayoutLine * line,int x,int y)1487 pango_x_render_layout_line (Display          *display,
1488 			    Drawable          drawable,
1489 			    GC                gc,
1490 			    PangoLayoutLine  *line,
1491 			    int               x,
1492 			    int               y)
1493 {
1494   GSList *tmp_list = line->runs;
1495   PangoRectangle overall_rect;
1496   PangoRectangle logical_rect;
1497   PangoRectangle ink_rect;
1498   PangoContext *context = pango_layout_get_context (line->layout);
1499   PangoXContextInfo *info = get_context_info (context);
1500 
1501   int x_off = 0;
1502 
1503   pango_layout_line_get_extents (line,NULL, &overall_rect);
1504 
1505   while (tmp_list)
1506     {
1507       PangoUnderline uline = PANGO_UNDERLINE_NONE;
1508       PangoLayoutRun *run = tmp_list->data;
1509       PangoAttrColor fg_color, bg_color;
1510       gboolean fg_set, bg_set;
1511       GC fg_gc;
1512 
1513       tmp_list = tmp_list->next;
1514 
1515       pango_x_get_item_properties (run->item, &uline, &fg_color, &fg_set, &bg_color, &bg_set);
1516 
1517       if (fg_set && info->get_gc_func)
1518 	fg_gc = info->get_gc_func (context, &fg_color.color, gc);
1519       else
1520 	fg_gc = gc;
1521 
1522       if (uline == PANGO_UNDERLINE_NONE)
1523 	pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
1524 				    NULL, &logical_rect);
1525       else
1526 	pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
1527 				    &ink_rect, &logical_rect);
1528 
1529       if (bg_set && info->get_gc_func)
1530 	{
1531 	  GC bg_gc = info->get_gc_func (context, &bg_color.color, gc);
1532 
1533 	  XFillRectangle (display, drawable, bg_gc,
1534 			  x + (x_off + logical_rect.x) / PANGO_SCALE,
1535 			  y + overall_rect.y / PANGO_SCALE,
1536 			  logical_rect.width / PANGO_SCALE,
1537 			  overall_rect.height / PANGO_SCALE);
1538 
1539 	  if (info->free_gc_func)
1540 	    info->free_gc_func (context, bg_gc);
1541 	}
1542 
1543       pango_x_render (display, drawable, fg_gc, run->item->analysis.font, run->glyphs,
1544 		      x + x_off / PANGO_SCALE, y);
1545 
1546       switch (uline)
1547 	{
1548 	case PANGO_UNDERLINE_NONE:
1549 	  break;
1550 	case PANGO_UNDERLINE_DOUBLE:
1551 	  XDrawLine (display, drawable, fg_gc,
1552 		     x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 4,
1553 		     x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 4);
1554 	  /* Fall through */
1555 	case PANGO_UNDERLINE_SINGLE:
1556 	  XDrawLine (display, drawable, fg_gc,
1557 		     x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 2,
1558 		     x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 2);
1559 	  break;
1560 	case PANGO_UNDERLINE_ERROR:
1561 	  {
1562 	    int point_x;
1563 	    int counter = 0;
1564 	    int end_x = x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE;
1565 
1566 	    for (point_x = x + PANGO_PIXELS (x_off + ink_rect.x) - 1;
1567 		 point_x <= end_x;
1568 		 point_x += 2)
1569 	      {
1570 		if (counter)
1571 		  XDrawLine (display, drawable, gc,
1572 			     point_x, y + 2, MIN (point_x + 1, end_x), y + 2);
1573 		else
1574 		  XDrawLine (display, drawable, gc,
1575 			     point_x, y + 3, MIN (point_x + 1, end_x), y + 3);
1576 
1577 		counter = (counter + 1) % 2;
1578 	      }
1579 	  }
1580 	  break;
1581 	case PANGO_UNDERLINE_LOW:
1582 	  XDrawLine (display, drawable, fg_gc,
1583 		     x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2,
1584 		     x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2);
1585 	  break;
1586 	}
1587 
1588       if (fg_set && info->get_gc_func && info->free_gc_func)
1589 	info->free_gc_func (context, fg_gc);
1590 
1591       x_off += logical_rect.width;
1592     }
1593 }
1594 
1595 /**
1596  * pango_x_render_layout:
1597  * @display:   the X display.
1598  * @drawable:  the drawable on which to draw.
1599  * @gc:        GC to use for uncolored drawing.
1600  * @layout:    a #PangoLayout.
1601  * @x:         the x position of the left of the layout (in pixels).
1602  * @y:         the y position of the top of the layout (in pixels).
1603  *
1604  * Renders a #PangoLayout onto an X drawable.
1605  */
1606 void
pango_x_render_layout(Display * display,Drawable drawable,GC gc,PangoLayout * layout,int x,int y)1607 pango_x_render_layout (Display         *display,
1608 		       Drawable         drawable,
1609 		       GC               gc,
1610 		       PangoLayout     *layout,
1611 		       int              x,
1612 		       int              y)
1613 {
1614   PangoLayoutIter *iter;
1615 
1616   g_return_if_fail (display != NULL);
1617   g_return_if_fail (PANGO_IS_LAYOUT (layout));
1618 
1619   iter = pango_layout_get_iter (layout);
1620 
1621   do
1622     {
1623       PangoRectangle   logical_rect;
1624       PangoLayoutLine *line;
1625       int              baseline;
1626 
1627       line = pango_layout_iter_get_line_readonly (iter);
1628 
1629       pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
1630       baseline = pango_layout_iter_get_baseline (iter);
1631 
1632       pango_x_render_layout_line (display, drawable, gc,
1633 				  line,
1634 				  x + PANGO_PIXELS (logical_rect.x),
1635 				  y + PANGO_PIXELS (baseline));
1636     }
1637   while (pango_layout_iter_next_line (iter));
1638 
1639   pango_layout_iter_free (iter);
1640 }
1641 
1642 /* This utility function is duplicated here and in pango-layout.c; should it be
1643  * public? Trouble is - what is the appropriate set of properties?
1644  */
1645 static void
pango_x_get_item_properties(PangoItem * item,PangoUnderline * uline,PangoAttrColor * fg_color,gboolean * fg_set,PangoAttrColor * bg_color,gboolean * bg_set)1646 pango_x_get_item_properties (PangoItem      *item,
1647 			     PangoUnderline *uline,
1648 			     PangoAttrColor *fg_color,
1649 			     gboolean       *fg_set,
1650 			     PangoAttrColor *bg_color,
1651 			     gboolean       *bg_set)
1652 {
1653   GSList *tmp_list = item->analysis.extra_attrs;
1654 
1655   if (fg_set)
1656     *fg_set = FALSE;
1657 
1658   if (bg_set)
1659     *bg_set = FALSE;
1660 
1661   while (tmp_list)
1662     {
1663       PangoAttribute *attr = tmp_list->data;
1664 
1665       switch ((int) attr->klass->type)
1666 	{
1667 	case PANGO_ATTR_UNDERLINE:
1668 	  if (uline)
1669 	    *uline = ((PangoAttrInt *)attr)->value;
1670 	  break;
1671 
1672 	case PANGO_ATTR_FOREGROUND:
1673 	  if (fg_color)
1674 	    *fg_color = *((PangoAttrColor *)attr);
1675 	  if (fg_set)
1676 	    *fg_set = TRUE;
1677 
1678 	  break;
1679 
1680 	case PANGO_ATTR_BACKGROUND:
1681 	  if (bg_color)
1682 	    *bg_color = *((PangoAttrColor *)attr);
1683 	  if (bg_set)
1684 	    *bg_set = TRUE;
1685 
1686 	  break;
1687 
1688 	default:
1689 	  break;
1690 	}
1691       tmp_list = tmp_list->next;
1692     }
1693 }
1694 
1695 /**
1696  * pango_x_apply_ligatures:
1697  * @font: unused
1698  * @subfont: unused
1699  * @glyphs: unused
1700  * @n_glyphs: unused
1701  * @clusters: unused
1702  *
1703  * Previously did subfont-specific ligation. Now a no-op.
1704  *
1705  * Return value: %FALSE, always.
1706  */
1707 gboolean
pango_x_apply_ligatures(PangoFont * font G_GNUC_UNUSED,PangoXSubfont subfont_id G_GNUC_UNUSED,gunichar ** glyphs G_GNUC_UNUSED,int * n_glyphs G_GNUC_UNUSED,int ** clusters G_GNUC_UNUSED)1708 pango_x_apply_ligatures (PangoFont     *font G_GNUC_UNUSED,
1709 			 PangoXSubfont  subfont_id G_GNUC_UNUSED,
1710 			 gunichar     **glyphs G_GNUC_UNUSED,
1711 			 int           *n_glyphs G_GNUC_UNUSED,
1712 			 int           **clusters G_GNUC_UNUSED)
1713 {
1714   return FALSE;
1715 }
1716 
1717 /**
1718  * pango_x_find_first_subfont:
1719  * @font: A #PangoFont.
1720  * @rfont: A pointer to a #PangoXSubfont.
1721  * @charsets: An array of charsets.
1722  * @n_charsets: The number of charsets in @charsets.
1723  *
1724  * Looks for subfonts with the @charset charset,
1725  * in @font, and puts the first one in *@rfont.
1726  *
1727  * Return value: %TRUE if *@rfont now contains a font.
1728  */
1729 gboolean
pango_x_find_first_subfont(PangoFont * font,char ** charsets,int n_charsets,PangoXSubfont * rfont)1730 pango_x_find_first_subfont (PangoFont      *font,
1731 			    char          **charsets,
1732 			    int             n_charsets,
1733 			    PangoXSubfont  *rfont)
1734 {
1735   int n_subfonts;
1736   gboolean result = FALSE;
1737   PangoXSubfont *subfonts;
1738   int *subfont_charsets;
1739 
1740   g_return_val_if_fail (font, 0);
1741   g_return_val_if_fail (charsets, 0);
1742   g_return_val_if_fail (rfont, 0);
1743 
1744   n_subfonts = pango_x_list_subfonts (font, charsets, n_charsets,
1745 				      &subfonts, &subfont_charsets);
1746 
1747   if (n_subfonts > 0)
1748     {
1749       *rfont = subfonts[0];
1750       result = TRUE;
1751     }
1752 
1753   g_free (subfonts);
1754   g_free (subfont_charsets);
1755   return result;
1756 }
1757 
1758 /**
1759  * pango_x_fallback_shape:
1760  * @font: A #PangoFont.
1761  * @glyphs: A pointer to a #PangoGlyphString.
1762  * @text: UTF-8 string.
1763  * @n_chars: Number of UTF-8 seqs in @text.
1764  *
1765  * This is a simple fallback shaper, that can be used
1766  * if no subfont that supports a given script is found.
1767  * For every character in @text, it puts the unknown glyph.
1768  */
1769 void
pango_x_fallback_shape(PangoFont * font,PangoGlyphString * glyphs,const char * text,int n_chars)1770 pango_x_fallback_shape (PangoFont        *font,
1771 			PangoGlyphString *glyphs,
1772 			const char       *text,
1773 			int               n_chars)
1774 {
1775   PangoGlyph unknown_glyph = pango_x_get_unknown_glyph (font);
1776   PangoRectangle logical_rect;
1777   const char *p;
1778   int i;
1779 
1780   g_return_if_fail (font);
1781   g_return_if_fail (glyphs);
1782   g_return_if_fail (text);
1783   g_return_if_fail (n_chars >= 0);
1784 
1785   pango_font_get_glyph_extents (font, unknown_glyph, NULL, &logical_rect);
1786   pango_glyph_string_set_size (glyphs, n_chars);
1787   p = text;
1788   for (i = 0; i < n_chars; i++)
1789     {
1790       glyphs->glyphs[i].glyph = unknown_glyph;
1791 
1792       glyphs->glyphs[i].geometry.x_offset = 0;
1793       glyphs->glyphs[i].geometry.y_offset = 0;
1794       glyphs->glyphs[i].geometry.width = logical_rect.width;
1795 
1796       glyphs->log_clusters[i] = p - text;
1797 
1798       p = g_utf8_next_char (p);
1799     }
1800 }
1801 
1802 /**
1803  * pango_x_font_get_unknown_glyph:
1804  * @font: a #PangoFont.
1805  * @wc: the Unicode character for which a glyph is needed.
1806  *
1807  * Returns the index of a glyph suitable for drawing @wc as an
1808  * unknown character.
1809  *
1810  * Use PANGO_GET_UNKNOWN_GLYPH() instead.
1811  *
1812  * Return value: a glyph index into @font.
1813  */
1814 PangoGlyph
pango_x_font_get_unknown_glyph(PangoFont * font G_GNUC_UNUSED,gunichar wc)1815 pango_x_font_get_unknown_glyph (PangoFont *font G_GNUC_UNUSED,
1816 				gunichar   wc)
1817 {
1818   return PANGO_GET_UNKNOWN_GLYPH (wc);
1819 }
1820