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