2 * pango-context.c: Contexts for itemization and shaping
4 * Copyright (C) 2000, 2006 Red Hat Software
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
26 #include "pango-context.h"
27 #include "pango-impl-utils.h"
29 #include "pango-engine.h"
30 #include "pango-engine-private.h"
31 #include "pango-modules.h"
32 #include "pango-script-private.h"
36 GObject parent_instance;
38 PangoLanguage *set_language;
39 PangoLanguage *language;
40 PangoDirection base_dir;
41 PangoGravity base_gravity;
42 PangoGravity resolved_gravity;
43 PangoGravityHint gravity_hint;
45 PangoFontDescription *font_desc;
49 PangoFontMap *font_map;
52 struct _PangoContextClass
54 GObjectClass parent_class;
58 static void pango_context_finalize (GObject *object);
60 G_DEFINE_TYPE (PangoContext, pango_context, G_TYPE_OBJECT)
63 pango_context_init (PangoContext *context)
65 context->base_dir = PANGO_DIRECTION_WEAK_LTR;
66 context->resolved_gravity = context->base_gravity = PANGO_GRAVITY_SOUTH;
67 context->gravity_hint = PANGO_GRAVITY_HINT_NATURAL;
69 context->set_language = NULL;
70 context->language = pango_language_get_default ();
71 context->font_map = NULL;
73 context->font_desc = pango_font_description_new ();
74 pango_font_description_set_family_static (context->font_desc, "serif");
75 pango_font_description_set_style (context->font_desc, PANGO_STYLE_NORMAL);
76 pango_font_description_set_variant (context->font_desc, PANGO_VARIANT_NORMAL);
77 pango_font_description_set_weight (context->font_desc, PANGO_WEIGHT_NORMAL);
78 pango_font_description_set_stretch (context->font_desc, PANGO_STRETCH_NORMAL);
79 pango_font_description_set_size (context->font_desc, 12 * PANGO_SCALE);
83 pango_context_class_init (PangoContextClass *klass)
85 GObjectClass *object_class = G_OBJECT_CLASS (klass);
87 object_class->finalize = pango_context_finalize;
91 pango_context_finalize (GObject *object)
93 PangoContext *context;
95 context = PANGO_CONTEXT (object);
97 if (context->font_map)
98 g_object_unref (context->font_map);
100 pango_font_description_free (context->font_desc);
102 pango_matrix_free (context->matrix);
104 G_OBJECT_CLASS (pango_context_parent_class)->finalize (object);
111 * Creates a new #PangoContext initialized to default values.
113 * This function is not particularly useful as it should always
114 * be followed by a pango_context_set_font_map() call, and the
115 * function pango_font_map_create_context() does these two steps
116 * together and hence users are recommended to use that.
118 * If you are using Pango as part of a higher-level system,
119 * that system may have it's own way of create a #PangoContext.
120 * For instance, the GTK+ toolkit has, among others,
121 * gdk_pango_context_get_for_screen(), and
122 * gtk_widget_get_pango_context(). Use those instead.
124 * Return value: the newly allocated #PangoContext, which should
125 * be freed with g_object_unref().
128 pango_context_new (void)
130 PangoContext *context;
132 context = g_object_new (PANGO_TYPE_CONTEXT, NULL);
138 update_resolved_gravity (PangoContext *context)
140 if (context->base_gravity == PANGO_GRAVITY_AUTO)
141 context->resolved_gravity = pango_gravity_get_for_matrix (context->matrix);
143 context->resolved_gravity = context->base_gravity;
147 * pango_context_set_matrix:
148 * @context: a #PangoContext
149 * @matrix: a #PangoMatrix, or %NULL to unset any existing matrix.
150 * (No matrix set is the same as setting the identity matrix.)
152 * Sets the transformation matrix that will be applied when rendering
153 * with this context. Note that reported metrics are in the user space
154 * coordinates before the application of the matrix, not device-space
155 * coordinates after the application of the matrix. So, they don't scale
156 * with the matrix, though they may change slightly for different
157 * matrices, depending on how the text is fit to the pixel grid.
162 pango_context_set_matrix (PangoContext *context,
163 const PangoMatrix *matrix)
165 g_return_if_fail (PANGO_IS_CONTEXT (context));
168 pango_matrix_free (context->matrix);
170 context->matrix = pango_matrix_copy (matrix);
172 context->matrix = NULL;
174 update_resolved_gravity (context);
178 * pango_context_get_matrix:
179 * @context: a #PangoContext
181 * Gets the transformation matrix that will be applied when
182 * rendering with this context. See pango_context_set_matrix().
184 * Return value: the matrix, or %NULL if no matrix has been set
185 * (which is the same as the identity matrix). The returned
186 * matrix is owned by Pango and must not be modified or
191 G_CONST_RETURN PangoMatrix *
192 pango_context_get_matrix (PangoContext *context)
194 g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
196 return context->matrix;
200 * pango_context_set_font_map:
201 * @context: a #PangoContext
202 * @font_map: the #PangoFontMap to set.
204 * Sets the font map to be searched when fonts are looked-up in this context.
205 * This is only for internal use by Pango backends, a #PangoContext obtained
206 * via one of the recommended methods should already have a suitable font map.
209 pango_context_set_font_map (PangoContext *context,
210 PangoFontMap *font_map)
212 g_return_if_fail (PANGO_IS_CONTEXT (context));
213 g_return_if_fail (!font_map || PANGO_IS_FONT_MAP (font_map));
216 g_object_ref (font_map);
218 if (context->font_map)
219 g_object_unref (context->font_map);
221 context->font_map = font_map;
225 * pango_context_get_font_map:
226 * @context: a #PangoContext
228 * Gets the #PangoFontmap used to look up fonts for this context.
230 * Return value: the font map for the #PangoContext. This value
231 * is owned by Pango and should not be unreferenced.
236 pango_context_get_font_map (PangoContext *context)
238 g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
240 return context->font_map;
244 * pango_context_list_families:
245 * @context: a #PangoContext
246 * @families: location to store a pointer to an array of #PangoFontFamily *.
247 * This array should be freed with g_free().
248 * @n_families: location to store the number of elements in @descs
250 * List all families for a context.
253 pango_context_list_families (PangoContext *context,
254 PangoFontFamily ***families,
257 g_return_if_fail (context != NULL);
258 g_return_if_fail (families == NULL || n_families != NULL);
260 if (n_families == NULL)
263 if (context->font_map == NULL)
272 pango_font_map_list_families (context->font_map, families, n_families);
276 * pango_context_load_font:
277 * @context: a #PangoContext
278 * @desc: a #PangoFontDescription describing the font to load
280 * Loads the font in one of the fontmaps in the context
281 * that is the closest match for @desc.
283 * Returns: the font loaded, or %NULL if no font matched.
286 pango_context_load_font (PangoContext *context,
287 const PangoFontDescription *desc)
289 g_return_val_if_fail (context != NULL, NULL);
291 return pango_font_map_load_font (context->font_map, context, desc);
295 * pango_context_load_fontset:
296 * @context: a #PangoContext
297 * @desc: a #PangoFontDescription describing the fonts to load
298 * @language: a #PangoLanguage the fonts will be used for
300 * Load a set of fonts in the context that can be used to render
301 * a font matching @desc.
303 * Returns: the fontset, or %NULL if no font matched.
306 pango_context_load_fontset (PangoContext *context,
307 const PangoFontDescription *desc,
308 PangoLanguage *language)
310 g_return_val_if_fail (context != NULL, NULL);
312 return pango_font_map_load_fontset (context->font_map, context, desc, language);
316 * pango_context_set_font_description:
317 * @context: a #PangoContext
318 * @desc: the new pango font description
320 * Set the default font description for the context
323 pango_context_set_font_description (PangoContext *context,
324 const PangoFontDescription *desc)
326 g_return_if_fail (context != NULL);
327 g_return_if_fail (desc != NULL);
329 pango_font_description_free (context->font_desc);
330 context->font_desc = pango_font_description_copy (desc);
334 * pango_context_get_font_description:
335 * @context: a #PangoContext
337 * Retrieve the default font description for the context.
339 * Return value: a pointer to the context's default font description.
340 * This value must not be modified or freed.
342 PangoFontDescription *
343 pango_context_get_font_description (PangoContext *context)
345 g_return_val_if_fail (context != NULL, NULL);
347 return context->font_desc;
351 * pango_context_set_language:
352 * @context: a #PangoContext
353 * @language: the new language tag.
355 * Sets the global language tag for the context. The default language
356 * for the locale of the running process can be found using
357 * pango_language_get_default().
360 pango_context_set_language (PangoContext *context,
361 PangoLanguage *language)
363 g_return_if_fail (context != NULL);
365 context->set_language = language;
367 context->language = language;
369 context->language = pango_language_get_default ();
373 * pango_context_get_language:
374 * @context: a #PangoContext
376 * Retrieves the global language tag for the context.
378 * Return value: the global language tag.
381 pango_context_get_language (PangoContext *context)
383 g_return_val_if_fail (context != NULL, NULL);
385 return context->set_language;
389 * pango_context_set_base_dir:
390 * @context: a #PangoContext
391 * @direction: the new base direction
393 * Sets the base direction for the context.
395 * The base direction is used in applying the Unicode bidirectional
396 * algorithm; if the @direction is %PANGO_DIRECTION_LTR or
397 * %PANGO_DIRECTION_RTL, then the value will be used as the paragraph
398 * direction in the Unicode bidirectional algorithm. A value of
399 * %PANGO_DIRECTION_WEAK_LTR or %PANGO_DIRECTION_WEAK_RTL is used only
400 * for paragraphs that do not contain any strong characters themselves.
403 pango_context_set_base_dir (PangoContext *context,
404 PangoDirection direction)
406 g_return_if_fail (context != NULL);
408 context->base_dir = direction;
412 * pango_context_get_base_dir:
413 * @context: a #PangoContext
415 * Retrieves the base direction for the context. See
416 * pango_context_set_base_dir().
418 * Return value: the base direction for the context.
421 pango_context_get_base_dir (PangoContext *context)
423 g_return_val_if_fail (context != NULL, PANGO_DIRECTION_LTR);
425 return context->base_dir;
429 * pango_context_set_base_gravity:
430 * @context: a #PangoContext
431 * @gravity: the new base gravity
433 * Sets the base gravity for the context.
435 * The base gravity is used in laying vertical text out.
440 pango_context_set_base_gravity (PangoContext *context,
441 PangoGravity gravity)
443 g_return_if_fail (context != NULL);
445 context->base_gravity = gravity;
447 update_resolved_gravity (context);
451 * pango_context_get_base_gravity:
452 * @context: a #PangoContext
454 * Retrieves the base gravity for the context. See
455 * pango_context_set_base_gravity().
457 * Return value: the base gravity for the context.
462 pango_context_get_base_gravity (PangoContext *context)
464 g_return_val_if_fail (context != NULL, PANGO_GRAVITY_SOUTH);
466 return context->base_gravity;
470 * pango_context_get_gravity:
471 * @context: a #PangoContext
473 * Retrieves the gravity for the context. This is similar to
474 * pango_context_get_base_gravity(), except for when the base gravity
475 * is %PANGO_GRAVITY_AUTO for which pango_gravity_get_for_matrix() is used
476 * to return the gravity from the current context matrix.
478 * Return value: the resolved gravity for the context.
483 pango_context_get_gravity (PangoContext *context)
485 g_return_val_if_fail (context != NULL, PANGO_GRAVITY_SOUTH);
487 return context->resolved_gravity;
491 * pango_context_set_gravity_hint:
492 * @context: a #PangoContext
493 * @hint: the new gravity hint
495 * Sets the gravity hint for the context.
497 * The gravity hint is used in laying vertical text out, and is only relevant
498 * if gravity of the context as returned by pango_context_get_gravity()
499 * is set %PANGO_GRAVITY_EAST or %PANGO_GRAVITY_WEST.
504 pango_context_set_gravity_hint (PangoContext *context,
505 PangoGravityHint hint)
507 g_return_if_fail (context != NULL);
509 context->gravity_hint = hint;
513 * pango_context_get_gravity_hint:
514 * @context: a #PangoContext
516 * Retrieves the gravity hint for the context. See
517 * pango_context_set_gravity_hint() for details.
519 * Return value: the gravity hint for the context.
524 pango_context_get_gravity_hint (PangoContext *context)
526 g_return_val_if_fail (context != NULL, PANGO_GRAVITY_HINT_NATURAL);
528 return context->gravity_hint;
531 /**********************************************************************/
534 advance_attr_iterator_to (PangoAttrIterator *iterator,
537 int start_range, end_range;
539 pango_attr_iterator_range (iterator, &start_range, &end_range);
541 while (start_index >= end_range)
543 if (!pango_attr_iterator_next (iterator))
545 pango_attr_iterator_range (iterator, &start_range, &end_range);
548 if (start_range > start_index)
549 g_warning ("In pango_itemize(), the cached iterator passed in "
550 "had already moved beyond the start_index");
555 /***************************************************************************
556 * We cache the results of character,fontset => font,shaper in a hash table
557 ***************************************************************************/
564 PangoEngineShape *shape_engine;
569 shaper_font_cache_destroy (ShaperFontCache *cache)
571 g_hash_table_destroy (cache->hash);
572 g_slice_free (ShaperFontCache, cache);
576 shaper_font_element_destroy (ShaperFontElement *element)
578 if (element->shape_engine)
579 g_object_unref (element->shape_engine);
581 g_object_unref (element->font);
582 g_slice_free (ShaperFontElement, element);
585 static ShaperFontCache *
586 get_shaper_font_cache (PangoFontset *fontset)
588 ShaperFontCache *cache;
590 static GQuark cache_quark = 0;
591 if (G_UNLIKELY (!cache_quark))
592 cache_quark = g_quark_from_static_string ("pango-shaper-font-cache");
594 cache = g_object_get_qdata (G_OBJECT (fontset), cache_quark);
597 cache = g_slice_new (ShaperFontCache);
598 cache->hash = g_hash_table_new_full (g_direct_hash, NULL,
599 NULL, (GDestroyNotify)shaper_font_element_destroy);
601 g_object_set_qdata_full (G_OBJECT (fontset), cache_quark,
602 cache, (GDestroyNotify)shaper_font_cache_destroy);
609 shaper_font_cache_get (ShaperFontCache *cache,
611 PangoEngineShape **shape_engine,
614 ShaperFontElement *element;
616 element = g_hash_table_lookup (cache->hash, GUINT_TO_POINTER (wc));
619 *shape_engine = element->shape_engine;
620 *font = element->font;
629 shaper_font_cache_insert (ShaperFontCache *cache,
631 PangoEngineShape *shape_engine,
634 ShaperFontElement *element = g_slice_new (ShaperFontElement);
635 element->shape_engine = shape_engine ? g_object_ref (shape_engine) : NULL;
636 element->font = font ? g_object_ref (font) : NULL;
638 g_hash_table_insert (cache->hash, GUINT_TO_POINTER (wc), element);
641 /**********************************************************************/
644 EMBEDDING_CHANGED = 1 << 0,
645 SCRIPT_CHANGED = 1 << 1,
646 LANG_CHANGED = 1 << 2,
647 FONT_CHANGED = 1 << 3,
648 DERIVED_LANG_CHANGED = 1 << 4,
649 WIDTH_CHANGED = 1 << 5
654 typedef struct _PangoWidthIter PangoWidthIter;
656 struct _PangoWidthIter
658 const gchar *text_start;
659 const gchar *text_end;
665 typedef struct _ItemizeState ItemizeState;
671 PangoContext *context;
675 const char *run_start;
681 guint8 *embedding_levels;
682 int embedding_end_offset;
683 const char *embedding_end;
686 PangoGravity gravity;
687 PangoGravityHint gravity_hint;
688 PangoGravity resolved_gravity;
689 PangoGravity font_desc_gravity;
690 gboolean centered_baseline;
692 PangoAttrIterator *attr_iter;
693 gboolean free_attr_iter;
694 const char *attr_end;
695 PangoFontDescription *font_desc;
698 gboolean copy_extra_attrs;
700 ChangedFlags changed;
702 PangoScriptIter script_iter;
703 const char *script_end;
706 PangoWidthIter width_iter;
708 PangoLanguage *derived_lang;
709 PangoEngineLang *lang_engine;
711 PangoFontset *current_fonts;
712 ShaperFontCache *cache;
713 PangoFont *base_font;
714 gboolean enable_fallback;
716 GSList *exact_engines;
717 GSList *fallback_engines;
721 update_embedding_end (ItemizeState *state)
723 state->embedding = state->embedding_levels[state->embedding_end_offset];
724 while (state->embedding_end < state->end &&
725 state->embedding_levels[state->embedding_end_offset] == state->embedding)
727 state->embedding_end_offset++;
728 state->embedding_end = g_utf8_next_char (state->embedding_end);
731 state->changed |= EMBEDDING_CHANGED;
734 static PangoAttribute *
735 find_attribute (GSList *attr_list,
740 for (node = attr_list; node; node = node->next)
741 if (((PangoAttribute *) node->data)->klass->type == type)
742 return (PangoAttribute *) node->data;
748 update_attr_iterator (ItemizeState *state)
750 PangoLanguage *old_lang;
751 PangoAttribute *attr;
754 pango_attr_iterator_range (state->attr_iter, NULL, &end_index);
755 if (end_index < state->end - state->text)
756 state->attr_end = state->text + end_index;
758 state->attr_end = state->end;
760 old_lang = state->lang;
761 if (state->font_desc)
762 pango_font_description_free (state->font_desc);
763 state->font_desc = pango_font_description_copy_static (state->context->font_desc);
764 pango_attr_iterator_get_font (state->attr_iter, state->font_desc,
765 &state->lang, &state->extra_attrs);
766 if (pango_font_description_get_set_fields (state->font_desc) & PANGO_FONT_MASK_GRAVITY)
767 state->font_desc_gravity = pango_font_description_get_gravity (state->font_desc);
769 state->font_desc_gravity = PANGO_GRAVITY_AUTO;
771 state->copy_extra_attrs = FALSE;
774 state->lang = state->context->language;
776 attr = find_attribute (state->extra_attrs, PANGO_ATTR_FALLBACK);
777 state->enable_fallback = (attr == NULL || ((PangoAttrInt *)attr)->value);
779 attr = find_attribute (state->extra_attrs, PANGO_ATTR_GRAVITY);
780 state->gravity = attr == NULL ? PANGO_GRAVITY_AUTO : ((PangoAttrInt *)attr)->value;
782 attr = find_attribute (state->extra_attrs, PANGO_ATTR_GRAVITY_HINT);
783 state->gravity_hint = attr == NULL ? state->context->gravity_hint : (PangoGravityHint)((PangoAttrInt *)attr)->value;
785 state->changed |= FONT_CHANGED;
786 if (state->lang != old_lang)
787 state->changed |= LANG_CHANGED;
791 update_end (ItemizeState *state)
793 state->run_end = state->embedding_end;
794 if (state->attr_end < state->run_end)
795 state->run_end = state->attr_end;
796 if (state->script_end < state->run_end)
797 state->run_end = state->script_end;
798 if (state->width_iter.end < state->run_end)
799 state->run_end = state->width_iter.end;
803 width_iter_next(PangoWidthIter* iter)
805 iter->start = iter->end;
807 if (iter->end < iter->text_end)
809 gunichar ch = g_utf8_get_char (iter->end);
810 iter->wide = g_unichar_iswide (ch);
813 while (iter->end < iter->text_end)
815 gunichar ch = g_utf8_get_char (iter->end);
816 if (g_unichar_iswide (ch) != iter->wide)
818 iter->end = g_utf8_next_char (iter->end);
823 width_iter_init (PangoWidthIter* iter, const char* text, int length)
825 iter->text_start = text;
826 iter->text_end = text + length;
827 iter->start = iter->end = text;
829 width_iter_next (iter);
833 itemize_state_init (ItemizeState *state,
834 PangoContext *context,
836 PangoDirection base_dir,
839 PangoAttrList *attrs,
840 PangoAttrIterator *cached_iter,
841 const PangoFontDescription *desc)
844 state->context = context;
846 state->end = text + start_index + length;
848 state->result = NULL;
851 state->run_start = text + start_index;
853 /* First, apply the bidirectional algorithm to break
854 * the text into directional runs.
856 state->embedding_levels = pango_log2vis_get_embedding_levels (text + start_index, length, &base_dir);
858 state->embedding_end_offset = 0;
859 state->embedding_end = text + start_index;
860 update_embedding_end (state);
862 /* Initialize the attribute iterator
866 state->attr_iter = cached_iter;
867 state->free_attr_iter = FALSE;
871 state->attr_iter = pango_attr_list_get_iterator (attrs);
872 state->free_attr_iter = TRUE;
876 state->attr_iter = NULL;
877 state->free_attr_iter = FALSE;
880 if (state->attr_iter)
882 state->font_desc = NULL;
885 advance_attr_iterator_to (state->attr_iter, start_index);
886 update_attr_iterator (state);
890 state->font_desc = pango_font_description_copy_static (desc ? desc : state->context->font_desc);
891 state->lang = state->context->language;
892 state->extra_attrs = NULL;
893 state->copy_extra_attrs = FALSE;
895 state->attr_end = state->end;
896 state->enable_fallback = TRUE;
899 /* Initialize the script iterator
901 _pango_script_iter_init (&state->script_iter, text + start_index, length);
902 pango_script_iter_get_range (&state->script_iter, NULL,
903 &state->script_end, &state->script);
905 /* Initialize the width iterator */
906 width_iter_init (&state->width_iter, text + start_index, length);
910 if (pango_font_description_get_set_fields (state->font_desc) & PANGO_FONT_MASK_GRAVITY)
911 state->font_desc_gravity = pango_font_description_get_gravity (state->font_desc);
913 state->font_desc_gravity = PANGO_GRAVITY_AUTO;
915 state->gravity = PANGO_GRAVITY_AUTO;
916 state->centered_baseline = PANGO_GRAVITY_IS_VERTICAL (state->context->resolved_gravity);
917 state->gravity_hint = state->context->gravity_hint;
918 state->resolved_gravity = PANGO_GRAVITY_AUTO;
919 state->derived_lang = NULL;
920 state->lang_engine = NULL;
921 state->current_fonts = NULL;
923 state->exact_engines = NULL;
924 state->fallback_engines = NULL;
925 state->base_font = NULL;
927 state->changed = EMBEDDING_CHANGED | SCRIPT_CHANGED | LANG_CHANGED | FONT_CHANGED | WIDTH_CHANGED;
931 itemize_state_next (ItemizeState *state)
933 if (state->run_end == state->end)
938 state->run_start = state->run_end;
940 if (state->run_end == state->embedding_end)
942 update_embedding_end (state);
945 if (state->run_end == state->attr_end)
947 pango_attr_iterator_next (state->attr_iter);
948 update_attr_iterator (state);
951 if (state->run_end == state->script_end)
953 pango_script_iter_next (&state->script_iter);
954 pango_script_iter_get_range (&state->script_iter, NULL,
955 &state->script_end, &state->script);
956 state->changed |= SCRIPT_CHANGED;
958 if (state->run_end == state->width_iter.end)
960 width_iter_next (&state->width_iter);
961 state->changed |= WIDTH_CHANGED;
970 copy_attr_slist (GSList *attr_slist)
972 GSList *new_list = NULL;
975 for (l = attr_slist; l; l = l->next)
976 new_list = g_slist_prepend (new_list, pango_attribute_copy (l->data));
978 return g_slist_reverse (new_list);
982 itemize_state_fill_shaper (ItemizeState *state,
983 PangoEngineShape *shape_engine,
988 for (l = state->result; l; l = l->next)
990 PangoItem *item = l->data;
991 if (item->analysis.shape_engine)
994 item->analysis.font = g_object_ref (font);
996 item->analysis.font = NULL;
997 item->analysis.shape_engine = shape_engine;
1002 itemize_state_add_character (ItemizeState *state,
1003 PangoEngineShape *shape_engine,
1005 gboolean force_break,
1010 if (!state->item->analysis.shape_engine && shape_engine)
1012 itemize_state_fill_shaper (state, shape_engine, font);
1014 else if (state->item->analysis.shape_engine && !shape_engine)
1016 font = state->item->analysis.font;
1017 shape_engine = state->item->analysis.shape_engine;
1021 state->item->analysis.lang_engine == state->lang_engine &&
1022 state->item->analysis.shape_engine == shape_engine &&
1023 state->item->analysis.font == font)
1025 state->item->num_chars++;
1029 state->item->length = (pos - state->text) - state->item->offset;
1032 state->item = pango_item_new ();
1033 state->item->offset = pos - state->text;
1034 state->item->length = 0;
1035 state->item->num_chars = 1;
1036 state->item->analysis.shape_engine = shape_engine;
1037 state->item->analysis.lang_engine = state->lang_engine;
1040 g_object_ref (font);
1041 state->item->analysis.font = font;
1043 state->item->analysis.level = state->embedding;
1044 state->item->analysis.gravity = state->resolved_gravity;
1046 /* The level vs. gravity dance:
1047 * - If gravity is SOUTH, leave level untouched.
1048 * - If gravity is NORTH, step level one up, to
1049 * not get mirrored upside-down text.
1050 * - If gravity is EAST, step up to an even level, as
1051 * it's a clockwise-rotated layout, so the rotated
1052 * top is unrotated left.
1053 * - If gravity is WEST, step up to an odd level, as
1054 * it's a counter-clockwise-rotated layout, so the rotated
1055 * top is unrotated right.
1057 * A similar dance is performed in pango-layout.c:
1058 * line_set_resolved_dir(). Keep in synch.
1060 switch (state->item->analysis.gravity)
1062 case PANGO_GRAVITY_SOUTH:
1065 case PANGO_GRAVITY_NORTH:
1066 state->item->analysis.level++;
1068 case PANGO_GRAVITY_EAST:
1069 state->item->analysis.level += 1;
1070 state->item->analysis.level &= ~1;
1072 case PANGO_GRAVITY_WEST:
1073 state->item->analysis.level |= 1;
1077 state->item->analysis.flags = state->centered_baseline ? PANGO_ANALYSIS_FLAG_CENTERED_BASELINE : 0;
1079 state->item->analysis.script = state->script;
1080 state->item->analysis.language = state->derived_lang;
1082 if (state->copy_extra_attrs)
1084 state->item->analysis.extra_attrs = copy_attr_slist (state->extra_attrs);
1088 state->item->analysis.extra_attrs = state->extra_attrs;
1089 state->copy_extra_attrs = TRUE;
1092 state->result = g_list_prepend (state->result, state->item);
1096 get_engines (PangoContext *context,
1097 PangoLanguage *lang,
1099 GSList **exact_engines,
1100 GSList **fallback_engines)
1102 const char *engine_type = pango_font_map_get_shape_engine_type (context->font_map);
1103 PangoMap *shaper_map = pango_find_map (lang,
1104 g_quark_from_string (PANGO_ENGINE_TYPE_SHAPE),
1105 g_quark_from_string (engine_type));
1106 pango_map_get_engines (shaper_map, script,
1107 exact_engines, fallback_engines);
1111 PangoLanguage *lang;
1114 PangoEngineShape *shape_engine;
1116 } GetShaperFontInfo;
1119 get_shaper_and_font_foreach (PangoFontset *fontset G_GNUC_UNUSED,
1123 GetShaperFontInfo *info = data;
1126 if (G_UNLIKELY (!font))
1129 for (l = info->engines; l; l = l->next)
1131 PangoEngineShape *engine = l->data;
1132 PangoCoverageLevel level;
1134 level = _pango_engine_shape_covers (engine, font,
1135 info->lang, info->wc);
1136 if (level != PANGO_COVERAGE_NONE)
1138 info->shape_engine = engine;
1148 get_base_font (ItemizeState *state)
1150 if (!state->base_font)
1151 state->base_font = pango_font_map_load_font (state->context->font_map,
1154 return state->base_font;
1158 get_script (ItemizeState *state)
1160 /* Always use a basic shaper for vertical layout (ie, east/west gravity)
1161 * as none of our script shapers support vertical shaping right now.
1163 * XXX Should move the knowledge into the shaper interface.
1166 if (PANGO_GRAVITY_IS_VERTICAL (state->resolved_gravity))
1167 return PANGO_SCRIPT_COMMON;
1169 return state->script;
1173 get_shaper_and_font (ItemizeState *state,
1175 PangoEngineShape **shape_engine,
1178 GetShaperFontInfo info;
1180 /* We'd need a separate cache when fallback is disabled, but since lookup
1181 * with fallback disabled is faster anyways, we just skip caching */
1182 if (state->enable_fallback && shaper_font_cache_get (state->cache, wc, shape_engine, font))
1183 return *shape_engine != NULL;
1185 if (!state->exact_engines && !state->fallback_engines)
1186 get_engines (state->context, state->derived_lang, get_script (state),
1187 &state->exact_engines, &state->fallback_engines);
1189 info.lang = state->derived_lang;
1191 info.shape_engine = NULL;
1194 info.engines = state->exact_engines;
1197 if (state->enable_fallback)
1198 pango_fontset_foreach (state->current_fonts, get_shaper_and_font_foreach, &info);
1200 get_shaper_and_font_foreach (NULL, get_base_font (state), &info);
1202 if (info.shape_engine)
1204 *shape_engine = info.shape_engine;
1207 /* skip caching if fallback disabled (see above) */
1208 if (state->enable_fallback)
1209 shaper_font_cache_insert (state->cache, wc, *shape_engine, *font);
1215 info.engines = state->fallback_engines;
1218 if (state->enable_fallback)
1219 pango_fontset_foreach (state->current_fonts, get_shaper_and_font_foreach, &info);
1221 get_shaper_and_font_foreach (NULL, get_base_font (state), &info);
1224 *shape_engine = info.shape_engine;
1227 /* skip caching if fallback disabled (see above) */
1228 if (state->enable_fallback)
1229 shaper_font_cache_insert (state->cache, wc, *shape_engine, *font);
1231 return *shape_engine != NULL;
1235 itemize_state_reset_shape_engines (ItemizeState *state)
1237 g_slist_free (state->exact_engines);
1238 state->exact_engines = NULL;
1239 g_slist_free (state->fallback_engines);
1240 state->fallback_engines = NULL;
1243 static PangoLanguage *
1244 compute_derived_language (PangoLanguage *lang,
1247 PangoLanguage *derived_lang;
1249 /* Make sure the language tag is consistent with the derived
1250 * script. There is no point in marking up a section of
1251 * Arabic text with the "en" language tag.
1253 if (lang && pango_language_includes_script (lang, script))
1254 derived_lang = lang;
1257 derived_lang = pango_script_get_sample_language (script);
1258 /* If we don't find a sample language for the script, we
1259 * use a language tag that shouldn't actually be used
1260 * anywhere. This keeps fontconfig (for the PangoFc*
1261 * backend) from using the language tag to affect the
1262 * sort order. I don't have a reference for 'xx' being
1263 * safe here, though Keith Packard claims it is.
1266 derived_lang = pango_language_from_string ("xx");
1269 return derived_lang;
1273 get_lang_map (PangoLanguage *lang)
1275 static guint engine_type_id = 0;
1276 static guint render_type_id = 0;
1278 if (engine_type_id == 0)
1280 engine_type_id = g_quark_from_static_string (PANGO_ENGINE_TYPE_LANG);
1281 render_type_id = g_quark_from_static_string (PANGO_RENDER_TYPE_NONE);
1284 return pango_find_map (lang, engine_type_id, render_type_id);
1288 itemize_state_update_for_new_run (ItemizeState *state)
1290 /* This block should be moved to update_attr_iterator, but I'm too lazy to
1291 * do it right now */
1292 if (state->changed & (FONT_CHANGED | SCRIPT_CHANGED | WIDTH_CHANGED))
1294 PangoGravity old_gravity = state->resolved_gravity;
1296 /* Font-desc gravity overrides everything */
1297 if (state->font_desc_gravity != PANGO_GRAVITY_AUTO)
1299 state->resolved_gravity = state->font_desc_gravity;
1303 PangoGravity gravity = state->gravity;
1304 PangoGravityHint gravity_hint = state->gravity_hint;
1306 if (G_LIKELY (gravity == PANGO_GRAVITY_AUTO))
1307 gravity = state->context->resolved_gravity;
1309 state->resolved_gravity = pango_gravity_get_for_script_and_width (state->script,
1310 state->width_iter.wide,
1315 if (old_gravity != state->resolved_gravity)
1317 pango_font_description_set_gravity (state->font_desc, state->resolved_gravity);
1318 state->changed |= FONT_CHANGED;
1322 if (state->changed & (SCRIPT_CHANGED | LANG_CHANGED))
1324 PangoLanguage *old_derived_lang = state->derived_lang;
1325 state->derived_lang = compute_derived_language (state->lang, state->script);
1326 if (old_derived_lang != state->derived_lang)
1327 state->changed |= DERIVED_LANG_CHANGED;
1330 if ((state->changed & DERIVED_LANG_CHANGED) || !state->lang_engine)
1332 PangoMap *lang_map = get_lang_map (state->derived_lang);
1333 state->lang_engine = (PangoEngineLang *)pango_map_get_engine (lang_map, state->script);
1336 if (state->changed & (SCRIPT_CHANGED | DERIVED_LANG_CHANGED))
1337 itemize_state_reset_shape_engines (state);
1339 if (state->changed & (FONT_CHANGED | DERIVED_LANG_CHANGED) &&
1340 state->current_fonts)
1342 g_object_unref (state->current_fonts);
1343 state->current_fonts = NULL;
1344 state->cache = NULL;
1347 if (!state->current_fonts)
1349 state->current_fonts = pango_font_map_load_fontset (state->context->font_map,
1352 state->derived_lang);
1353 state->cache = get_shaper_font_cache (state->current_fonts);
1356 if ((state->changed & FONT_CHANGED) && state->base_font)
1358 g_object_unref (state->base_font);
1359 state->base_font = NULL;
1364 string_from_script (PangoScript script)
1366 static GEnumClass *class = NULL;
1369 class = g_type_class_ref (PANGO_TYPE_SCRIPT);
1371 value = g_enum_get_value (class, script);
1373 return string_from_script (PANGO_SCRIPT_INVALID_CODE);
1375 return value->value_nick;
1379 itemize_state_process_run (ItemizeState *state)
1382 gboolean last_was_forced_break = FALSE;
1384 /* Only one character has type G_UNICODE_LINE_SEPARATOR in Unicode 4.0;
1385 * update this if that changes. */
1386 #define LINE_SEPARATOR 0x2028
1388 itemize_state_update_for_new_run (state);
1390 /* We should never get an empty run */
1391 g_assert (state->run_end != state->run_start);
1393 for (p = state->run_start;
1395 p = g_utf8_next_char (p))
1397 gunichar wc = g_utf8_get_char (p);
1398 gboolean is_forced_break = (wc == '\t' || wc == LINE_SEPARATOR);
1399 PangoEngineShape *shape_engine;
1402 /* We don't want space characters to affect font selection; in general,
1403 * it's always wrong to select a font just to render a space. But until
1404 * we have a better solution, choosing a font for spaces seems to work
1405 * better. However, all fonts are assumed to cover ASCII space, so that
1406 * one is an exception. See bug #355987.
1408 * The exception of PrivateUse and Unassigned characters is necessary
1409 * to be able to render any of them. (for private or being encoded
1410 * scripts, etc.) (Recent glib returns true in isprint for PrivateUse.)
1413 G_UNLIKELY (!g_unichar_isprint (wc) &&
1414 g_unichar_type (wc) != G_UNICODE_PRIVATE_USE &&
1415 g_unichar_type (wc) != G_UNICODE_UNASSIGNED))
1417 shape_engine = NULL;
1422 get_shaper_and_font (state, wc, &shape_engine, &font);
1425 itemize_state_add_character (state,
1427 is_forced_break || last_was_forced_break,
1430 last_was_forced_break = is_forced_break;
1433 /* Finish the final item from the current segment */
1434 state->item->length = (p - state->text) - state->item->offset;
1435 if (!state->item->analysis.shape_engine)
1437 PangoEngineShape *shape_engine;
1440 if (G_UNLIKELY (!get_shaper_and_font (state, ' ', &shape_engine, &font)))
1442 /* If no shaper was found, warn only once per fontmap/script pair */
1443 PangoFontMap *fontmap = state->context->font_map;
1444 const char *script_name = string_from_script (get_script (state));
1446 if (!g_object_get_data (G_OBJECT (fontmap), script_name))
1448 g_warning ("failed to choose a font, expect ugly output. engine-type='%s', script='%s'",
1449 pango_font_map_get_shape_engine_type (fontmap),
1452 g_object_set_data_full (G_OBJECT (fontmap), script_name,
1453 GINT_TO_POINTER (1), NULL);
1456 shape_engine = _pango_get_fallback_shaper ();
1460 itemize_state_fill_shaper (state, shape_engine, font);
1466 itemize_state_finish (ItemizeState *state)
1468 g_free (state->embedding_levels);
1469 if (state->free_attr_iter)
1470 pango_attr_iterator_destroy (state->attr_iter);
1471 _pango_script_iter_fini (&state->script_iter);
1472 pango_font_description_free (state->font_desc);
1474 itemize_state_reset_shape_engines (state);
1475 if (state->current_fonts)
1476 g_object_unref (state->current_fonts);
1477 if (state->base_font)
1478 g_object_unref (state->base_font);
1482 * pango_itemize_with_base_dir:
1483 * @context: a structure holding information that affects
1484 the itemization process.
1485 * @text: the text to itemize.
1486 * @start_index: first byte in @text to process
1487 * @length: the number of bytes (not characters) to process
1488 * after @start_index.
1489 * This must be >= 0.
1490 * @base_dir: base direction to use for bidirectional processing
1491 * @attrs: the set of attributes that apply to @text.
1492 * @cached_iter: Cached attribute iterator, or %NULL
1494 * Like pango_itemize(), but the base direction to use when
1495 * computing bidirectional levels (see pango_context_set_base_dir ()),
1496 * is specified explicitly rather than gotten from the #PangoContext.
1498 * Return value: a #GList of #PangoItem structures. The items should be
1499 * freed using pango_item_free() probably in combination with g_list_foreach(),
1500 * and the list itself using g_list_free().
1505 pango_itemize_with_base_dir (PangoContext *context,
1506 PangoDirection base_dir,
1510 PangoAttrList *attrs,
1511 PangoAttrIterator *cached_iter)
1515 g_return_val_if_fail (context != NULL, NULL);
1516 g_return_val_if_fail (start_index >= 0, NULL);
1517 g_return_val_if_fail (length >= 0, NULL);
1518 g_return_val_if_fail (length == 0 || text != NULL, NULL);
1523 itemize_state_init (&state, context, text, base_dir, start_index, length,
1524 attrs, cached_iter, NULL);
1527 itemize_state_process_run (&state);
1528 while (itemize_state_next (&state));
1530 itemize_state_finish (&state);
1532 return g_list_reverse (state.result);
1536 itemize_with_font (PangoContext *context,
1540 const PangoFontDescription *desc)
1547 itemize_state_init (&state, context, text, context->base_dir, start_index, length,
1551 itemize_state_process_run (&state);
1552 while (itemize_state_next (&state));
1554 itemize_state_finish (&state);
1556 return g_list_reverse (state.result);
1561 * @context: a structure holding information that affects
1562 the itemization process.
1563 * @text: the text to itemize.
1564 * @start_index: first byte in @text to process
1565 * @length: the number of bytes (not characters) to process
1566 * after @start_index.
1567 * This must be >= 0.
1568 * @attrs: the set of attributes that apply to @text.
1569 * @cached_iter: Cached attribute iterator, or %NULL
1571 * Breaks a piece of text into segments with consistent
1572 * directional level and shaping engine. Each byte of @text will
1573 * be contained in exactly one of the items in the returned list;
1574 * the generated list of items will be in logical order (the start
1575 * offsets of the items are ascending).
1577 * @cached_iter should be an iterator over @attrs currently positioned at a
1578 * range before or containing @start_index; @cached_iter will be advanced to
1579 * the range covering the position just after @start_index + @length.
1580 * (i.e. if itemizing in a loop, just keep passing in the same @cached_iter).
1582 * Return value: a #GList of #PangoItem structures.
1585 pango_itemize (PangoContext *context,
1589 PangoAttrList *attrs,
1590 PangoAttrIterator *cached_iter)
1592 g_return_val_if_fail (context != NULL, NULL);
1593 g_return_val_if_fail (start_index >= 0, NULL);
1594 g_return_val_if_fail (length >= 0, NULL);
1595 g_return_val_if_fail (length == 0 || text != NULL, NULL);
1597 return pango_itemize_with_base_dir (context, context->base_dir,
1598 text, start_index, length, attrs, cached_iter);
1602 get_first_metrics_foreach (PangoFontset *fontset,
1606 PangoFontMetrics *fontset_metrics = data;
1607 PangoLanguage *language = PANGO_FONTSET_GET_CLASS (fontset)->get_language (fontset);
1608 PangoFontMetrics *font_metrics = pango_font_get_metrics (font, language);
1609 guint save_ref_count;
1611 /* Initialize the fontset metrics to metrics of the first font in the
1612 * fontset; saving the refcount and restoring it is a bit of hack but avoids
1613 * having to update this code for each metrics addition.
1615 save_ref_count = fontset_metrics->ref_count;
1616 *fontset_metrics = *font_metrics;
1617 fontset_metrics->ref_count = save_ref_count;
1619 pango_font_metrics_unref (font_metrics);
1621 return TRUE; /* Stops iteration */
1624 static PangoFontMetrics *
1625 get_base_metrics (PangoFontset *fontset)
1627 PangoFontMetrics *metrics = pango_font_metrics_new ();
1629 /* Initialize the metrics from the first font in the fontset */
1630 pango_fontset_foreach (fontset, get_first_metrics_foreach, metrics);
1636 update_metrics_from_items (PangoFontMetrics *metrics,
1637 PangoLanguage *language,
1642 GHashTable *fonts_seen = g_hash_table_new (NULL, NULL);
1643 PangoGlyphString *glyphs = pango_glyph_string_new ();
1646 metrics->approximate_char_width = 0;
1648 for (l = items; l; l = l->next)
1650 PangoItem *item = l->data;
1651 PangoFont *font = item->analysis.font;
1653 if (font != NULL && g_hash_table_lookup (fonts_seen, font) == NULL)
1655 PangoFontMetrics *raw_metrics = pango_font_get_metrics (font, language);
1656 g_hash_table_insert (fonts_seen, font, font);
1658 /* metrics will already be initialized from the first font in the fontset */
1659 metrics->ascent = MAX (metrics->ascent, raw_metrics->ascent);
1660 metrics->descent = MAX (metrics->descent, raw_metrics->descent);
1661 pango_font_metrics_unref (raw_metrics);
1664 pango_shape (text + item->offset, item->length, &item->analysis, glyphs);
1665 metrics->approximate_char_width += pango_glyph_string_get_width (glyphs);
1668 pango_glyph_string_free (glyphs);
1669 g_hash_table_destroy (fonts_seen);
1671 metrics->approximate_char_width /= pango_utf8_strwidth (text);
1675 * pango_context_get_metrics:
1676 * @context: a #PangoContext
1677 * @desc: a #PangoFontDescription structure. %NULL means that the font
1678 * description from the context will be used.
1679 * @language: language tag used to determine which script to get the metrics
1680 * for. %NULL means that the language tag from the context will
1681 * be used. If no language tag is set on the context, metrics
1682 * for the default language (as determined by
1683 * pango_language_get_default()) will be returned.
1685 * Get overall metric information for a particular font
1686 * description. Since the metrics may be substantially different for
1687 * different scripts, a language tag can be provided to indicate that
1688 * the metrics should be retrieved that correspond to the script(s)
1689 * used by that language.
1691 * The #PangoFontDescription is interpreted in the same way as
1692 * by pango_itemize(), and the family name may be a comma separated
1693 * list of figures. If characters from multiple of these families
1694 * would be used to render the string, then the returned fonts would
1695 * be a composite of the metrics for the fonts loaded for the
1696 * individual families.
1698 * Return value: a #PangoFontMetrics object. The caller must call pango_font_metrics_unref()
1699 * when finished using the object.
1702 pango_context_get_metrics (PangoContext *context,
1703 const PangoFontDescription *desc,
1704 PangoLanguage *language)
1706 PangoFontset *current_fonts = NULL;
1707 PangoFontMetrics *metrics;
1708 const char *sample_str;
1711 g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
1714 desc = context->font_desc;
1717 language = context->language;
1719 current_fonts = pango_font_map_load_fontset (context->font_map, context, desc, language);
1720 metrics = get_base_metrics (current_fonts);
1722 sample_str = pango_language_get_sample_string (language);
1723 items = itemize_with_font (context, sample_str, 0, strlen (sample_str), desc);
1725 update_metrics_from_items (metrics, language, sample_str, items);
1727 g_list_foreach (items, (GFunc)pango_item_free, NULL);
1728 g_list_free (items);
1730 g_object_unref (current_fonts);