summaryrefslogtreecommitdiff
path: root/pango/pangocairo-font.c
diff options
context:
space:
mode:
Diffstat (limited to 'pango/pangocairo-font.c')
-rw-r--r--pango/pangocairo-font.c506
1 files changed, 439 insertions, 67 deletions
diff --git a/pango/pangocairo-font.c b/pango/pangocairo-font.c
index 2595c0ef..5f439998 100644
--- a/pango/pangocairo-font.c
+++ b/pango/pangocairo-font.c
@@ -29,6 +29,12 @@
PangoCairoWarningHistory _pango_cairo_warning_history = { FALSE };
+#define PANGO_CAIRO_FONT_PRIVATE(font) \
+ ((PangoCairoFontPrivate *) \
+ (font == NULL ? NULL : \
+ G_STRUCT_MEMBER_P (font, \
+ PANGO_CAIRO_FONT_GET_IFACE(PANGO_CAIRO_FONT(font))->cf_priv_offset)))
+
GType
pango_cairo_font_get_type (void)
{
@@ -60,45 +66,86 @@ pango_cairo_font_get_type (void)
return cairo_font_type;
}
-/**
- * _pango_cairo_font_install:
- * @font: a #PangoCairoFont
- * @cr: a #cairo_t
- *
- * Makes @font the current font for rendering in the specified
- * Cairo context.
- **/
-gboolean
-_pango_cairo_font_install (PangoCairoFont *font,
- cairo_t *cr)
+static PangoCairoFontPrivateScaledFontData *
+_pango_cairo_font_private_scaled_font_data_create (void)
+{
+ return g_slice_new (PangoCairoFontPrivateScaledFontData);
+}
+
+static void
+_pango_cairo_font_private_scaled_font_data_destroy (PangoCairoFontPrivateScaledFontData *data)
{
- if (G_UNLIKELY (!PANGO_IS_CAIRO_FONT (font)))
+ cairo_font_options_destroy (data->options);
+ g_slice_free (PangoCairoFontPrivateScaledFontData, data);
+}
+
+cairo_scaled_font_t *
+_pango_cairo_font_private_get_scaled_font (PangoCairoFontPrivate *cf_priv)
+{
+ cairo_font_face_t *font_face;
+
+ if (G_LIKELY (cf_priv->scaled_font))
+ return cf_priv->scaled_font;
+
+ /* need to create it */
+
+ if (G_UNLIKELY (cf_priv->data == NULL))
+ {
+ /* we have tried to create and failed before */
+ return NULL;
+ }
+
+ font_face = (* PANGO_CAIRO_FONT_GET_IFACE (cf_priv->cfont)->create_font_face) (cf_priv->cfont);
+ if (G_UNLIKELY (font_face == NULL))
+ goto done;
+
+ cf_priv->scaled_font = cairo_scaled_font_create (font_face,
+ &cf_priv->data->font_matrix,
+ &cf_priv->data->ctm,
+ cf_priv->data->options);
+
+ cairo_font_face_destroy (font_face);
+
+done:
+ _pango_cairo_font_private_scaled_font_data_destroy (cf_priv->data);
+ cf_priv->data = NULL;
+
+ if (G_UNLIKELY (cf_priv->scaled_font == NULL || cairo_scaled_font_status (cf_priv->scaled_font) != CAIRO_STATUS_SUCCESS))
{
- if (!_pango_cairo_warning_history.font_install)
+ PangoFont *font = PANGO_FONT (cf_priv->cfont);
+ static GQuark warned_quark = 0;
+ if (!warned_quark)
+ warned_quark = g_quark_from_static_string ("pangocairo-scaledfont-warned");
+
+ if (!g_object_get_qdata (G_OBJECT (font), warned_quark))
{
- _pango_cairo_warning_history.font_install = TRUE;
- g_warning ("_pango_cairo_font_install called with bad font, expect ugly output");
- cairo_set_font_face (cr, NULL);
+ PangoFontDescription *desc;
+ char *s;
+
+ desc = pango_font_describe (font);
+ s = pango_font_description_to_string (desc);
+ pango_font_description_free (desc);
+
+ g_warning ("failed to create cairo %s, expect ugly output. the offending font is '%s'",
+ font_face ? "scaled font" : "font face",
+ s);
+
+ g_free (s);
+
+ g_object_set_qdata_full (G_OBJECT (font), warned_quark,
+ GINT_TO_POINTER (1), NULL);
}
- return FALSE;
}
- return (* PANGO_CAIRO_FONT_GET_IFACE (font)->install) (font, cr);
+ return cf_priv->scaled_font;
}
-cairo_font_face_t *
-_pango_cairo_font_get_font_face (PangoCairoFont *font)
+static cairo_scaled_font_t *
+_pango_cairo_font_get_scaled_font (PangoFont *cfont)
{
- g_return_val_if_fail (PANGO_IS_CAIRO_FONT (font), NULL);
- /* this function will be removed. don't bother with warning history here */
-
- return (* PANGO_CAIRO_FONT_GET_IFACE (font)->get_font_face) (font);
-}
+ PangoCairoFontPrivate *cf_priv;
-cairo_scaled_font_t *
-_pango_cairo_font_get_scaled_font (PangoCairoFont *font)
-{
- if (G_UNLIKELY (!PANGO_IS_CAIRO_FONT (font)))
+ if (G_UNLIKELY (!PANGO_IS_CAIRO_FONT (cfont)))
{
if (!_pango_cairo_warning_history.font_get_scaled_font)
{
@@ -108,27 +155,126 @@ _pango_cairo_font_get_scaled_font (PangoCairoFont *font)
return NULL;
}
- return (* PANGO_CAIRO_FONT_GET_IFACE (font)->get_scaled_font) (font);
+ cf_priv = PANGO_CAIRO_FONT_PRIVATE (cfont);
+
+ return _pango_cairo_font_private_get_scaled_font (cf_priv);
}
-static void
-_pango_cairo_hex_box_info_destroy (PangoCairoHexBoxInfo *hbi)
+/**
+ * _pango_cairo_font_install:
+ * @font: a #PangoCairoFont
+ * @cr: a #cairo_t
+ *
+ * Makes @font the current font for rendering in the specified
+ * Cairo context.
+ *
+ * Return value: %TRUE if font was installed successfully, %FALSE otherwise.
+ **/
+gboolean
+_pango_cairo_font_install (PangoFont *font,
+ cairo_t *cr)
{
- if (hbi)
+ cairo_scaled_font_t *scaled_font = _pango_cairo_font_get_scaled_font (font);
+
+ if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
+ return FALSE;
+
+ cairo_set_scaled_font (cr, scaled_font);
+
+ return TRUE;
+}
+
+
+typedef struct _PangoCairoFontMetricsInfo
+{
+ const char *sample_str;
+ PangoFontMetrics *metrics;
+} PangoCairoFontMetricsInfo;
+
+PangoFontMetrics *
+_pango_cairo_font_get_metrics (PangoFont *font,
+ PangoLanguage *language)
+{
+ PangoCairoFont *cfont = (PangoCairoFont *) font;
+ PangoCairoFontPrivate *cf_priv = PANGO_CAIRO_FONT_PRIVATE (font);
+ PangoCairoFontMetricsInfo *info = NULL; /* Quiet gcc */
+ GSList *tmp_list;
+
+ const char *sample_str = pango_language_get_sample_string (language);
+
+ tmp_list = cf_priv->metrics_by_lang;
+ while (tmp_list)
{
- g_object_unref (hbi->font);
- g_slice_free (PangoCairoHexBoxInfo, hbi);
+ info = tmp_list->data;
+
+ if (info->sample_str == sample_str) /* We _don't_ need strcmp */
+ break;
+
+ tmp_list = tmp_list->next;
}
+
+ if (!tmp_list)
+ {
+ PangoContext *context;
+ cairo_font_options_t *font_options;
+ int height, shift;
+
+ info = g_slice_new0 (PangoCairoFontMetricsInfo);
+
+ cf_priv->metrics_by_lang = g_slist_prepend (cf_priv->metrics_by_lang, info);
+
+ info->sample_str = sample_str;
+
+ context = pango_cairo_font_map_create_context ((PangoCairoFontMap *) pango_font_get_font_map (font));
+ pango_context_set_language (context, language);
+ font_options = cairo_font_options_create ();
+ cairo_scaled_font_get_font_options (_pango_cairo_font_private_get_scaled_font (cf_priv), font_options);
+ pango_cairo_context_set_font_options (context, font_options);
+ cairo_font_options_destroy (font_options);
+
+ info->metrics = (* PANGO_CAIRO_FONT_GET_IFACE (font)->create_metrics_for_context) (cfont, context);
+
+ /* We may actually reuse ascent/descent we got from cairo here. that's
+ * in cf_priv->font_extents.
+ */
+ height = info->metrics->ascent + info->metrics->descent;
+ switch (cf_priv->gravity)
+ {
+ default:
+ case PANGO_GRAVITY_AUTO:
+ case PANGO_GRAVITY_SOUTH:
+ break;
+ case PANGO_GRAVITY_NORTH:
+ info->metrics->ascent = info->metrics->descent;
+ break;
+ case PANGO_GRAVITY_EAST:
+ case PANGO_GRAVITY_WEST:
+ {
+ int ascent = height / 2;
+ if (cf_priv->is_hinted)
+ ascent = PANGO_UNITS_ROUND (ascent);
+ info->metrics->ascent = ascent;
+ }
+ }
+ shift = (height - info->metrics->ascent) - info->metrics->descent;
+ info->metrics->descent += shift;
+ info->metrics->underline_position -= shift;
+ info->metrics->strikethrough_position -= shift;
+ info->metrics->ascent = height - info->metrics->descent;
+
+ g_object_unref (context);
+ }
+
+ return pango_font_metrics_ref (info->metrics);
}
-PangoCairoHexBoxInfo *
-_pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont)
+static PangoCairoFontHexBoxInfo *
+_pango_cairo_font_private_get_hex_box_info (PangoCairoFontPrivate *cf_priv)
{
static const char hexdigits[] = "0123456789ABCDEF";
char c[2] = {0, 0};
PangoFont *mini_font;
- PangoCairoFont *mini_cfont;
- PangoCairoHexBoxInfo *hbi;
+ PangoCairoFontHexBoxInfo *hbi;
/* for metrics hinting */
double scale_x = 1., scale_x_inv = 1., scale_y = 1., scale_y_inv = 1.;
@@ -148,24 +294,20 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont)
cairo_matrix_t cairo_ctm;
PangoGravity gravity;
- if (!cfont)
+ if (!cf_priv)
return NULL;
- hbi = (PangoCairoHexBoxInfo *) g_object_get_data (G_OBJECT (cfont), "hex_box_info");
- if (hbi)
- return hbi;
+ if (cf_priv->hbi)
+ return cf_priv->hbi;
- scaled_font = _pango_cairo_font_get_scaled_font (cfont);
- if (!scaled_font)
- {
- g_object_set_data_full (G_OBJECT (cfont), "hex_box_info", NULL, NULL);
- return NULL;
- }
+ scaled_font = _pango_cairo_font_private_get_scaled_font (cf_priv);
+ if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
+ return NULL;
+
+ is_hinted = cf_priv->is_hinted;
font_options = cairo_font_options_create ();
- cairo_scaled_font_get_font_options (scaled_font, font_options);
- is_hinted = (cairo_font_options_get_hint_metrics(font_options) != CAIRO_HINT_METRICS_OFF);
- desc = pango_font_describe_with_absolute_size ((PangoFont *)cfont);
+ desc = pango_font_describe_with_absolute_size ((PangoFont *)cf_priv->cfont);
size = pango_font_description_get_size (desc) / (1.*PANGO_SCALE);
gravity = pango_font_description_get_gravity (desc);
@@ -208,7 +350,7 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont)
PangoContext *context;
PangoFontMap *fontmap;
- fontmap = pango_font_get_font_map ((PangoFont *)cfont);
+ fontmap = pango_font_get_font_map ((PangoFont *)cf_priv->cfont);
/* we inherit most font properties for the mini font. just
* change family and size. means, you get bold hex digits
@@ -233,8 +375,7 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont)
}
}
- pango_font_description_set_absolute_size (desc, mini_size * PANGO_SCALE);
-
+ pango_font_description_set_absolute_size (desc, pango_units_from_double (mini_size));
/* load mini_font */
@@ -251,8 +392,7 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont)
cairo_font_options_destroy (font_options);
- mini_cfont = (PangoCairoFont *) mini_font;
- scaled_mini_font = _pango_cairo_font_get_scaled_font (mini_cfont);
+ scaled_mini_font = _pango_cairo_font_get_scaled_font (mini_font);
for (i = 0 ; i < 16 ; i++)
{
@@ -274,8 +414,8 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont)
pad = (font_extents.ascent + font_extents.descent) / 43;
pad = MIN (pad, mini_size);
- hbi = g_slice_new (PangoCairoHexBoxInfo);
- hbi->font = mini_font;
+ hbi = g_slice_new (PangoCairoFontHexBoxInfo);
+ hbi->font = (PangoCairoFont *) mini_font;
hbi->rows = rows;
hbi->digit_width = width;
@@ -314,21 +454,114 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont)
hbi->box_descent = HINT_Y (hbi->box_descent);
}
- g_object_set_data_full (G_OBJECT (cfont), "hex_box_info", hbi, (GDestroyNotify)_pango_cairo_hex_box_info_destroy);
-
return hbi;
}
+static void
+_pango_cairo_font_hex_box_info_destroy (PangoCairoFontHexBoxInfo *hbi)
+{
+ if (hbi)
+ {
+ g_object_unref (hbi->font);
+ g_slice_free (PangoCairoFontHexBoxInfo, hbi);
+ }
+}
+
+PangoCairoFontHexBoxInfo *
+_pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont)
+{
+ PangoCairoFontPrivate *cf_priv = PANGO_CAIRO_FONT_PRIVATE (cfont);
+
+ return _pango_cairo_font_private_get_hex_box_info (cf_priv);
+}
+
+void
+_pango_cairo_font_private_initialize (PangoCairoFontPrivate *cf_priv,
+ PangoCairoFont *cfont,
+ PangoContext *context,
+ const PangoFontDescription *desc,
+ const cairo_matrix_t *font_matrix)
+{
+ const cairo_font_options_t *font_options;
+ cairo_matrix_t gravity_matrix;
+ const PangoMatrix *pango_ctm;
+
+ cf_priv->cfont = cfont;
+ cf_priv->gravity = pango_font_description_get_gravity (desc);
+
+ cf_priv->data = _pango_cairo_font_private_scaled_font_data_create ();
+
+ /* first apply gravity rotation, then font_matrix, such that
+ * vertical italic text comes out "correct". we don't do anything
+ * like baseline adjustment etc though. should be specially
+ * handled when we support italic correction. */
+ cairo_matrix_init_rotate(&gravity_matrix,
+ pango_gravity_to_rotation (cf_priv->gravity));
+ cairo_matrix_multiply (&cf_priv->data->font_matrix,
+ font_matrix,
+ &gravity_matrix);
+
+ pango_ctm = pango_context_get_matrix (context);
+ if (pango_ctm)
+ cairo_matrix_init (&cf_priv->data->ctm,
+ pango_ctm->xx,
+ pango_ctm->yx,
+ pango_ctm->xy,
+ pango_ctm->yy,
+ 0., 0.);
+ else
+ cairo_matrix_init_identity (&cf_priv->data->ctm);
+
+ font_options = _pango_cairo_context_get_merged_font_options (context);
+ cf_priv->data->options = cairo_font_options_copy (font_options);
+ cf_priv->is_hinted = cairo_font_options_get_hint_metrics (font_options) != CAIRO_HINT_METRICS_OFF;
+
+ cf_priv->scaled_font = NULL;
+ cf_priv->hbi = NULL;
+ cf_priv->glyph_extents_cache = NULL;
+ cf_priv->metrics_by_lang = NULL;
+}
+
+static void
+free_metrics_info (PangoCairoFontMetricsInfo *info)
+{
+ pango_font_metrics_unref (info->metrics);
+ g_slice_free (PangoCairoFontMetricsInfo, info);
+}
+
void
-_pango_cairo_get_glyph_extents_missing (PangoCairoFont *cfont,
- PangoGlyph glyph,
- PangoRectangle *ink_rect,
- PangoRectangle *logical_rect)
+_pango_cairo_font_private_finalize (PangoCairoFontPrivate *cf_priv)
+{
+ _pango_cairo_font_private_scaled_font_data_destroy (cf_priv->data);
+
+ if (cf_priv->scaled_font)
+ cairo_scaled_font_destroy (cf_priv->scaled_font);
+
+ _pango_cairo_font_hex_box_info_destroy (cf_priv->hbi);
+
+ if (cf_priv->glyph_extents_cache)
+ g_free (cf_priv->glyph_extents_cache);
+
+ g_slist_foreach (cf_priv->metrics_by_lang, (GFunc)free_metrics_info, NULL);
+ g_slist_free (cf_priv->metrics_by_lang);
+}
+
+gboolean
+_pango_cairo_font_private_is_metrics_hinted (PangoCairoFontPrivate *cf_priv)
+{
+ return cf_priv->is_hinted;
+}
+
+static void
+_pango_cairo_font_private_get_glyph_extents_missing (PangoCairoFontPrivate *cf_priv,
+ PangoGlyph glyph,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
{
- PangoCairoHexBoxInfo *hbi;
+ PangoCairoFontHexBoxInfo *hbi;
gint rows, cols;
- hbi = _pango_cairo_font_get_hex_box_info (cfont);
+ hbi = _pango_cairo_font_private_get_hex_box_info (cf_priv);
if (!hbi)
{
pango_font_get_glyph_extents (NULL, glyph, ink_rect, logical_rect);
@@ -355,3 +588,142 @@ _pango_cairo_get_glyph_extents_missing (PangoCairoFont *cfont,
}
}
+#define GLYPH_CACHE_NUM_ENTRIES 256 /* should be power of two */
+#define GLYPH_CACHE_MASK (GLYPH_CACHE_NUM_ENTRIES - 1)
+/* An entry in the fixed-size cache for the glyph->extents mapping.
+ * The cache is indexed by the lower N bits of the glyph (see
+ * GLYPH_CACHE_NUM_ENTRIES). For scripts with few glyphs,
+ * this should provide pretty much instant lookups.
+ */
+struct _PangoCairoFontGlyphExtentsCacheEntry
+{
+ PangoGlyph glyph;
+ int width;
+ PangoRectangle ink_rect;
+};
+
+static gboolean
+_pango_cairo_font_private_glyph_extents_cache_init (PangoCairoFontPrivate *cf_priv)
+{
+ cairo_scaled_font_t *scaled_font = _pango_cairo_font_private_get_scaled_font (cf_priv);
+ cairo_font_extents_t font_extents;
+
+ if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
+ return FALSE;
+
+ cairo_scaled_font_extents (scaled_font, &font_extents);
+
+ cf_priv->font_extents.x = 0;
+ cf_priv->font_extents.width = 0;
+ cf_priv->font_extents.height = pango_units_from_double (font_extents.ascent + font_extents.descent);
+ switch (cf_priv->gravity)
+ {
+ default:
+ case PANGO_GRAVITY_AUTO:
+ case PANGO_GRAVITY_SOUTH:
+ cf_priv->font_extents.y = - pango_units_from_double (font_extents.ascent);
+ break;
+ case PANGO_GRAVITY_NORTH:
+ cf_priv->font_extents.y = - pango_units_from_double (font_extents.descent);
+ break;
+ case PANGO_GRAVITY_EAST:
+ case PANGO_GRAVITY_WEST:
+ {
+ int ascent = pango_units_from_double (font_extents.ascent + font_extents.descent) / 2;
+ if (cf_priv->is_hinted)
+ ascent = PANGO_UNITS_ROUND (ascent);
+ cf_priv->font_extents.y = - ascent;
+ }
+ }
+
+ cf_priv->glyph_extents_cache = g_new0 (PangoCairoFontGlyphExtentsCacheEntry, GLYPH_CACHE_NUM_ENTRIES);
+ /* Make sure all cache entries are invalid initially */
+ cf_priv->glyph_extents_cache[0].glyph = 1; /* glyph 1 cannot happen in bucket 0 */
+
+ return TRUE;
+}
+
+/* Fills in the glyph extents cache entry
+ */
+static void
+compute_glyph_extents (PangoCairoFontPrivate *cf_priv,
+ PangoGlyph glyph,
+ PangoCairoFontGlyphExtentsCacheEntry *entry)
+{
+ cairo_text_extents_t extents;
+ cairo_glyph_t cairo_glyph;
+
+ cairo_glyph.index = glyph;
+ cairo_glyph.x = 0;
+ cairo_glyph.y = 0;
+
+ cairo_scaled_font_glyph_extents (_pango_cairo_font_private_get_scaled_font (cf_priv),
+ &cairo_glyph, 1, &extents);
+
+ entry->glyph = glyph;
+ entry->width = pango_units_from_double (extents.x_advance);
+ entry->ink_rect.x = pango_units_from_double (extents.x_bearing);
+ entry->ink_rect.y = pango_units_from_double (extents.y_bearing);
+ entry->ink_rect.width = pango_units_from_double (extents.width);
+ entry->ink_rect.height = pango_units_from_double (extents.height);
+}
+
+static PangoCairoFontGlyphExtentsCacheEntry *
+_pango_cairo_font_private_get_glyph_extents_cache_entry (PangoCairoFontPrivate *cf_priv,
+ PangoGlyph glyph)
+{
+ PangoCairoFontGlyphExtentsCacheEntry *entry;
+ guint idx;
+
+ idx = glyph & GLYPH_CACHE_MASK;
+ entry = cf_priv->glyph_extents_cache + idx;
+
+ if (entry->glyph != glyph)
+ {
+ compute_glyph_extents (cf_priv, glyph, entry);
+ }
+
+ return entry;
+}
+
+void
+_pango_cairo_font_private_get_glyph_extents (PangoCairoFontPrivate *cf_priv,
+ PangoGlyph glyph,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ PangoCairoFontGlyphExtentsCacheEntry *entry;
+
+ if (!cf_priv ||
+ (cf_priv->glyph_extents_cache == NULL &&
+ !_pango_cairo_font_private_glyph_extents_cache_init (cf_priv)))
+ {
+ /* Get generic unknown-glyph extents. */
+ pango_font_get_glyph_extents (NULL, glyph, ink_rect, logical_rect);
+ return;
+ }
+
+ if (glyph == PANGO_GLYPH_EMPTY)
+ {
+ if (ink_rect)
+ ink_rect->x = ink_rect->y = ink_rect->width = ink_rect->height = 0;
+ if (logical_rect)
+ *logical_rect = cf_priv->font_extents;
+ return;
+ }
+ else if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
+ {
+ _pango_cairo_font_private_get_glyph_extents_missing(cf_priv, glyph, ink_rect, logical_rect);
+ return;
+ }
+
+ entry = _pango_cairo_font_private_get_glyph_extents_cache_entry (cf_priv, glyph);
+
+ if (ink_rect)
+ *ink_rect = entry->ink_rect;
+ if (logical_rect)
+ {
+ *logical_rect = cf_priv->font_extents;
+ logical_rect->width = entry->width;
+ }
+}