2 * pangocairo-font.c: Cairo font handling
4 * Copyright (C) 2000-2005 Red Hat Software
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.
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.
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.
26 #include "pangocairo.h"
27 #include "pangocairo-private.h"
28 #include "pango-impl-utils.h"
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)))
37 pango_cairo_font_get_type (void)
39 static GType cairo_font_type = 0;
41 if (! cairo_font_type)
43 const GTypeInfo cairo_font_info =
45 sizeof (PangoCairoFontIface), /* class_size */
47 NULL, /* base_finalize */
49 NULL, /* class_finalize */
50 NULL, /* class_data */
58 g_type_register_static (G_TYPE_INTERFACE, I_("PangoCairoFont"),
61 g_type_interface_add_prerequisite (cairo_font_type, PANGO_TYPE_FONT);
64 return cairo_font_type;
67 static PangoCairoFontPrivateScaledFontData *
68 _pango_cairo_font_private_scaled_font_data_create (void)
70 return g_slice_new (PangoCairoFontPrivateScaledFontData);
74 _pango_cairo_font_private_scaled_font_data_destroy (PangoCairoFontPrivateScaledFontData *data)
78 cairo_font_options_destroy (data->options);
79 g_slice_free (PangoCairoFontPrivateScaledFontData, data);
84 _pango_cairo_font_private_get_scaled_font (PangoCairoFontPrivate *cf_priv)
86 cairo_font_face_t *font_face;
88 if (G_LIKELY (cf_priv->scaled_font))
89 return cf_priv->scaled_font;
91 /* need to create it */
93 if (G_UNLIKELY (cf_priv->data == NULL))
95 /* we have tried to create and failed before */
99 font_face = (* PANGO_CAIRO_FONT_GET_IFACE (cf_priv->cfont)->create_font_face) (cf_priv->cfont);
100 if (G_UNLIKELY (font_face == NULL))
103 cf_priv->scaled_font = cairo_scaled_font_create (font_face,
104 &cf_priv->data->font_matrix,
106 cf_priv->data->options);
108 cairo_font_face_destroy (font_face);
112 if (G_UNLIKELY (cf_priv->scaled_font == NULL || cairo_scaled_font_status (cf_priv->scaled_font) != CAIRO_STATUS_SUCCESS))
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;
118 warned_quark = g_quark_from_static_string ("pangocairo-scaledfont-warned");
120 if (!g_object_get_qdata (G_OBJECT (font), warned_quark))
122 PangoFontDescription *desc;
125 desc = pango_font_describe (font);
126 s = pango_font_description_to_string (desc);
127 pango_font_description_free (desc);
129 g_warning ("failed to create cairo %s, expect ugly output. the offending font is '%s'",
130 font_face ? "scaled font" : "font face",
134 g_warning ("font_face is NULL");
136 g_warning ("font_face status is: %s",
137 cairo_status_to_string (cairo_font_face_status (font_face)));
140 g_warning ("scaled_font is NULL");
142 g_warning ("scaled_font status is: %s",
143 cairo_status_to_string (cairo_scaled_font_status (scaled_font)));
147 g_object_set_qdata_full (G_OBJECT (font), warned_quark,
148 GINT_TO_POINTER (1), NULL);
152 _pango_cairo_font_private_scaled_font_data_destroy (cf_priv->data);
153 cf_priv->data = NULL;
155 return cf_priv->scaled_font;
159 * pango_cairo_font_get_scaled_font:
160 * @font: a #PangoFont from a #PangoCairoFontMap
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().
166 * Return value: the #cairo_scaled_font_t used by @font,
167 * or %NULL if @font is %NULL.
171 cairo_scaled_font_t *
172 pango_cairo_font_get_scaled_font (PangoCairoFont *cfont)
174 PangoCairoFontPrivate *cf_priv;
176 if (G_UNLIKELY (!cfont))
179 cf_priv = PANGO_CAIRO_FONT_PRIVATE (cfont);
181 return _pango_cairo_font_private_get_scaled_font (cf_priv);
185 * _pango_cairo_font_install:
186 * @font: a #PangoCairoFont
189 * Makes @font the current font for rendering in the specified
192 * Return value: %TRUE if font was installed successfully, %FALSE otherwise.
195 _pango_cairo_font_install (PangoFont *font,
198 cairo_scaled_font_t *scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
200 if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
203 cairo_set_scaled_font (cr, scaled_font);
209 typedef struct _PangoCairoFontMetricsInfo
211 const char *sample_str;
212 PangoFontMetrics *metrics;
213 } PangoCairoFontMetricsInfo;
216 _pango_cairo_font_get_metrics (PangoFont *font,
217 PangoLanguage *language)
219 PangoCairoFont *cfont = (PangoCairoFont *) font;
220 PangoCairoFontPrivate *cf_priv = PANGO_CAIRO_FONT_PRIVATE (font);
221 PangoCairoFontMetricsInfo *info = NULL; /* Quiet gcc */
224 const char *sample_str = pango_language_get_sample_string (language);
226 tmp_list = cf_priv->metrics_by_lang;
229 info = tmp_list->data;
231 if (info->sample_str == sample_str) /* We _don't_ need strcmp */
234 tmp_list = tmp_list->next;
239 PangoFontMap *fontmap;
240 PangoContext *context;
241 cairo_font_options_t *font_options;
244 /* XXX this is racy. need a ref'ing getter... */
245 fontmap = pango_font_get_font_map (font);
247 return pango_font_metrics_new ();
248 fontmap = g_object_ref (fontmap);
250 info = g_slice_new0 (PangoCairoFontMetricsInfo);
252 cf_priv->metrics_by_lang = g_slist_prepend (cf_priv->metrics_by_lang, info);
254 info->sample_str = sample_str;
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);
263 info->metrics = (* PANGO_CAIRO_FONT_GET_IFACE (font)->create_metrics_for_context) (cfont, context);
265 /* We may actually reuse ascent/descent we got from cairo here. that's
266 * in cf_priv->font_extents.
268 height = info->metrics->ascent + info->metrics->descent;
269 switch (cf_priv->gravity)
272 case PANGO_GRAVITY_AUTO:
273 case PANGO_GRAVITY_SOUTH:
275 case PANGO_GRAVITY_NORTH:
276 info->metrics->ascent = info->metrics->descent;
278 case PANGO_GRAVITY_EAST:
279 case PANGO_GRAVITY_WEST:
281 int ascent = height / 2;
282 if (cf_priv->is_hinted)
283 ascent = PANGO_UNITS_ROUND (ascent);
284 info->metrics->ascent = ascent;
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;
293 g_object_unref (context);
294 g_object_unref (fontmap);
297 return pango_font_metrics_ref (info->metrics);
300 static PangoCairoFontHexBoxInfo *
301 _pango_cairo_font_private_get_hex_box_info (PangoCairoFontPrivate *cf_priv)
303 static const char hexdigits[] = "0123456789ABCDEF";
305 PangoFont *mini_font;
306 PangoCairoFontHexBoxInfo *hbi;
308 /* for metrics hinting */
309 double scale_x = 1., scale_x_inv = 1., scale_y = 1., scale_y_inv = 1.;
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;
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))
336 is_hinted = cf_priv->is_hinted;
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);
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
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;
359 /* prepare for some hinting */
363 cairo_matrix_transform_distance (&cairo_ctm, &x, &y);
364 scale_x = sqrt (x*x + y*y);
365 scale_x_inv = 1 / scale_x;
368 cairo_matrix_transform_distance (&cairo_ctm, &x, &y);
369 scale_y = sqrt (x*x + y*y);
370 scale_y_inv = 1 / scale_y;
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)
378 /* create mini_font description */
380 PangoFontMap *fontmap;
381 PangoContext *context;
383 /* XXX this is racy. need a ref'ing getter... */
384 fontmap = pango_font_get_font_map ((PangoFont *)cf_priv->cfont);
387 fontmap = g_object_ref (fontmap);
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.
394 /* We should rotate the box, not glyphs */
395 pango_font_description_unset_fields (desc, PANGO_FONT_MASK_GRAVITY);
397 pango_font_description_set_family_static (desc, "monospace");
400 mini_size = size / 2.2;
403 mini_size = HINT_Y (mini_size);
408 mini_size = MIN (MAX (size - 1, 0), 6.0);
412 pango_font_description_set_absolute_size (desc, pango_units_from_double (mini_size));
416 context = pango_font_map_create_context (fontmap);
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);
423 g_object_unref (context);
424 g_object_unref (fontmap);
427 pango_font_description_free (desc);
428 cairo_font_options_destroy (font_options);
431 scaled_mini_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) mini_font);
433 for (i = 0 ; i < 16 ; i++)
435 cairo_text_extents_t extents;
438 cairo_scaled_font_text_extents (scaled_mini_font, c, &extents);
439 width = MAX (width, extents.width);
440 height = MAX (height, extents.height);
443 cairo_scaled_font_extents (scaled_font, &font_extents);
444 if (font_extents.ascent + font_extents.descent <= 0)
446 font_extents.ascent = PANGO_UNKNOWN_GLYPH_HEIGHT;
447 font_extents.descent = 0;
450 pad = (font_extents.ascent + font_extents.descent) / 43;
451 pad = MIN (pad, mini_size);
453 hbi = g_slice_new (PangoCairoFontHexBoxInfo);
454 hbi->font = (PangoCairoFont *) mini_font;
457 hbi->digit_width = width;
458 hbi->digit_height = height;
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);
471 hbi->line_width = MIN (hbi->pad_x, hbi->pad_y);
473 hbi->box_height = 3 * hbi->pad_y + rows * (hbi->pad_y + hbi->digit_height);
475 if (rows == 1 || hbi->box_height <= font_extents.ascent)
477 hbi->box_descent = 2 * hbi->pad_y;
479 else if (hbi->box_height <= font_extents.ascent + font_extents.descent - 2 * hbi->pad_y)
481 hbi->box_descent = 2 * hbi->pad_y + hbi->box_height - font_extents.ascent;
485 hbi->box_descent = font_extents.descent * hbi->box_height /
486 (font_extents.ascent + font_extents.descent);
490 hbi->box_descent = HINT_Y (hbi->box_descent);
498 _pango_cairo_font_hex_box_info_destroy (PangoCairoFontHexBoxInfo *hbi)
502 g_object_unref (hbi->font);
503 g_slice_free (PangoCairoFontHexBoxInfo, hbi);
507 PangoCairoFontHexBoxInfo *
508 _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont)
510 PangoCairoFontPrivate *cf_priv = PANGO_CAIRO_FONT_PRIVATE (cfont);
512 return _pango_cairo_font_private_get_hex_box_info (cf_priv);
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)
523 cairo_matrix_t gravity_matrix;
525 cf_priv->cfont = cfont;
526 cf_priv->gravity = gravity;
528 cf_priv->data = _pango_cairo_font_private_scaled_font_data_create ();
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,
541 cairo_matrix_init (&cf_priv->data->ctm,
548 cairo_matrix_init_identity (&cf_priv->data->ctm);
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;
553 cf_priv->scaled_font = NULL;
555 cf_priv->glyph_extents_cache = NULL;
556 cf_priv->metrics_by_lang = NULL;
560 free_metrics_info (PangoCairoFontMetricsInfo *info)
562 pango_font_metrics_unref (info->metrics);
563 g_slice_free (PangoCairoFontMetricsInfo, info);
567 _pango_cairo_font_private_finalize (PangoCairoFontPrivate *cf_priv)
569 _pango_cairo_font_private_scaled_font_data_destroy (cf_priv->data);
571 if (cf_priv->scaled_font)
572 cairo_scaled_font_destroy (cf_priv->scaled_font);
573 cf_priv->scaled_font = NULL;
575 _pango_cairo_font_hex_box_info_destroy (cf_priv->hbi);
578 if (cf_priv->glyph_extents_cache)
579 g_free (cf_priv->glyph_extents_cache);
580 cf_priv->glyph_extents_cache = NULL;
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;
588 _pango_cairo_font_private_is_metrics_hinted (PangoCairoFontPrivate *cf_priv)
590 return cf_priv->is_hinted;
594 _pango_cairo_font_private_get_glyph_extents_missing (PangoCairoFontPrivate *cf_priv,
596 PangoRectangle *ink_rect,
597 PangoRectangle *logical_rect)
599 PangoCairoFontHexBoxInfo *hbi;
603 hbi = _pango_cairo_font_private_get_hex_box_info (cf_priv);
606 pango_font_get_glyph_extents (NULL, glyph, ink_rect, logical_rect);
610 ch = glyph & ~PANGO_GLYPH_UNKNOWN_FLAG;
613 if (G_UNLIKELY (glyph == PANGO_GLYPH_INVALID_INPUT || ch > 0x10FFFF))
616 cols = ((glyph & ~PANGO_GLYPH_UNKNOWN_FLAG) > 0xffff ? 6 : 4) / rows;
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;
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);
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.
642 struct _PangoCairoFontGlyphExtentsCacheEntry
646 PangoRectangle ink_rect;
650 _pango_cairo_font_private_glyph_extents_cache_init (PangoCairoFontPrivate *cf_priv)
652 cairo_scaled_font_t *scaled_font = _pango_cairo_font_private_get_scaled_font (cf_priv);
653 cairo_font_extents_t font_extents;
655 if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
658 cairo_scaled_font_extents (scaled_font, &font_extents);
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)
666 case PANGO_GRAVITY_AUTO:
667 case PANGO_GRAVITY_SOUTH:
668 cf_priv->font_extents.y = - pango_units_from_double (font_extents.ascent);
670 case PANGO_GRAVITY_NORTH:
671 cf_priv->font_extents.y = - pango_units_from_double (font_extents.descent);
673 case PANGO_GRAVITY_EAST:
674 case PANGO_GRAVITY_WEST:
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;
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 */
690 /* Fills in the glyph extents cache entry
693 compute_glyph_extents (PangoCairoFontPrivate *cf_priv,
695 PangoCairoFontGlyphExtentsCacheEntry *entry)
697 cairo_text_extents_t extents;
698 cairo_glyph_t cairo_glyph;
700 cairo_glyph.index = glyph;
704 cairo_scaled_font_glyph_extents (_pango_cairo_font_private_get_scaled_font (cf_priv),
705 &cairo_glyph, 1, &extents);
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);
715 static PangoCairoFontGlyphExtentsCacheEntry *
716 _pango_cairo_font_private_get_glyph_extents_cache_entry (PangoCairoFontPrivate *cf_priv,
719 PangoCairoFontGlyphExtentsCacheEntry *entry;
722 idx = glyph & GLYPH_CACHE_MASK;
723 entry = cf_priv->glyph_extents_cache + idx;
725 if (entry->glyph != glyph)
727 compute_glyph_extents (cf_priv, glyph, entry);
734 _pango_cairo_font_private_get_glyph_extents (PangoCairoFontPrivate *cf_priv,
736 PangoRectangle *ink_rect,
737 PangoRectangle *logical_rect)
739 PangoCairoFontGlyphExtentsCacheEntry *entry;
742 (cf_priv->glyph_extents_cache == NULL &&
743 !_pango_cairo_font_private_glyph_extents_cache_init (cf_priv)))
745 /* Get generic unknown-glyph extents. */
746 pango_font_get_glyph_extents (NULL, glyph, ink_rect, logical_rect);
750 if (glyph == PANGO_GLYPH_EMPTY)
753 ink_rect->x = ink_rect->y = ink_rect->width = ink_rect->height = 0;
755 *logical_rect = cf_priv->font_extents;
758 else if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
760 _pango_cairo_font_private_get_glyph_extents_missing(cf_priv, glyph, ink_rect, logical_rect);
764 entry = _pango_cairo_font_private_get_glyph_extents_cache_entry (cf_priv, glyph);
767 *ink_rect = entry->ink_rect;
770 *logical_rect = cf_priv->font_extents;
771 logical_rect->width = entry->width;