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