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
38typedef PangoCairoFontIface PangoCairoFontInterface;
39G_DEFINE_INTERFACE (PangoCairoFont, pango_cairo_font, PANGO_TYPE_FONT)
40
41static void
42pango_cairo_font_default_init (PangoCairoFontIface *iface)
43{
44}
45
46
47static PangoCairoFontPrivateScaledFontData *
48_pango_cairo_font_private_scaled_font_data_create (void)
49{
50 return g_slice_new (PangoCairoFontPrivateScaledFontData);
51}
52
53static void
54_pango_cairo_font_private_scaled_font_data_destroy (PangoCairoFontPrivateScaledFontData *data)
55{
56 if (data)
57 {
58 cairo_font_options_destroy (options: data->options);
59 g_slice_free (PangoCairoFontPrivateScaledFontData, data);
60 }
61}
62
63cairo_scaled_font_t *
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 font_matrix: &cf_priv->data->font_matrix,
85 ctm: &cf_priv->data->ctm,
86 options: cf_priv->data->options);
87
88 cairo_font_face_destroy (font_face);
89
90done:
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 (string: "pangocairo-scaledfont-warned");
99
100 if (!g_object_get_qdata (G_OBJECT (font), quark: 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 (mem: s);
126
127 g_object_set_qdata_full (G_OBJECT (font), quark: warned_quark,
128 GINT_TO_POINTER (1), NULL);
129 }
130 }
131
132 _pango_cairo_font_private_scaled_font_data_destroy (data: 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 */
151cairo_scaled_font_t *
152pango_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 */
174gboolean
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 (cfont: (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
189static int
190max_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
213typedef struct _PangoCairoFontMetricsInfo
214{
215 const char *sample_str;
216 PangoFontMetrics *metrics;
217} PangoCairoFontMetricsInfo;
218
219PangoFontMetrics *
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 (list: cf_priv->metrics_by_lang, data: 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, options: font_options);
277 pango_cairo_context_set_font_options (context, options: font_options);
278 cairo_font_options_destroy (options: 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, ctm: &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 (matrix: &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, matrix: &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, text: sample_str, length: -1);
322 pango_layout_get_extents (layout, NULL, logical_rect: &extents);
323
324 sample_str_width = pango_utf8_strwidth (p: 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, text: "0123456789", length: -1);
329 info->metrics->approximate_digit_width = max_glyph_width (layout);
330
331 g_object_unref (object: 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 (object: context);
364 g_object_unref (object: fontmap);
365 }
366
367 return pango_font_metrics_ref (metrics: info->metrics);
368}
369
370static PangoCairoFontHexBoxInfo *
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 (font: (PangoFont *)cf_priv->cfont);
410 /*gravity = pango_font_description_get_gravity (desc);*/
411
412 cairo_scaled_font_get_ctm (scaled_font, ctm: &cairo_ctm);
413 cairo_scaled_font_get_font_matrix (scaled_font, font_matrix: &cairo_font_matrix);
414 cairo_scaled_font_get_font_options (scaled_font, options: 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 (matrix: &pango_font_matrix) /
434 pango_matrix_get_font_scale_factor (matrix: &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 (matrix: &cairo_ctm, dx: &x, dy: &y);
443 scale_x = sqrt (x: x*x + y*y);
444 scale_x_inv = 1 / scale_x;
445
446 x = 0.; y = 1.;
447 cairo_matrix_transform_distance (matrix: &cairo_ctm, dx: &x, dy: &y);
448 scale_y = sqrt (x: 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 (font: (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, to_unset: PANGO_FONT_MASK_GRAVITY);
475
476 pango_font_description_set_family_static (desc, family: "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, size: pango_units_from_double (d: mini_size));
492
493 /* load mini_font */
494
495 context = pango_font_map_create_context (fontmap);
496
497 pango_context_set_matrix (context, matrix: &pango_ctm);
498 pango_context_set_language (context, language: pango_script_get_sample_language (script: PANGO_SCRIPT_LATIN));
499 pango_cairo_context_set_font_options (context, options: font_options);
500 mini_font = pango_font_map_load_font (fontmap, context, desc);
501
502 g_object_unref (object: context);
503 g_object_unref (object: fontmap);
504 }
505
506 pango_font_description_free (desc);
507 cairo_font_options_destroy (options: font_options);
508
509
510 scaled_mini_font = pango_cairo_font_get_scaled_font (cfont: (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_font: scaled_mini_font, utf8: c, extents: &extents);
520 width = MAX (width, extents.width);
521 height = MAX (height, extents.height);
522 }
523
524 cairo_scaled_font_extents (scaled_font, extents: &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
578static void
579_pango_cairo_font_hex_box_info_destroy (PangoCairoFontHexBoxInfo *hbi)
580{
581 if (hbi)
582 {
583 g_object_unref (object: hbi->font);
584 g_slice_free (PangoCairoFontHexBoxInfo, hbi);
585 }
586}
587
588PangoCairoFontHexBoxInfo *
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
596void
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(matrix: &gravity_matrix,
616 radians: pango_gravity_to_rotation (gravity: cf_priv->gravity));
617 cairo_matrix_multiply (result: &cf_priv->data->font_matrix,
618 a: font_matrix,
619 b: &gravity_matrix);
620
621 if (pango_ctm)
622 cairo_matrix_init (matrix: &cf_priv->data->ctm,
623 xx: pango_ctm->xx,
624 yx: pango_ctm->yx,
625 xy: pango_ctm->xy,
626 yy: pango_ctm->yy,
627 x0: 0., y0: 0.);
628 else
629 cairo_matrix_init_identity (matrix: &cf_priv->data->ctm);
630
631 cf_priv->data->options = cairo_font_options_copy (original: font_options);
632 cf_priv->is_hinted = cairo_font_options_get_hint_metrics (options: 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
640static void
641free_metrics_info (PangoCairoFontMetricsInfo *info)
642{
643 pango_font_metrics_unref (metrics: info->metrics);
644 g_slice_free (PangoCairoFontMetricsInfo, info);
645}
646
647void
648_pango_cairo_font_private_finalize (PangoCairoFontPrivate *cf_priv)
649{
650 _pango_cairo_font_private_scaled_font_data_destroy (data: cf_priv->data);
651
652 if (cf_priv->scaled_font)
653 cairo_scaled_font_destroy (scaled_font: cf_priv->scaled_font);
654 cf_priv->scaled_font = NULL;
655
656 _pango_cairo_font_hex_box_info_destroy (hbi: cf_priv->hbi);
657 cf_priv->hbi = NULL;
658
659 if (cf_priv->glyph_extents_cache)
660 g_free (mem: cf_priv->glyph_extents_cache);
661 cf_priv->glyph_extents_cache = NULL;
662
663 g_slist_foreach (list: cf_priv->metrics_by_lang, func: (GFunc)free_metrics_info, NULL);
664 g_slist_free (list: cf_priv->metrics_by_lang);
665 cf_priv->metrics_by_lang = NULL;
666}
667
668gboolean
669_pango_cairo_font_private_is_metrics_hinted (PangoCairoFontPrivate *cf_priv)
670{
671 return cf_priv->is_hinted;
672}
673
674
675static void
676get_space_extents (PangoCairoFontPrivate *cf_priv,
677 PangoRectangle *ink_rect,
678 PangoRectangle *logical_rect)
679{
680 /* See https://docs.microsoft.com/en-us/typography/develop/character-design-standards/whitespace */
681
682 int width = pango_font_get_absolute_size (PANGO_FONT (cf_priv->cfont)) / 4;
683
684 if (ink_rect)
685 {
686 ink_rect->x = ink_rect->y = ink_rect->height = 0;
687 ink_rect->width = width;
688 }
689 if (logical_rect)
690 {
691 *logical_rect = cf_priv->font_extents;
692 logical_rect->width = width;
693 }
694}
695
696static void
697_pango_cairo_font_private_get_glyph_extents_missing (PangoCairoFontPrivate *cf_priv,
698 PangoGlyph glyph,
699 PangoRectangle *ink_rect,
700 PangoRectangle *logical_rect)
701{
702 PangoCairoFontHexBoxInfo *hbi;
703 gunichar ch;
704 gint rows, cols;
705
706 ch = glyph & ~PANGO_GLYPH_UNKNOWN_FLAG;
707
708 if (ch == 0x20 || ch == 0x2423)
709 {
710 get_space_extents (cf_priv, ink_rect, logical_rect);
711 return;
712 }
713
714 hbi = _pango_cairo_font_private_get_hex_box_info (cf_priv);
715 if (!hbi)
716 {
717 pango_font_get_glyph_extents (NULL, glyph, ink_rect, logical_rect);
718 return;
719 }
720
721 if (G_UNLIKELY (glyph == PANGO_GLYPH_INVALID_INPUT || ch > 0x10FFFF))
722 {
723 rows = hbi->rows;
724 cols = 1;
725 }
726 else if (pango_get_ignorable_size (ch, rows: &rows, cols: &cols))
727 {
728 /* We special-case ignorables when rendering hex boxes */
729 }
730 else
731 {
732 rows = hbi->rows;
733 cols = ((glyph & ~PANGO_GLYPH_UNKNOWN_FLAG) > 0xffff ? 6 : 4) / rows;
734 }
735
736 if (ink_rect)
737 {
738 ink_rect->x = PANGO_SCALE * hbi->pad_x;
739 ink_rect->y = PANGO_SCALE * (hbi->box_descent - hbi->box_height);
740 ink_rect->width = PANGO_SCALE * (3 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x));
741 ink_rect->height = PANGO_SCALE * hbi->box_height;
742 }
743
744 if (logical_rect)
745 {
746 logical_rect->x = 0;
747 logical_rect->y = PANGO_SCALE * (hbi->box_descent - (hbi->box_height + hbi->pad_y));
748 logical_rect->width = PANGO_SCALE * (5 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x));
749 logical_rect->height = PANGO_SCALE * (hbi->box_height + 2 * hbi->pad_y);
750 }
751}
752
753#define GLYPH_CACHE_NUM_ENTRIES 256 /* should be power of two */
754#define GLYPH_CACHE_MASK (GLYPH_CACHE_NUM_ENTRIES - 1)
755/* An entry in the fixed-size cache for the glyph->extents mapping.
756 * The cache is indexed by the lower N bits of the glyph (see
757 * GLYPH_CACHE_NUM_ENTRIES). For scripts with few glyphs,
758 * this should provide pretty much instant lookups.
759 */
760struct _PangoCairoFontGlyphExtentsCacheEntry
761{
762 PangoGlyph glyph;
763 int width;
764 PangoRectangle ink_rect;
765};
766
767static gboolean
768_pango_cairo_font_private_glyph_extents_cache_init (PangoCairoFontPrivate *cf_priv)
769{
770 hb_font_extents_t extents;
771
772 hb_font_get_h_extents (font: pango_font_get_hb_font (PANGO_FONT (cf_priv->cfont)),
773 extents: &extents);
774
775 cf_priv->font_extents.x = 0;
776 cf_priv->font_extents.width = 0;
777 cf_priv->font_extents.height = extents.ascender - extents.descender;
778
779 switch (cf_priv->gravity)
780 {
781 default:
782 case PANGO_GRAVITY_AUTO:
783 case PANGO_GRAVITY_SOUTH:
784 cf_priv->font_extents.y = - extents.ascender;
785 break;
786 case PANGO_GRAVITY_NORTH:
787 cf_priv->font_extents.y = extents.descender;
788 break;
789 case PANGO_GRAVITY_EAST:
790 case PANGO_GRAVITY_WEST:
791 {
792 int ascent = cf_priv->font_extents.height / 2;
793 if (cf_priv->is_hinted)
794 ascent = PANGO_UNITS_ROUND (ascent);
795 cf_priv->font_extents.y = - ascent;
796 }
797 break;
798 }
799
800 if (cf_priv->is_hinted)
801 {
802 if (cf_priv->font_extents.y < 0)
803 cf_priv->font_extents.y = PANGO_UNITS_FLOOR (cf_priv->font_extents.y);
804 else
805 cf_priv->font_extents.y = PANGO_UNITS_CEIL (cf_priv->font_extents.y);
806 if (cf_priv->font_extents.height < 0)
807 cf_priv->font_extents.height = PANGO_UNITS_FLOOR (extents.ascender) - PANGO_UNITS_CEIL (extents.descender);
808 else
809 cf_priv->font_extents.height = PANGO_UNITS_CEIL (extents.ascender) - PANGO_UNITS_FLOOR (extents.descender);
810 }
811
812 if (PANGO_GRAVITY_IS_IMPROPER (cf_priv->gravity))
813 {
814 cf_priv->font_extents.y = - cf_priv->font_extents.y;
815 cf_priv->font_extents.height = - cf_priv->font_extents.height;
816 }
817
818 if (!cf_priv->glyph_extents_cache)
819 {
820 cf_priv->glyph_extents_cache = g_new0 (PangoCairoFontGlyphExtentsCacheEntry, GLYPH_CACHE_NUM_ENTRIES);
821 /* Make sure all cache entries are invalid initially */
822 cf_priv->glyph_extents_cache[0].glyph = 1; /* glyph 1 cannot happen in bucket 0 */
823 }
824
825 return TRUE;
826}
827
828
829/* Fills in the glyph extents cache entry
830 */
831static void
832compute_glyph_extents (PangoCairoFontPrivate *cf_priv,
833 PangoGlyph glyph,
834 PangoCairoFontGlyphExtentsCacheEntry *entry)
835{
836 cairo_text_extents_t extents;
837 cairo_glyph_t cairo_glyph;
838
839 cairo_glyph.index = glyph;
840 cairo_glyph.x = 0;
841 cairo_glyph.y = 0;
842
843 cairo_scaled_font_glyph_extents (scaled_font: _pango_cairo_font_private_get_scaled_font (cf_priv),
844 glyphs: &cairo_glyph, num_glyphs: 1, extents: &extents);
845
846 entry->glyph = glyph;
847 if (PANGO_GRAVITY_IS_VERTICAL (cf_priv->gravity))
848 entry->width = pango_units_from_double (d: extents.y_advance);
849 else
850 entry->width = pango_units_from_double (d: extents.x_advance);
851 entry->ink_rect.x = pango_units_from_double (d: extents.x_bearing);
852 entry->ink_rect.y = pango_units_from_double (d: extents.y_bearing);
853 entry->ink_rect.width = pango_units_from_double (d: extents.width);
854 entry->ink_rect.height = pango_units_from_double (d: extents.height);
855}
856
857static PangoCairoFontGlyphExtentsCacheEntry *
858_pango_cairo_font_private_get_glyph_extents_cache_entry (PangoCairoFontPrivate *cf_priv,
859 PangoGlyph glyph)
860{
861 PangoCairoFontGlyphExtentsCacheEntry *entry;
862 guint idx;
863
864 idx = glyph & GLYPH_CACHE_MASK;
865 entry = cf_priv->glyph_extents_cache + idx;
866
867 if (entry->glyph != glyph)
868 {
869 compute_glyph_extents (cf_priv, glyph, entry);
870 }
871
872 return entry;
873}
874
875void
876_pango_cairo_font_private_get_glyph_extents (PangoCairoFontPrivate *cf_priv,
877 PangoGlyph glyph,
878 PangoRectangle *ink_rect,
879 PangoRectangle *logical_rect)
880{
881 PangoCairoFontGlyphExtentsCacheEntry *entry;
882
883 if (!cf_priv ||
884 (cf_priv->glyph_extents_cache == NULL &&
885 !_pango_cairo_font_private_glyph_extents_cache_init (cf_priv)))
886 {
887 /* Get generic unknown-glyph extents. */
888 pango_font_get_glyph_extents (NULL, glyph, ink_rect, logical_rect);
889 return;
890 }
891
892 if (glyph == PANGO_GLYPH_EMPTY)
893 {
894 if (ink_rect)
895 ink_rect->x = ink_rect->y = ink_rect->width = ink_rect->height = 0;
896 if (logical_rect)
897 *logical_rect = cf_priv->font_extents;
898 return;
899 }
900 else if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
901 {
902 _pango_cairo_font_private_get_glyph_extents_missing (cf_priv, glyph, ink_rect, logical_rect);
903 return;
904 }
905
906 entry = _pango_cairo_font_private_get_glyph_extents_cache_entry (cf_priv, glyph);
907
908 if (ink_rect)
909 *ink_rect = entry->ink_rect;
910 if (logical_rect)
911 {
912 *logical_rect = cf_priv->font_extents;
913 switch (cf_priv->gravity)
914 {
915 case PANGO_GRAVITY_SOUTH:
916 logical_rect->width = entry->width;
917 break;
918 case PANGO_GRAVITY_EAST:
919 logical_rect->width = cf_priv->font_extents.height;
920 logical_rect->x = - logical_rect->width;
921 break;
922 case PANGO_GRAVITY_NORTH:
923 logical_rect->width = entry->width;
924 break;
925 case PANGO_GRAVITY_WEST:
926 logical_rect->width = - cf_priv->font_extents.height;
927 logical_rect->x = - logical_rect->width;
928 break;
929 case PANGO_GRAVITY_AUTO:
930 default:
931 g_assert_not_reached ();
932 }
933 }
934}
935

source code of gtk/subprojects/pango/pango/pangocairo-font.c