1 | /* Pango |
2 | * pango-context.c: Contexts for itemization and shaping |
3 | * |
4 | * Copyright (C) 2000, 2006 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 | #include <string.h> |
24 | #include <stdlib.h> |
25 | |
26 | #include "pango-context.h" |
27 | #include "pango-context-private.h" |
28 | #include "pango-impl-utils.h" |
29 | |
30 | #include "pango-font-private.h" |
31 | #include "pango-item-private.h" |
32 | #include "pango-fontset.h" |
33 | #include "pango-fontmap-private.h" |
34 | #include "pango-script-private.h" |
35 | #include "pango-emoji-private.h" |
36 | |
37 | /** |
38 | * PangoContext: |
39 | * |
40 | * A `PangoContext` stores global information used to control the |
41 | * itemization process. |
42 | * |
43 | * The information stored by `PangoContext` includes the fontmap used |
44 | * to look up fonts, and default values such as the default language, |
45 | * default gravity, or default font. |
46 | * |
47 | * To obtain a `PangoContext`, use [method@Pango.FontMap.create_context]. |
48 | */ |
49 | |
50 | struct _PangoContextClass |
51 | { |
52 | GObjectClass parent_class; |
53 | |
54 | }; |
55 | |
56 | static void pango_context_finalize (GObject *object); |
57 | static void context_changed (PangoContext *context); |
58 | |
59 | G_DEFINE_TYPE (PangoContext, pango_context, G_TYPE_OBJECT) |
60 | |
61 | static void |
62 | pango_context_init (PangoContext *context) |
63 | { |
64 | context->base_dir = PANGO_DIRECTION_WEAK_LTR; |
65 | context->resolved_gravity = context->base_gravity = PANGO_GRAVITY_SOUTH; |
66 | context->gravity_hint = PANGO_GRAVITY_HINT_NATURAL; |
67 | |
68 | context->serial = 1; |
69 | context->set_language = NULL; |
70 | context->language = pango_language_get_default (); |
71 | context->font_map = NULL; |
72 | context->round_glyph_positions = TRUE; |
73 | |
74 | context->font_desc = pango_font_description_new (); |
75 | pango_font_description_set_family_static (desc: context->font_desc, family: "serif" ); |
76 | pango_font_description_set_style (desc: context->font_desc, style: PANGO_STYLE_NORMAL); |
77 | pango_font_description_set_variant (desc: context->font_desc, variant: PANGO_VARIANT_NORMAL); |
78 | pango_font_description_set_weight (desc: context->font_desc, weight: PANGO_WEIGHT_NORMAL); |
79 | pango_font_description_set_stretch (desc: context->font_desc, stretch: PANGO_STRETCH_NORMAL); |
80 | pango_font_description_set_size (desc: context->font_desc, size: 12 * PANGO_SCALE); |
81 | } |
82 | |
83 | static void |
84 | pango_context_class_init (PangoContextClass *klass) |
85 | { |
86 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
87 | |
88 | object_class->finalize = pango_context_finalize; |
89 | } |
90 | |
91 | static void |
92 | pango_context_finalize (GObject *object) |
93 | { |
94 | PangoContext *context; |
95 | |
96 | context = PANGO_CONTEXT (object); |
97 | |
98 | if (context->font_map) |
99 | g_object_unref (object: context->font_map); |
100 | |
101 | pango_font_description_free (desc: context->font_desc); |
102 | if (context->matrix) |
103 | pango_matrix_free (matrix: context->matrix); |
104 | |
105 | if (context->metrics) |
106 | pango_font_metrics_unref (metrics: context->metrics); |
107 | |
108 | G_OBJECT_CLASS (pango_context_parent_class)->finalize (object); |
109 | } |
110 | |
111 | /** |
112 | * pango_context_new: |
113 | * |
114 | * Creates a new `PangoContext` initialized to default values. |
115 | * |
116 | * This function is not particularly useful as it should always |
117 | * be followed by a [method@Pango.Context.set_font_map] call, and the |
118 | * function [method@Pango.FontMap.create_context] does these two steps |
119 | * together and hence users are recommended to use that. |
120 | * |
121 | * If you are using Pango as part of a higher-level system, |
122 | * that system may have it's own way of create a `PangoContext`. |
123 | * For instance, the GTK toolkit has, among others, |
124 | * `gtk_widget_get_pango_context()`. Use those instead. |
125 | * |
126 | * Return value: the newly allocated `PangoContext`, which should |
127 | * be freed with g_object_unref(). |
128 | */ |
129 | PangoContext * |
130 | pango_context_new (void) |
131 | { |
132 | PangoContext *context; |
133 | |
134 | context = g_object_new (PANGO_TYPE_CONTEXT, NULL); |
135 | |
136 | return context; |
137 | } |
138 | |
139 | static void |
140 | update_resolved_gravity (PangoContext *context) |
141 | { |
142 | if (context->base_gravity == PANGO_GRAVITY_AUTO) |
143 | context->resolved_gravity = pango_gravity_get_for_matrix (matrix: context->matrix); |
144 | else |
145 | context->resolved_gravity = context->base_gravity; |
146 | } |
147 | |
148 | /** |
149 | * pango_context_set_matrix: |
150 | * @context: a `PangoContext` |
151 | * @matrix: (nullable): a `PangoMatrix`, or %NULL to unset any existing |
152 | * matrix. (No matrix set is the same as setting the identity matrix.) |
153 | * |
154 | * Sets the transformation matrix that will be applied when rendering |
155 | * with this context. |
156 | * |
157 | * Note that reported metrics are in the user space coordinates before |
158 | * the application of the matrix, not device-space coordinates after the |
159 | * application of the matrix. So, they don't scale with the matrix, though |
160 | * they may change slightly for different matrices, depending on how the |
161 | * text is fit to the pixel grid. |
162 | * |
163 | * Since: 1.6 |
164 | */ |
165 | void |
166 | pango_context_set_matrix (PangoContext *context, |
167 | const PangoMatrix *matrix) |
168 | { |
169 | g_return_if_fail (PANGO_IS_CONTEXT (context)); |
170 | |
171 | if (context->matrix || matrix) |
172 | context_changed (context); |
173 | |
174 | if (context->matrix) |
175 | pango_matrix_free (matrix: context->matrix); |
176 | if (matrix) |
177 | context->matrix = pango_matrix_copy (matrix); |
178 | else |
179 | context->matrix = NULL; |
180 | |
181 | update_resolved_gravity (context); |
182 | } |
183 | |
184 | /** |
185 | * pango_context_get_matrix: |
186 | * @context: a `PangoContext` |
187 | * |
188 | * Gets the transformation matrix that will be applied when |
189 | * rendering with this context. |
190 | * |
191 | * See [method@Pango.Context.set_matrix]. |
192 | * |
193 | * Return value: (nullable): the matrix, or %NULL if no matrix has |
194 | * been set (which is the same as the identity matrix). The returned |
195 | * matrix is owned by Pango and must not be modified or freed. |
196 | * |
197 | * Since: 1.6 |
198 | */ |
199 | const PangoMatrix * |
200 | pango_context_get_matrix (PangoContext *context) |
201 | { |
202 | g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL); |
203 | |
204 | return context->matrix; |
205 | } |
206 | |
207 | /** |
208 | * pango_context_set_font_map: |
209 | * @context: a `PangoContext` |
210 | * @font_map: the `PangoFontMap` to set. |
211 | * |
212 | * Sets the font map to be searched when fonts are looked-up |
213 | * in this context. |
214 | * |
215 | * This is only for internal use by Pango backends, a `PangoContext` |
216 | * obtained via one of the recommended methods should already have a |
217 | * suitable font map. |
218 | */ |
219 | void |
220 | pango_context_set_font_map (PangoContext *context, |
221 | PangoFontMap *font_map) |
222 | { |
223 | g_return_if_fail (PANGO_IS_CONTEXT (context)); |
224 | g_return_if_fail (!font_map || PANGO_IS_FONT_MAP (font_map)); |
225 | |
226 | if (font_map == context->font_map) |
227 | return; |
228 | |
229 | context_changed (context); |
230 | |
231 | if (font_map) |
232 | g_object_ref (font_map); |
233 | |
234 | if (context->font_map) |
235 | g_object_unref (object: context->font_map); |
236 | |
237 | context->font_map = font_map; |
238 | context->fontmap_serial = pango_font_map_get_serial (fontmap: font_map); |
239 | } |
240 | |
241 | /** |
242 | * pango_context_get_font_map: |
243 | * @context: a `PangoContext` |
244 | * |
245 | * Gets the `PangoFontMap` used to look up fonts for this context. |
246 | * |
247 | * Return value: (transfer none): the font map for the `PangoContext`. |
248 | * This value is owned by Pango and should not be unreferenced. |
249 | * |
250 | * Since: 1.6 |
251 | */ |
252 | PangoFontMap * |
253 | pango_context_get_font_map (PangoContext *context) |
254 | { |
255 | g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL); |
256 | |
257 | return context->font_map; |
258 | } |
259 | |
260 | /** |
261 | * pango_context_list_families: |
262 | * @context: a `PangoContext` |
263 | * @families: (out) (array length=n_families) (transfer container): location |
264 | * to store a pointer to an array of `PangoFontFamily`. This array should |
265 | * be freed with g_free(). |
266 | * @n_families: (out): location to store the number of elements in @descs |
267 | * |
268 | * List all families for a context. |
269 | */ |
270 | void |
271 | pango_context_list_families (PangoContext *context, |
272 | PangoFontFamily ***families, |
273 | int *n_families) |
274 | { |
275 | g_return_if_fail (context != NULL); |
276 | g_return_if_fail (families == NULL || n_families != NULL); |
277 | |
278 | if (n_families == NULL) |
279 | return; |
280 | |
281 | if (context->font_map == NULL) |
282 | { |
283 | *n_families = 0; |
284 | if (families) |
285 | *families = NULL; |
286 | |
287 | return; |
288 | } |
289 | else |
290 | pango_font_map_list_families (fontmap: context->font_map, families, n_families); |
291 | } |
292 | |
293 | /** |
294 | * pango_context_load_font: |
295 | * @context: a `PangoContext` |
296 | * @desc: a `PangoFontDescription` describing the font to load |
297 | * |
298 | * Loads the font in one of the fontmaps in the context |
299 | * that is the closest match for @desc. |
300 | * |
301 | * Returns: (transfer full) (nullable): the newly allocated `PangoFont` |
302 | * that was loaded, or %NULL if no font matched. |
303 | */ |
304 | PangoFont * |
305 | pango_context_load_font (PangoContext *context, |
306 | const PangoFontDescription *desc) |
307 | { |
308 | g_return_val_if_fail (context != NULL, NULL); |
309 | g_return_val_if_fail (context->font_map != NULL, NULL); |
310 | |
311 | return pango_font_map_load_font (fontmap: context->font_map, context, desc); |
312 | } |
313 | |
314 | /** |
315 | * pango_context_load_fontset: |
316 | * @context: a `PangoContext` |
317 | * @desc: a `PangoFontDescription` describing the fonts to load |
318 | * @language: a `PangoLanguage` the fonts will be used for |
319 | * |
320 | * Load a set of fonts in the context that can be used to render |
321 | * a font matching @desc. |
322 | * |
323 | * Returns: (transfer full) (nullable): the newly allocated |
324 | * `PangoFontset` loaded, or %NULL if no font matched. |
325 | */ |
326 | PangoFontset * |
327 | pango_context_load_fontset (PangoContext *context, |
328 | const PangoFontDescription *desc, |
329 | PangoLanguage *language) |
330 | { |
331 | g_return_val_if_fail (context != NULL, NULL); |
332 | |
333 | return pango_font_map_load_fontset (fontmap: context->font_map, context, desc, language); |
334 | } |
335 | |
336 | /** |
337 | * pango_context_set_font_description: |
338 | * @context: a `PangoContext` |
339 | * @desc: the new pango font description |
340 | * |
341 | * Set the default font description for the context |
342 | */ |
343 | void |
344 | pango_context_set_font_description (PangoContext *context, |
345 | const PangoFontDescription *desc) |
346 | { |
347 | g_return_if_fail (context != NULL); |
348 | g_return_if_fail (desc != NULL); |
349 | |
350 | if (desc != context->font_desc && |
351 | (!desc || !context->font_desc || !pango_font_description_equal(desc1: desc, desc2: context->font_desc))) |
352 | { |
353 | context_changed (context); |
354 | |
355 | pango_font_description_free (desc: context->font_desc); |
356 | context->font_desc = pango_font_description_copy (desc); |
357 | } |
358 | } |
359 | |
360 | /** |
361 | * pango_context_get_font_description: |
362 | * @context: a `PangoContext` |
363 | * |
364 | * Retrieve the default font description for the context. |
365 | * |
366 | * Return value: (transfer none): a pointer to the context's default font |
367 | * description. This value must not be modified or freed. |
368 | */ |
369 | PangoFontDescription * |
370 | pango_context_get_font_description (PangoContext *context) |
371 | { |
372 | g_return_val_if_fail (context != NULL, NULL); |
373 | |
374 | return context->font_desc; |
375 | } |
376 | |
377 | /** |
378 | * pango_context_set_language: |
379 | * @context: a `PangoContext` |
380 | * @language: the new language tag. |
381 | * |
382 | * Sets the global language tag for the context. |
383 | * |
384 | * The default language for the locale of the running process |
385 | * can be found using [func@Pango.Language.get_default]. |
386 | */ |
387 | void |
388 | pango_context_set_language (PangoContext *context, |
389 | PangoLanguage *language) |
390 | { |
391 | g_return_if_fail (context != NULL); |
392 | |
393 | if (language != context->language) |
394 | context_changed (context); |
395 | |
396 | context->set_language = language; |
397 | if (language) |
398 | context->language = language; |
399 | else |
400 | context->language = pango_language_get_default (); |
401 | } |
402 | |
403 | /** |
404 | * pango_context_get_language: |
405 | * @context: a `PangoContext` |
406 | * |
407 | * Retrieves the global language tag for the context. |
408 | * |
409 | * Return value: the global language tag. |
410 | */ |
411 | PangoLanguage * |
412 | pango_context_get_language (PangoContext *context) |
413 | { |
414 | g_return_val_if_fail (context != NULL, NULL); |
415 | |
416 | return context->set_language; |
417 | } |
418 | |
419 | /** |
420 | * pango_context_set_base_dir: |
421 | * @context: a `PangoContext` |
422 | * @direction: the new base direction |
423 | * |
424 | * Sets the base direction for the context. |
425 | * |
426 | * The base direction is used in applying the Unicode bidirectional |
427 | * algorithm; if the @direction is %PANGO_DIRECTION_LTR or |
428 | * %PANGO_DIRECTION_RTL, then the value will be used as the paragraph |
429 | * direction in the Unicode bidirectional algorithm. A value of |
430 | * %PANGO_DIRECTION_WEAK_LTR or %PANGO_DIRECTION_WEAK_RTL is used only |
431 | * for paragraphs that do not contain any strong characters themselves. |
432 | */ |
433 | void |
434 | pango_context_set_base_dir (PangoContext *context, |
435 | PangoDirection direction) |
436 | { |
437 | g_return_if_fail (context != NULL); |
438 | |
439 | if (direction != context->base_dir) |
440 | context_changed (context); |
441 | |
442 | context->base_dir = direction; |
443 | } |
444 | |
445 | /** |
446 | * pango_context_get_base_dir: |
447 | * @context: a `PangoContext` |
448 | * |
449 | * Retrieves the base direction for the context. |
450 | * |
451 | * See [method@Pango.Context.set_base_dir]. |
452 | * |
453 | * Return value: the base direction for the context. |
454 | */ |
455 | PangoDirection |
456 | pango_context_get_base_dir (PangoContext *context) |
457 | { |
458 | g_return_val_if_fail (context != NULL, PANGO_DIRECTION_LTR); |
459 | |
460 | return context->base_dir; |
461 | } |
462 | |
463 | /** |
464 | * pango_context_set_base_gravity: |
465 | * @context: a `PangoContext` |
466 | * @gravity: the new base gravity |
467 | * |
468 | * Sets the base gravity for the context. |
469 | * |
470 | * The base gravity is used in laying vertical text out. |
471 | * |
472 | * Since: 1.16 |
473 | */ |
474 | void |
475 | pango_context_set_base_gravity (PangoContext *context, |
476 | PangoGravity gravity) |
477 | { |
478 | g_return_if_fail (context != NULL); |
479 | |
480 | if (gravity != context->base_gravity) |
481 | context_changed (context); |
482 | |
483 | context->base_gravity = gravity; |
484 | |
485 | update_resolved_gravity (context); |
486 | } |
487 | |
488 | /** |
489 | * pango_context_get_base_gravity: |
490 | * @context: a `PangoContext` |
491 | * |
492 | * Retrieves the base gravity for the context. |
493 | * |
494 | * See [method@Pango.Context.set_base_gravity]. |
495 | * |
496 | * Return value: the base gravity for the context. |
497 | * |
498 | * Since: 1.16 |
499 | */ |
500 | PangoGravity |
501 | pango_context_get_base_gravity (PangoContext *context) |
502 | { |
503 | g_return_val_if_fail (context != NULL, PANGO_GRAVITY_SOUTH); |
504 | |
505 | return context->base_gravity; |
506 | } |
507 | |
508 | /** |
509 | * pango_context_get_gravity: |
510 | * @context: a `PangoContext` |
511 | * |
512 | * Retrieves the gravity for the context. |
513 | * |
514 | * This is similar to [method@Pango.Context.get_base_gravity], |
515 | * except for when the base gravity is %PANGO_GRAVITY_AUTO for |
516 | * which [func@Pango.Gravity.get_for_matrix] is used to return the |
517 | * gravity from the current context matrix. |
518 | * |
519 | * Return value: the resolved gravity for the context. |
520 | * |
521 | * Since: 1.16 |
522 | */ |
523 | PangoGravity |
524 | pango_context_get_gravity (PangoContext *context) |
525 | { |
526 | g_return_val_if_fail (context != NULL, PANGO_GRAVITY_SOUTH); |
527 | |
528 | return context->resolved_gravity; |
529 | } |
530 | |
531 | /** |
532 | * pango_context_set_gravity_hint: |
533 | * @context: a `PangoContext` |
534 | * @hint: the new gravity hint |
535 | * |
536 | * Sets the gravity hint for the context. |
537 | * |
538 | * The gravity hint is used in laying vertical text out, and |
539 | * is only relevant if gravity of the context as returned by |
540 | * [method@Pango.Context.get_gravity] is set to %PANGO_GRAVITY_EAST |
541 | * or %PANGO_GRAVITY_WEST. |
542 | * |
543 | * Since: 1.16 |
544 | */ |
545 | void |
546 | pango_context_set_gravity_hint (PangoContext *context, |
547 | PangoGravityHint hint) |
548 | { |
549 | g_return_if_fail (context != NULL); |
550 | |
551 | if (hint != context->gravity_hint) |
552 | context_changed (context); |
553 | |
554 | context->gravity_hint = hint; |
555 | } |
556 | |
557 | /** |
558 | * pango_context_get_gravity_hint: |
559 | * @context: a `PangoContext` |
560 | * |
561 | * Retrieves the gravity hint for the context. |
562 | * |
563 | * See [method@Pango.Context.set_gravity_hint] for details. |
564 | * |
565 | * Return value: the gravity hint for the context. |
566 | * |
567 | * Since: 1.16 |
568 | */ |
569 | PangoGravityHint |
570 | pango_context_get_gravity_hint (PangoContext *context) |
571 | { |
572 | g_return_val_if_fail (context != NULL, PANGO_GRAVITY_HINT_NATURAL); |
573 | |
574 | return context->gravity_hint; |
575 | } |
576 | |
577 | |
578 | static gboolean |
579 | get_first_metrics_foreach (PangoFontset *fontset, |
580 | PangoFont *font, |
581 | gpointer data) |
582 | { |
583 | PangoFontMetrics *fontset_metrics = data; |
584 | PangoLanguage *language = PANGO_FONTSET_GET_CLASS (fontset)->get_language (fontset); |
585 | PangoFontMetrics *font_metrics = pango_font_get_metrics (font, language); |
586 | guint save_ref_count; |
587 | |
588 | /* Initialize the fontset metrics to metrics of the first font in the |
589 | * fontset; saving the refcount and restoring it is a bit of hack but avoids |
590 | * having to update this code for each metrics addition. |
591 | */ |
592 | save_ref_count = fontset_metrics->ref_count; |
593 | *fontset_metrics = *font_metrics; |
594 | fontset_metrics->ref_count = save_ref_count; |
595 | |
596 | pango_font_metrics_unref (metrics: font_metrics); |
597 | |
598 | return TRUE; /* Stops iteration */ |
599 | } |
600 | |
601 | static PangoFontMetrics * |
602 | get_base_metrics (PangoFontset *fontset) |
603 | { |
604 | PangoFontMetrics *metrics = pango_font_metrics_new (); |
605 | |
606 | /* Initialize the metrics from the first font in the fontset */ |
607 | pango_fontset_foreach (fontset, func: get_first_metrics_foreach, data: metrics); |
608 | |
609 | return metrics; |
610 | } |
611 | |
612 | static void |
613 | update_metrics_from_items (PangoFontMetrics *metrics, |
614 | PangoLanguage *language, |
615 | const char *text, |
616 | unsigned int text_len, |
617 | GList *items) |
618 | |
619 | { |
620 | GHashTable *fonts_seen = g_hash_table_new (NULL, NULL); |
621 | PangoGlyphString *glyphs = pango_glyph_string_new (); |
622 | GList *l; |
623 | glong text_width; |
624 | |
625 | /* This should typically be called with a sample text string. */ |
626 | g_return_if_fail (text_len > 0); |
627 | |
628 | metrics->approximate_char_width = 0; |
629 | |
630 | for (l = items; l; l = l->next) |
631 | { |
632 | PangoItem *item = l->data; |
633 | PangoFont *font = item->analysis.font; |
634 | |
635 | if (font != NULL && g_hash_table_lookup (hash_table: fonts_seen, key: font) == NULL) |
636 | { |
637 | PangoFontMetrics *raw_metrics = pango_font_get_metrics (font, language); |
638 | g_hash_table_insert (hash_table: fonts_seen, key: font, value: font); |
639 | |
640 | /* metrics will already be initialized from the first font in the fontset */ |
641 | metrics->ascent = MAX (metrics->ascent, raw_metrics->ascent); |
642 | metrics->descent = MAX (metrics->descent, raw_metrics->descent); |
643 | metrics->height = MAX (metrics->height, raw_metrics->height); |
644 | pango_font_metrics_unref (metrics: raw_metrics); |
645 | } |
646 | |
647 | pango_shape_full (item_text: text + item->offset, item_length: item->length, |
648 | paragraph_text: text, paragraph_length: text_len, |
649 | analysis: &item->analysis, glyphs); |
650 | metrics->approximate_char_width += pango_glyph_string_get_width (glyphs); |
651 | } |
652 | |
653 | pango_glyph_string_free (string: glyphs); |
654 | g_hash_table_destroy (hash_table: fonts_seen); |
655 | |
656 | text_width = pango_utf8_strwidth (p: text); |
657 | g_assert (text_width > 0); |
658 | metrics->approximate_char_width /= text_width; |
659 | } |
660 | |
661 | /** |
662 | * pango_context_get_metrics: |
663 | * @context: a `PangoContext` |
664 | * @desc: (nullable): a `PangoFontDescription` structure. %NULL means that the |
665 | * font description from the context will be used. |
666 | * @language: (nullable): language tag used to determine which script to get |
667 | * the metrics for. %NULL means that the language tag from the context |
668 | * will be used. If no language tag is set on the context, metrics |
669 | * for the default language (as determined by [func@Pango.Language.get_default] |
670 | * will be returned. |
671 | * |
672 | * Get overall metric information for a particular font description. |
673 | * |
674 | * Since the metrics may be substantially different for different scripts, |
675 | * a language tag can be provided to indicate that the metrics should be |
676 | * retrieved that correspond to the script(s) used by that language. |
677 | * |
678 | * The `PangoFontDescription` is interpreted in the same way as by [func@itemize], |
679 | * and the family name may be a comma separated list of names. If characters |
680 | * from multiple of these families would be used to render the string, then |
681 | * the returned fonts would be a composite of the metrics for the fonts loaded |
682 | * for the individual families. |
683 | * |
684 | * Return value: a `PangoFontMetrics` object. The caller must call |
685 | * [method@Pango.FontMetrics.unref] when finished using the object. |
686 | */ |
687 | PangoFontMetrics * |
688 | pango_context_get_metrics (PangoContext *context, |
689 | const PangoFontDescription *desc, |
690 | PangoLanguage *language) |
691 | { |
692 | PangoFontset *current_fonts = NULL; |
693 | PangoFontMetrics *metrics; |
694 | const char *sample_str; |
695 | unsigned int text_len; |
696 | GList *items; |
697 | |
698 | g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL); |
699 | |
700 | if (!desc) |
701 | desc = context->font_desc; |
702 | |
703 | if (!language) |
704 | language = context->language; |
705 | |
706 | if (desc == context->font_desc && |
707 | language == context->language && |
708 | context->metrics != NULL) |
709 | return pango_font_metrics_ref (metrics: context->metrics); |
710 | |
711 | current_fonts = pango_font_map_load_fontset (fontmap: context->font_map, context, desc, language); |
712 | metrics = get_base_metrics (fontset: current_fonts); |
713 | |
714 | sample_str = pango_language_get_sample_string (language); |
715 | text_len = strlen (s: sample_str); |
716 | items = pango_itemize_with_font (context, base_dir: context->base_dir, |
717 | text: sample_str, start_index: 0, length: text_len, |
718 | NULL, NULL, |
719 | desc); |
720 | |
721 | update_metrics_from_items (metrics, language, text: sample_str, text_len, items); |
722 | |
723 | g_list_foreach (list: items, func: (GFunc)pango_item_free, NULL); |
724 | g_list_free (list: items); |
725 | |
726 | g_object_unref (object: current_fonts); |
727 | |
728 | if (desc == context->font_desc && |
729 | language == context->language) |
730 | context->metrics = pango_font_metrics_ref (metrics); |
731 | |
732 | return metrics; |
733 | } |
734 | |
735 | static void |
736 | context_changed (PangoContext *context) |
737 | { |
738 | context->serial++; |
739 | if (context->serial == 0) |
740 | context->serial++; |
741 | |
742 | g_clear_pointer (&context->metrics, pango_font_metrics_unref); |
743 | } |
744 | |
745 | /** |
746 | * pango_context_changed: |
747 | * @context: a `PangoContext` |
748 | * |
749 | * Forces a change in the context, which will cause any `PangoLayout` |
750 | * using this context to re-layout. |
751 | * |
752 | * This function is only useful when implementing a new backend |
753 | * for Pango, something applications won't do. Backends should |
754 | * call this function if they have attached extra data to the context |
755 | * and such data is changed. |
756 | * |
757 | * Since: 1.32.4 |
758 | **/ |
759 | void |
760 | pango_context_changed (PangoContext *context) |
761 | { |
762 | context_changed (context); |
763 | } |
764 | |
765 | static void |
766 | check_fontmap_changed (PangoContext *context) |
767 | { |
768 | guint old_serial = context->fontmap_serial; |
769 | |
770 | if (!context->font_map) |
771 | return; |
772 | |
773 | context->fontmap_serial = pango_font_map_get_serial (fontmap: context->font_map); |
774 | |
775 | if (old_serial != context->fontmap_serial) |
776 | context_changed (context); |
777 | } |
778 | |
779 | /** |
780 | * pango_context_get_serial: |
781 | * @context: a `PangoContext` |
782 | * |
783 | * Returns the current serial number of @context. |
784 | * |
785 | * The serial number is initialized to an small number larger than zero |
786 | * when a new context is created and is increased whenever the context |
787 | * is changed using any of the setter functions, or the `PangoFontMap` it |
788 | * uses to find fonts has changed. The serial may wrap, but will never |
789 | * have the value 0. Since it can wrap, never compare it with "less than", |
790 | * always use "not equals". |
791 | * |
792 | * This can be used to automatically detect changes to a `PangoContext`, |
793 | * and is only useful when implementing objects that need update when their |
794 | * `PangoContext` changes, like `PangoLayout`. |
795 | * |
796 | * Return value: The current serial number of @context. |
797 | * |
798 | * Since: 1.32.4 |
799 | */ |
800 | guint |
801 | pango_context_get_serial (PangoContext *context) |
802 | { |
803 | check_fontmap_changed (context); |
804 | return context->serial; |
805 | } |
806 | |
807 | /** |
808 | * pango_context_set_round_glyph_positions: |
809 | * @context: a `PangoContext` |
810 | * @round_positions: whether to round glyph positions |
811 | * |
812 | * Sets whether font rendering with this context should |
813 | * round glyph positions and widths to integral positions, |
814 | * in device units. |
815 | * |
816 | * This is useful when the renderer can't handle subpixel |
817 | * positioning of glyphs. |
818 | * |
819 | * The default value is to round glyph positions, to remain |
820 | * compatible with previous Pango behavior. |
821 | * |
822 | * Since: 1.44 |
823 | */ |
824 | void |
825 | pango_context_set_round_glyph_positions (PangoContext *context, |
826 | gboolean round_positions) |
827 | { |
828 | if (context->round_glyph_positions != round_positions) |
829 | { |
830 | context->round_glyph_positions = round_positions; |
831 | context_changed (context); |
832 | } |
833 | } |
834 | |
835 | /** |
836 | * pango_context_get_round_glyph_positions: |
837 | * @context: a `PangoContext` |
838 | * |
839 | * Returns whether font rendering with this context should |
840 | * round glyph positions and widths. |
841 | * |
842 | * Since: 1.44 |
843 | */ |
844 | gboolean |
845 | pango_context_get_round_glyph_positions (PangoContext *context) |
846 | { |
847 | return context->round_glyph_positions; |
848 | } |
849 | |