Git init
[external/pango1.0.git] / pango / pangocairo-font.c
1 /* Pango
2  * pangocairo-font.c: Cairo font handling
3  *
4  * Copyright (C) 2000-2005 Red Hat Software
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
24 #include <math.h>
25
26 #include "pangocairo.h"
27 #include "pangocairo-private.h"
28 #include "pango-impl-utils.h"
29
30 #define PANGO_CAIRO_FONT_PRIVATE(font)          \
31   ((PangoCairoFontPrivate *)                    \
32    (font == NULL ? NULL :                       \
33     G_STRUCT_MEMBER_P (font,                    \
34     PANGO_CAIRO_FONT_GET_IFACE(PANGO_CAIRO_FONT(font))->cf_priv_offset)))
35
36 GType
37 pango_cairo_font_get_type (void)
38 {
39   static GType cairo_font_type = 0;
40
41   if (! cairo_font_type)
42     {
43       const GTypeInfo cairo_font_info =
44       {
45         sizeof (PangoCairoFontIface), /* class_size */
46         NULL,           /* base_init */
47         NULL,           /* base_finalize */
48         NULL,
49         NULL,           /* class_finalize */
50         NULL,           /* class_data */
51         0,
52         0,
53         NULL,
54         NULL
55       };
56
57       cairo_font_type =
58         g_type_register_static (G_TYPE_INTERFACE, I_("PangoCairoFont"),
59                                 &cairo_font_info, 0);
60
61       g_type_interface_add_prerequisite (cairo_font_type, PANGO_TYPE_FONT);
62     }
63
64   return cairo_font_type;
65 }
66
67 static PangoCairoFontPrivateScaledFontData *
68 _pango_cairo_font_private_scaled_font_data_create (void)
69 {
70   return g_slice_new (PangoCairoFontPrivateScaledFontData);
71 }
72
73 static void
74 _pango_cairo_font_private_scaled_font_data_destroy (PangoCairoFontPrivateScaledFontData *data)
75 {
76   if (data)
77     {
78       cairo_font_options_destroy (data->options);
79       g_slice_free (PangoCairoFontPrivateScaledFontData, data);
80     }
81 }
82
83 cairo_scaled_font_t *
84 _pango_cairo_font_private_get_scaled_font (PangoCairoFontPrivate *cf_priv)
85 {
86   cairo_font_face_t *font_face;
87
88   if (G_LIKELY (cf_priv->scaled_font))
89     return cf_priv->scaled_font;
90
91   /* need to create it */
92
93   if (G_UNLIKELY (cf_priv->data == NULL))
94     {
95       /* we have tried to create and failed before */
96       return NULL;
97     }
98
99   font_face = (* PANGO_CAIRO_FONT_GET_IFACE (cf_priv->cfont)->create_font_face) (cf_priv->cfont);
100   if (G_UNLIKELY (font_face == NULL))
101     goto done;
102
103   cf_priv->scaled_font = cairo_scaled_font_create (font_face,
104                                                    &cf_priv->data->font_matrix,
105                                                    &cf_priv->data->ctm,
106                                                    cf_priv->data->options);
107
108   cairo_font_face_destroy (font_face);
109
110 done:
111
112   if (G_UNLIKELY (cf_priv->scaled_font == NULL || cairo_scaled_font_status (cf_priv->scaled_font) != CAIRO_STATUS_SUCCESS))
113     {
114       cairo_scaled_font_t *scaled_font = cf_priv->scaled_font;
115       PangoFont *font = PANGO_FONT (cf_priv->cfont);
116       static GQuark warned_quark = 0;
117       if (!warned_quark)
118         warned_quark = g_quark_from_static_string ("pangocairo-scaledfont-warned");
119
120       if (!g_object_get_qdata (G_OBJECT (font), warned_quark))
121         {
122           PangoFontDescription *desc;
123           char *s;
124
125           desc = pango_font_describe (font);
126           s = pango_font_description_to_string (desc);
127           pango_font_description_free (desc);
128
129           g_warning ("failed to create cairo %s, expect ugly output. the offending font is '%s'",
130                      font_face ? "scaled font" : "font face",
131                      s);
132
133           if (!font_face)
134                 g_warning ("font_face is NULL");
135           else
136                 g_warning ("font_face status is: %s",
137                            cairo_status_to_string (cairo_font_face_status (font_face)));
138
139           if (!scaled_font)
140                 g_warning ("scaled_font is NULL");
141           else
142                 g_warning ("scaled_font status is: %s",
143                            cairo_status_to_string (cairo_scaled_font_status (scaled_font)));
144
145           g_free (s);
146
147           g_object_set_qdata_full (G_OBJECT (font), warned_quark,
148                                    GINT_TO_POINTER (1), NULL);
149         }
150     }
151
152   _pango_cairo_font_private_scaled_font_data_destroy (cf_priv->data);
153   cf_priv->data = NULL;
154
155   return cf_priv->scaled_font;
156 }
157
158 /**
159  * pango_cairo_font_get_scaled_font:
160  * @font: a #PangoFont from a #PangoCairoFontMap
161  *
162  * Gets the #cairo_scaled_font_t used by @font.
163  * The scaled font can be referenced and kept using
164  * cairo_scaled_font_reference().
165  *
166  * Return value: the #cairo_scaled_font_t used by @font,
167  *               or %NULL if @font is %NULL.
168  *
169  * Since: 1.18
170  **/
171 cairo_scaled_font_t *
172 pango_cairo_font_get_scaled_font (PangoCairoFont *cfont)
173 {
174   PangoCairoFontPrivate *cf_priv;
175
176   if (G_UNLIKELY (!cfont))
177     return NULL;
178
179   cf_priv = PANGO_CAIRO_FONT_PRIVATE (cfont);
180
181   return _pango_cairo_font_private_get_scaled_font (cf_priv);
182 }
183
184 /**
185  * _pango_cairo_font_install:
186  * @font: a #PangoCairoFont
187  * @cr: a #cairo_t
188  *
189  * Makes @font the current font for rendering in the specified
190  * Cairo context.
191  *
192  * Return value: %TRUE if font was installed successfully, %FALSE otherwise.
193  **/
194 gboolean
195 _pango_cairo_font_install (PangoFont *font,
196                            cairo_t   *cr)
197 {
198   cairo_scaled_font_t *scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
199
200   if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
201     return FALSE;
202
203   cairo_set_scaled_font (cr, scaled_font);
204
205   return TRUE;
206 }
207
208
209 typedef struct _PangoCairoFontMetricsInfo
210 {
211   const char       *sample_str;
212   PangoFontMetrics *metrics;
213 } PangoCairoFontMetricsInfo;
214
215 PangoFontMetrics *
216 _pango_cairo_font_get_metrics (PangoFont     *font,
217                                PangoLanguage *language)
218 {
219   PangoCairoFont *cfont = (PangoCairoFont *) font;
220   PangoCairoFontPrivate *cf_priv = PANGO_CAIRO_FONT_PRIVATE (font);
221   PangoCairoFontMetricsInfo *info = NULL; /* Quiet gcc */
222   GSList *tmp_list;
223
224   const char *sample_str = pango_language_get_sample_string (language);
225
226   tmp_list = cf_priv->metrics_by_lang;
227   while (tmp_list)
228     {
229       info = tmp_list->data;
230
231       if (info->sample_str == sample_str)    /* We _don't_ need strcmp */
232         break;
233
234       tmp_list = tmp_list->next;
235     }
236
237   if (!tmp_list)
238     {
239       PangoFontMap *fontmap;
240       PangoContext *context;
241       cairo_font_options_t *font_options;
242       int height, shift;
243
244       /* XXX this is racy.  need a ref'ing getter... */
245       fontmap = pango_font_get_font_map (font);
246       if (!fontmap)
247         return pango_font_metrics_new ();
248       fontmap = g_object_ref (fontmap);
249
250       info = g_slice_new0 (PangoCairoFontMetricsInfo);
251
252       cf_priv->metrics_by_lang = g_slist_prepend (cf_priv->metrics_by_lang, info);
253
254       info->sample_str = sample_str;
255
256       context = pango_font_map_create_context (fontmap);
257       pango_context_set_language (context, language);
258       font_options = cairo_font_options_create ();
259       cairo_scaled_font_get_font_options (_pango_cairo_font_private_get_scaled_font (cf_priv), font_options);
260       pango_cairo_context_set_font_options (context, font_options);
261       cairo_font_options_destroy (font_options);
262
263       info->metrics = (* PANGO_CAIRO_FONT_GET_IFACE (font)->create_metrics_for_context) (cfont, context);
264
265       /* We may actually reuse ascent/descent we got from cairo here.  that's
266        * in cf_priv->font_extents.
267        */
268       height = info->metrics->ascent + info->metrics->descent;
269       switch (cf_priv->gravity)
270         {
271           default:
272           case PANGO_GRAVITY_AUTO:
273           case PANGO_GRAVITY_SOUTH:
274             break;
275           case PANGO_GRAVITY_NORTH:
276             info->metrics->ascent = info->metrics->descent;
277             break;
278           case PANGO_GRAVITY_EAST:
279           case PANGO_GRAVITY_WEST:
280             {
281               int ascent = height / 2;
282               if (cf_priv->is_hinted)
283                 ascent = PANGO_UNITS_ROUND (ascent);
284               info->metrics->ascent = ascent;
285             }
286         }
287       shift = (height - info->metrics->ascent) - info->metrics->descent;
288       info->metrics->descent += shift;
289       info->metrics->underline_position -= shift;
290       info->metrics->strikethrough_position -= shift;
291       info->metrics->ascent = height - info->metrics->descent;
292
293       g_object_unref (context);
294       g_object_unref (fontmap);
295     }
296
297   return pango_font_metrics_ref (info->metrics);
298 }
299
300 static PangoCairoFontHexBoxInfo *
301 _pango_cairo_font_private_get_hex_box_info (PangoCairoFontPrivate *cf_priv)
302 {
303   static const char hexdigits[] = "0123456789ABCDEF";
304   char c[2] = {0, 0};
305   PangoFont *mini_font;
306   PangoCairoFontHexBoxInfo *hbi;
307
308   /* for metrics hinting */
309   double scale_x = 1., scale_x_inv = 1., scale_y = 1., scale_y_inv = 1.;
310   gboolean is_hinted;
311
312   int i;
313   int rows;
314   double pad;
315   double width = 0;
316   double height = 0;
317   cairo_font_options_t *font_options;
318   cairo_font_extents_t font_extents;
319   double size, mini_size;
320   PangoFontDescription *desc;
321   cairo_scaled_font_t *scaled_font, *scaled_mini_font;
322   PangoMatrix pango_ctm;
323   cairo_matrix_t cairo_ctm;
324   PangoGravity gravity;
325
326   if (!cf_priv)
327     return NULL;
328
329   if (cf_priv->hbi)
330     return cf_priv->hbi;
331
332   scaled_font = _pango_cairo_font_private_get_scaled_font (cf_priv);
333   if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
334     return NULL;
335
336   is_hinted = cf_priv->is_hinted;
337
338   font_options = cairo_font_options_create ();
339   desc = pango_font_describe_with_absolute_size ((PangoFont *)cf_priv->cfont);
340   size = pango_font_description_get_size (desc) / (1.*PANGO_SCALE);
341   gravity = pango_font_description_get_gravity (desc);
342
343   cairo_scaled_font_get_ctm (scaled_font, &cairo_ctm);
344   cairo_scaled_font_get_font_options (scaled_font, font_options);
345   /* I started adding support for vertical hexboxes here, but it's too much
346    * work.  Easier to do with cairo user fonts and vertical writing mode
347    * support in cairo.
348    */
349   /*cairo_matrix_rotate (&cairo_ctm, pango_gravity_to_rotation (gravity));*/
350   pango_ctm.xx = cairo_ctm.xx;
351   pango_ctm.yx = cairo_ctm.yx;
352   pango_ctm.xy = cairo_ctm.xy;
353   pango_ctm.yy = cairo_ctm.yy;
354   pango_ctm.x0 = cairo_ctm.x0;
355   pango_ctm.y0 = cairo_ctm.y0;
356
357   if (is_hinted)
358     {
359       /* prepare for some hinting */
360       double x, y;
361
362       x = 1.; y = 0.;
363       cairo_matrix_transform_distance (&cairo_ctm, &x, &y);
364       scale_x = sqrt (x*x + y*y);
365       scale_x_inv = 1 / scale_x;
366
367       x = 0.; y = 1.;
368       cairo_matrix_transform_distance (&cairo_ctm, &x, &y);
369       scale_y = sqrt (x*x + y*y);
370       scale_y_inv = 1 / scale_y;
371     }
372
373 /* we hint to the nearest device units */
374 #define HINT(value, scale, scale_inv) (ceil ((value-1e-5) * scale) * scale_inv)
375 #define HINT_X(value) HINT ((value), scale_x, scale_x_inv)
376 #define HINT_Y(value) HINT ((value), scale_y, scale_y_inv)
377
378   /* create mini_font description */
379   {
380     PangoFontMap *fontmap;
381     PangoContext *context;
382
383     /* XXX this is racy.  need a ref'ing getter... */
384     fontmap = pango_font_get_font_map ((PangoFont *)cf_priv->cfont);
385     if (!fontmap)
386       return NULL;
387     fontmap = g_object_ref (fontmap);
388
389     /* we inherit most font properties for the mini font.  just
390      * change family and size.  means, you get bold hex digits
391      * in the hexbox for a bold font.
392      */
393
394     /* We should rotate the box, not glyphs */
395     pango_font_description_unset_fields (desc, PANGO_FONT_MASK_GRAVITY);
396
397     pango_font_description_set_family_static (desc, "monospace");
398
399     rows = 2;
400     mini_size = size / 2.2;
401     if (is_hinted)
402       {
403         mini_size = HINT_Y (mini_size);
404
405         if (mini_size < 6.0)
406           {
407             rows = 1;
408             mini_size = MIN (MAX (size - 1, 0), 6.0);
409           }
410       }
411
412     pango_font_description_set_absolute_size (desc, pango_units_from_double (mini_size));
413
414     /* load mini_font */
415
416     context = pango_font_map_create_context (fontmap);
417
418     pango_context_set_matrix (context, &pango_ctm);
419     pango_context_set_language (context, pango_script_get_sample_language (PANGO_SCRIPT_LATIN));
420     pango_cairo_context_set_font_options (context, font_options);
421     mini_font = pango_font_map_load_font (fontmap, context, desc);
422
423     g_object_unref (context);
424     g_object_unref (fontmap);
425   }
426
427   pango_font_description_free (desc);
428   cairo_font_options_destroy (font_options);
429
430
431   scaled_mini_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) mini_font);
432
433   for (i = 0 ; i < 16 ; i++)
434     {
435       cairo_text_extents_t extents;
436
437       c[0] = hexdigits[i];
438       cairo_scaled_font_text_extents (scaled_mini_font, c, &extents);
439       width = MAX (width, extents.width);
440       height = MAX (height, extents.height);
441     }
442
443   cairo_scaled_font_extents (scaled_font, &font_extents);
444   if (font_extents.ascent + font_extents.descent <= 0)
445     {
446       font_extents.ascent = PANGO_UNKNOWN_GLYPH_HEIGHT;
447       font_extents.descent = 0;
448     }
449
450   pad = (font_extents.ascent + font_extents.descent) / 43;
451   pad = MIN (pad, mini_size);
452
453   hbi = g_slice_new (PangoCairoFontHexBoxInfo);
454   hbi->font = (PangoCairoFont *) mini_font;
455   hbi->rows = rows;
456
457   hbi->digit_width  = width;
458   hbi->digit_height = height;
459
460   hbi->pad_x = pad;
461   hbi->pad_y = pad;
462
463   if (is_hinted)
464     {
465       hbi->digit_width  = HINT_X (hbi->digit_width);
466       hbi->digit_height = HINT_Y (hbi->digit_height);
467       hbi->pad_x = HINT_X (hbi->pad_x);
468       hbi->pad_y = HINT_Y (hbi->pad_y);
469     }
470
471   hbi->line_width = MIN (hbi->pad_x, hbi->pad_y);
472
473   hbi->box_height = 3 * hbi->pad_y + rows * (hbi->pad_y + hbi->digit_height);
474
475   if (rows == 1 || hbi->box_height <= font_extents.ascent)
476     {
477       hbi->box_descent = 2 * hbi->pad_y;
478     }
479   else if (hbi->box_height <= font_extents.ascent + font_extents.descent - 2 * hbi->pad_y)
480     {
481       hbi->box_descent = 2 * hbi->pad_y + hbi->box_height - font_extents.ascent;
482     }
483   else
484     {
485       hbi->box_descent = font_extents.descent * hbi->box_height /
486                          (font_extents.ascent + font_extents.descent);
487     }
488   if (is_hinted)
489     {
490        hbi->box_descent = HINT_Y (hbi->box_descent);
491     }
492
493   cf_priv->hbi = hbi;
494   return hbi;
495 }
496
497 static void
498 _pango_cairo_font_hex_box_info_destroy (PangoCairoFontHexBoxInfo *hbi)
499 {
500   if (hbi)
501     {
502       g_object_unref (hbi->font);
503       g_slice_free (PangoCairoFontHexBoxInfo, hbi);
504     }
505 }
506
507 PangoCairoFontHexBoxInfo *
508 _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont)
509 {
510   PangoCairoFontPrivate *cf_priv = PANGO_CAIRO_FONT_PRIVATE (cfont);
511
512   return _pango_cairo_font_private_get_hex_box_info (cf_priv);
513 }
514
515 void
516 _pango_cairo_font_private_initialize (PangoCairoFontPrivate      *cf_priv,
517                                       PangoCairoFont             *cfont,
518                                       PangoGravity                gravity,
519                                       const cairo_font_options_t *font_options,
520                                       const PangoMatrix          *pango_ctm,
521                                       const cairo_matrix_t       *font_matrix)
522 {
523   cairo_matrix_t gravity_matrix;
524
525   cf_priv->cfont = cfont;
526   cf_priv->gravity = gravity;
527
528   cf_priv->data = _pango_cairo_font_private_scaled_font_data_create (); 
529
530   /* first apply gravity rotation, then font_matrix, such that
531    * vertical italic text comes out "correct".  we don't do anything
532    * like baseline adjustment etc though.  should be specially
533    * handled when we support italic correction. */
534   cairo_matrix_init_rotate(&gravity_matrix,
535                            pango_gravity_to_rotation (cf_priv->gravity));
536   cairo_matrix_multiply (&cf_priv->data->font_matrix,
537                          font_matrix,
538                          &gravity_matrix);
539
540   if (pango_ctm)
541     cairo_matrix_init (&cf_priv->data->ctm,
542                        pango_ctm->xx,
543                        pango_ctm->yx,
544                        pango_ctm->xy,
545                        pango_ctm->yy,
546                        0., 0.);
547   else
548     cairo_matrix_init_identity (&cf_priv->data->ctm);
549
550   cf_priv->data->options = cairo_font_options_copy (font_options);
551   cf_priv->is_hinted = cairo_font_options_get_hint_metrics (font_options) != CAIRO_HINT_METRICS_OFF;
552
553   cf_priv->scaled_font = NULL;
554   cf_priv->hbi = NULL;
555   cf_priv->glyph_extents_cache = NULL;
556   cf_priv->metrics_by_lang = NULL;
557 }
558
559 static void
560 free_metrics_info (PangoCairoFontMetricsInfo *info)
561 {
562   pango_font_metrics_unref (info->metrics);
563   g_slice_free (PangoCairoFontMetricsInfo, info);
564 }
565
566 void
567 _pango_cairo_font_private_finalize (PangoCairoFontPrivate *cf_priv)
568 {
569   _pango_cairo_font_private_scaled_font_data_destroy (cf_priv->data);
570
571   if (cf_priv->scaled_font)
572     cairo_scaled_font_destroy (cf_priv->scaled_font);
573   cf_priv->scaled_font = NULL;
574
575   _pango_cairo_font_hex_box_info_destroy (cf_priv->hbi);
576   cf_priv->hbi = NULL;
577
578   if (cf_priv->glyph_extents_cache)
579     g_free (cf_priv->glyph_extents_cache);
580   cf_priv->glyph_extents_cache = NULL;
581
582   g_slist_foreach (cf_priv->metrics_by_lang, (GFunc)free_metrics_info, NULL);
583   g_slist_free (cf_priv->metrics_by_lang);
584   cf_priv->metrics_by_lang = NULL;
585 }
586
587 gboolean
588 _pango_cairo_font_private_is_metrics_hinted (PangoCairoFontPrivate *cf_priv)
589 {
590   return cf_priv->is_hinted;
591 }
592
593 static void
594 _pango_cairo_font_private_get_glyph_extents_missing (PangoCairoFontPrivate *cf_priv,
595                                                      PangoGlyph             glyph,
596                                                      PangoRectangle        *ink_rect,
597                                                      PangoRectangle        *logical_rect)
598 {
599   PangoCairoFontHexBoxInfo *hbi;
600   gunichar ch;
601   gint rows, cols;
602
603   hbi = _pango_cairo_font_private_get_hex_box_info (cf_priv);
604   if (!hbi)
605     {
606       pango_font_get_glyph_extents (NULL, glyph, ink_rect, logical_rect);
607       return;
608     }
609
610   ch = glyph & ~PANGO_GLYPH_UNKNOWN_FLAG;
611
612   rows = hbi->rows;
613   if (G_UNLIKELY (glyph == PANGO_GLYPH_INVALID_INPUT || ch > 0x10FFFF))
614     cols = 1;
615   else
616     cols = ((glyph & ~PANGO_GLYPH_UNKNOWN_FLAG) > 0xffff ? 6 : 4) / rows;
617
618   if (ink_rect)
619     {
620       ink_rect->x = PANGO_SCALE * hbi->pad_x;
621       ink_rect->y = PANGO_SCALE * (hbi->box_descent - hbi->box_height);
622       ink_rect->width = PANGO_SCALE * (3 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x));
623       ink_rect->height = PANGO_SCALE * hbi->box_height;
624     }
625
626   if (logical_rect)
627     {
628       logical_rect->x = 0;
629       logical_rect->y = PANGO_SCALE * (hbi->box_descent - (hbi->box_height + hbi->pad_y));
630       logical_rect->width = PANGO_SCALE * (5 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x));
631       logical_rect->height = PANGO_SCALE * (hbi->box_height + 2 * hbi->pad_y);
632     }
633 }
634
635 #define GLYPH_CACHE_NUM_ENTRIES 256 /* should be power of two */
636 #define GLYPH_CACHE_MASK (GLYPH_CACHE_NUM_ENTRIES - 1)
637 /* An entry in the fixed-size cache for the glyph->extents mapping.
638  * The cache is indexed by the lower N bits of the glyph (see
639  * GLYPH_CACHE_NUM_ENTRIES).  For scripts with few glyphs,
640  * this should provide pretty much instant lookups.
641  */
642 struct _PangoCairoFontGlyphExtentsCacheEntry
643 {
644   PangoGlyph     glyph;
645   int            width;
646   PangoRectangle ink_rect;
647 };
648
649 static gboolean
650 _pango_cairo_font_private_glyph_extents_cache_init (PangoCairoFontPrivate *cf_priv)
651 {
652   cairo_scaled_font_t *scaled_font = _pango_cairo_font_private_get_scaled_font (cf_priv);
653   cairo_font_extents_t font_extents;
654
655   if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
656     return FALSE;
657
658   cairo_scaled_font_extents (scaled_font, &font_extents);
659
660   cf_priv->font_extents.x = 0;
661   cf_priv->font_extents.width = 0;
662   cf_priv->font_extents.height = pango_units_from_double (font_extents.ascent + font_extents.descent);
663   switch (cf_priv->gravity)
664     {
665       default:
666       case PANGO_GRAVITY_AUTO:
667       case PANGO_GRAVITY_SOUTH:
668         cf_priv->font_extents.y = - pango_units_from_double (font_extents.ascent);
669         break;
670       case PANGO_GRAVITY_NORTH:
671         cf_priv->font_extents.y = - pango_units_from_double (font_extents.descent);
672         break;
673       case PANGO_GRAVITY_EAST:
674       case PANGO_GRAVITY_WEST:
675         {
676           int ascent = pango_units_from_double (font_extents.ascent + font_extents.descent) / 2;
677           if (cf_priv->is_hinted)
678             ascent = PANGO_UNITS_ROUND (ascent);
679           cf_priv->font_extents.y = - ascent;
680         }
681     }
682
683   cf_priv->glyph_extents_cache = g_new0 (PangoCairoFontGlyphExtentsCacheEntry, GLYPH_CACHE_NUM_ENTRIES);
684   /* Make sure all cache entries are invalid initially */
685   cf_priv->glyph_extents_cache[0].glyph = 1; /* glyph 1 cannot happen in bucket 0 */
686
687   return TRUE;
688 }
689
690 /* Fills in the glyph extents cache entry
691  */
692 static void
693 compute_glyph_extents (PangoCairoFontPrivate  *cf_priv,
694                        PangoGlyph              glyph,
695                        PangoCairoFontGlyphExtentsCacheEntry *entry)
696 {
697   cairo_text_extents_t extents;
698   cairo_glyph_t cairo_glyph;
699
700   cairo_glyph.index = glyph;
701   cairo_glyph.x = 0;
702   cairo_glyph.y = 0;
703
704   cairo_scaled_font_glyph_extents (_pango_cairo_font_private_get_scaled_font (cf_priv),
705                                    &cairo_glyph, 1, &extents);
706
707   entry->glyph = glyph;
708   entry->width = pango_units_from_double (extents.x_advance);
709   entry->ink_rect.x = pango_units_from_double (extents.x_bearing);
710   entry->ink_rect.y = pango_units_from_double (extents.y_bearing);
711   entry->ink_rect.width = pango_units_from_double (extents.width);
712   entry->ink_rect.height = pango_units_from_double (extents.height);
713 }
714
715 static PangoCairoFontGlyphExtentsCacheEntry *
716 _pango_cairo_font_private_get_glyph_extents_cache_entry (PangoCairoFontPrivate  *cf_priv,
717                                                          PangoGlyph              glyph)
718 {
719   PangoCairoFontGlyphExtentsCacheEntry *entry;
720   guint idx;
721
722   idx = glyph & GLYPH_CACHE_MASK;
723   entry = cf_priv->glyph_extents_cache + idx;
724
725   if (entry->glyph != glyph)
726     {
727       compute_glyph_extents (cf_priv, glyph, entry);
728     }
729
730   return entry;
731 }
732
733 void
734 _pango_cairo_font_private_get_glyph_extents (PangoCairoFontPrivate *cf_priv,
735                                              PangoGlyph             glyph,
736                                              PangoRectangle        *ink_rect,
737                                              PangoRectangle        *logical_rect)
738 {
739   PangoCairoFontGlyphExtentsCacheEntry *entry;
740
741   if (!cf_priv ||
742       (cf_priv->glyph_extents_cache == NULL &&
743        !_pango_cairo_font_private_glyph_extents_cache_init (cf_priv)))
744     {
745       /* Get generic unknown-glyph extents. */
746       pango_font_get_glyph_extents (NULL, glyph, ink_rect, logical_rect);
747       return;
748     }
749
750   if (glyph == PANGO_GLYPH_EMPTY)
751     {
752       if (ink_rect)
753         ink_rect->x = ink_rect->y = ink_rect->width = ink_rect->height = 0;
754       if (logical_rect)
755         *logical_rect = cf_priv->font_extents;
756       return;
757     }
758   else if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
759     {
760       _pango_cairo_font_private_get_glyph_extents_missing(cf_priv, glyph, ink_rect, logical_rect);
761       return;
762     }
763
764   entry = _pango_cairo_font_private_get_glyph_extents_cache_entry (cf_priv, glyph);
765
766   if (ink_rect)
767     *ink_rect = entry->ink_rect;
768   if (logical_rect)
769     {
770       *logical_rect = cf_priv->font_extents;
771       logical_rect->width = entry->width;
772     }
773 }