diff options
Diffstat (limited to 'pango/pango-context.c')
-rw-r--r-- | pango/pango-context.c | 892 |
1 files changed, 642 insertions, 250 deletions
diff --git a/pango/pango-context.c b/pango/pango-context.c index 5c19adb9..d6cf6bc9 100644 --- a/pango/pango-context.c +++ b/pango/pango-context.c @@ -46,15 +46,6 @@ struct _PangoContextClass }; -static void add_engines (PangoContext *context, - const gchar *text, - gint start_index, - gint length, - PangoAttrList *attrs, - PangoAttrIterator *cached_iter, - gint n_chars, - PangoAnalysis *analyses); - static void pango_context_init (PangoContext *context); static void pango_context_class_init (PangoContextClass *klass); static void pango_context_finalize (GObject *object); @@ -360,299 +351,700 @@ pango_context_get_base_dir (PangoContext *context) return context->base_dir; } -/** - * pango_itemize: - * @context: a structure holding information that affects - the itemization process. - * @text: the text to itemize. - * @start_index: first byte in @text to process - * @length: the number of bytes (not characters) to process - * after @start_index. - * This must be >= 0. - * @attrs: the set of attributes that apply to @text. - * @cached_iter: Cached attribute iterator, or NULL - * - * Breaks a piece of text into segments with consistent - * directional level and shaping engine. Each byte of @text will - * be contained in exactly one of the items in the returned list; - * the generated list of items will be in logical order (the start - * offsets of the items are ascending). - * - * @cached_iter should be an iterator over @attrs currently positioned at a - * range before or containing @start_index; @cached_iter will be advanced to - * the range covering the position just after @start_index + @length. - * (i.e. if itemizing in a loop, just keep passing in the same @cached_iter). - * - * Return value: a GList of PangoItem structures. - */ -GList * -pango_itemize (PangoContext *context, - const char *text, - int start_index, - int length, - PangoAttrList *attrs, - PangoAttrIterator *cached_iter) +/**********************************************************************/ + +static gboolean +advance_attr_iterator_to (PangoAttrIterator *iterator, + int start_index) { - gunichar *text_ucs4; - long n_chars, i; - guint8 *embedding_levels; - PangoDirection base_dir; + int start_range, end_range; + + pango_attr_iterator_range (iterator, &start_range, &end_range); + + while (start_index >= end_range) + { + if (!pango_attr_iterator_next (iterator)) + return FALSE; + pango_attr_iterator_range (iterator, &start_range, &end_range); + } + + if (start_range > start_index) + g_warning ("In pango_itemize(), the cached iterator passed in " + "had already moved beyond the start_index"); + + return TRUE; +} + +/*************************************************************************** + * We cache the results of character,fontset => font,shaper in a hash table + ***************************************************************************/ + +typedef struct { + GHashTable *hash; +} ShaperFontCache; + +typedef struct { + PangoEngineShape *shape_engine; + PangoFont *font; +} ShaperFontElement; + +static void +shaper_font_cache_destroy (ShaperFontCache *cache) +{ + g_hash_table_destroy (cache->hash); + g_free (cache); +} + +static void +shaper_font_element_destroy (ShaperFontElement *element) +{ + if (element->font) + g_object_unref (element->font); + g_free (element); +} + +static ShaperFontCache * +get_shaper_font_cache (PangoFontset *fontset) +{ + ShaperFontCache *cache; + + static GQuark cache_quark = 0; + if (!cache_quark) + cache_quark = g_quark_from_static_string ("pango-shaper-font-cache"); + + cache = g_object_get_qdata (G_OBJECT (fontset), cache_quark); + if (!cache) + { + cache = g_new (ShaperFontCache, 1); + cache->hash = g_hash_table_new_full (g_direct_hash, NULL, + NULL, (GDestroyNotify)shaper_font_element_destroy); + + g_object_set_qdata_full (G_OBJECT (fontset), cache_quark, + cache, (GDestroyNotify)shaper_font_cache_destroy); + } + + return cache; +} + +static gboolean +shaper_font_cache_get (ShaperFontCache *cache, + gunichar wc, + PangoEngineShape **shape_engine, + PangoFont **font) +{ + ShaperFontElement *element; + + element = g_hash_table_lookup (cache->hash, GUINT_TO_POINTER (wc)); + if (element) + { + *shape_engine = element->shape_engine; + *font = element->font; + + return TRUE; + } + else + return FALSE; +} + +static void +shaper_font_cache_insert (ShaperFontCache *cache, + gunichar wc, + PangoEngineShape *shape_engine, + PangoFont *font) +{ + ShaperFontElement *element = g_new (ShaperFontElement, 1); + element->shape_engine = shape_engine; + element->font = font; + + g_hash_table_insert (cache->hash, GUINT_TO_POINTER (wc), element); +} + +/**********************************************************************/ + +typedef enum { + EMBEDDING_CHANGED = 1 << 0, + SCRIPT_CHANGED = 1 << 1, + LANG_CHANGED = 1 << 2, + FONT_CHANGED = 1 << 3, + DERIVED_LANG_CHANGED = 1 << 4 +} ChangedFlags; + +typedef struct _ItemizeState ItemizeState; + +struct _ItemizeState +{ + PangoContext *context; + const char *text; + const char *end; + + const char *run_start; + const char *run_end; + + GList *result; PangoItem *item; - const char *p; - const char *next; - GList *result = NULL; - PangoAnalysis *analyses; + guint8 *embedding_levels; + int embedding_end_offset; + const char *embedding_end; + guint8 embedding; - g_return_val_if_fail (context != NULL, NULL); - g_return_val_if_fail (start_index >= 0, NULL); - g_return_val_if_fail (length >= 0, NULL); - g_return_val_if_fail (length == 0 || text != NULL, NULL); + PangoAttrIterator *attr_iter; + gboolean free_attr_iter; + const char *attr_end; + PangoFontDescription *font_desc; + PangoLanguage *lang; + GSList *extra_attrs; + gboolean copy_extra_attrs; - if (length == 0) - return NULL; + ChangedFlags changed; + + PangoScriptIter *script_iter; + const char *script_end; + PangoScript script; - base_dir = context->base_dir; + PangoLanguage *derived_lang; + PangoEngineLang *lang_engine; + + PangoFontset *current_fonts; + ShaperFontCache *cache; + + GSList *exact_engines; + GSList *fallback_engines; +}; - if (length == 0) - return NULL; +static void +update_embedding_end (ItemizeState *state) +{ + state->embedding = state->embedding_levels[state->embedding_end_offset]; + while (state->embedding_end < state->end && + state->embedding_levels[state->embedding_end_offset] == state->embedding) + { + state->embedding_end_offset++; + state->embedding_end = g_utf8_next_char (state->embedding_end); + } + + state->changed |= EMBEDDING_CHANGED; +} + +static void +update_attr_iterator (ItemizeState *state) +{ + PangoLanguage *old_lang; + int end_index; + + pango_attr_iterator_range (state->attr_iter, NULL, &end_index); + if (end_index < state->end - state->text) + state->attr_end = state->text + end_index; + else + state->attr_end = state->end; + + old_lang = state->lang; + state->font_desc = pango_font_description_copy_static (state->context->font_desc); + pango_attr_iterator_get_font (state->attr_iter, state->font_desc, + &state->lang, &state->extra_attrs); + state->copy_extra_attrs = FALSE; + + state->changed |= FONT_CHANGED; + if (state->lang != old_lang) + state->changed |= LANG_CHANGED; +} +static void +update_end (ItemizeState *state) +{ + state->run_end = state->embedding_end; + if (state->attr_end < state->run_end) + state->run_end = state->attr_end; + if (state->script_end < state->run_end) + state->run_end = state->script_end; +} + +static void +itemize_state_init (ItemizeState *state, + PangoContext *context, + const char *text, + int start_index, + int length, + PangoAttrList *attrs, + PangoAttrIterator *cached_iter) +{ + PangoDirection base_dir; + gunichar *text_ucs4; + long n_chars; + + state->context = context; + state->text = text; + state->end = text + start_index + length; + + state->result = NULL; + state->item = NULL; + + state->run_start = text + start_index; + /* First, apply the bidirectional algorithm to break * the text into directional runs. */ - text_ucs4 = g_utf8_to_ucs4_fast (text + start_index, length, &n_chars); - - embedding_levels = g_new (guint8, n_chars); + base_dir = context->base_dir; + text_ucs4 = g_utf8_to_ucs4_fast (text + start_index, length, &n_chars); + state->embedding_levels = g_new (guint8, n_chars); pango_log2vis_get_embedding_levels (text_ucs4, n_chars, &base_dir, - embedding_levels); - - /* Storing ranges would be more efficient, but also more - * complicated... we take the simple approach for now. + state->embedding_levels); + g_free (text_ucs4); + + state->embedding_end_offset = 0; + state->embedding_end = text + start_index; + update_embedding_end (state); + + /* Initialize the attribute iterator */ + if (cached_iter) + { + state->attr_iter = cached_iter; + state->free_attr_iter = FALSE; + } + else + { + state->attr_iter = pango_attr_list_get_iterator (attrs); + state->free_attr_iter = TRUE; + } - analyses = g_new0 (PangoAnalysis, n_chars); + advance_attr_iterator_to (state->attr_iter, start_index); + update_attr_iterator (state); - /* Now, fill in the appropriate shapers, language engines and fonts for - * each character. + /* Initialize the script iterator */ + state->script_iter = pango_script_iter_new (text + start_index, length); + pango_script_iter_get_range (state->script_iter, NULL, + &state->script_end, &state->script); + + update_end (state); + + state->derived_lang = NULL; + state->lang_engine = NULL; + state->current_fonts = NULL; + state->cache = NULL; + state->exact_engines = NULL; + state->fallback_engines = NULL; + + state->changed = EMBEDDING_CHANGED | SCRIPT_CHANGED | LANG_CHANGED | FONT_CHANGED; +} - add_engines (context, text, start_index, length, attrs, - cached_iter, - n_chars, - analyses); +static gboolean +itemize_state_next (ItemizeState *state) +{ + if (state->run_end == state->end) + return FALSE; - /* Make a GList of PangoItems out of the above results - */ + state->changed = 0; + + state->run_start = state->run_end; - item = NULL; - p = text + start_index; - for (i=0; i<n_chars; i++) + if (state->run_end == state->embedding_end) { - PangoAnalysis *analysis = &analyses[i]; - PangoAnalysis *last_analysis = i > 0 ? &analyses[i-1] : 0; - - next = g_utf8_next_char (p); - - if (i == 0 || - text_ucs4[i] == '\t' || text_ucs4[i-1] == '\t' || - embedding_levels[i] != embedding_levels[i-1] || - analysis->shape_engine != last_analysis->shape_engine || - analysis->lang_engine != last_analysis->lang_engine || - analysis->font != last_analysis->font || - analysis->language != last_analysis->language || - analysis->extra_attrs != last_analysis->extra_attrs) + update_embedding_end (state); + } + + if (state->run_end == state->attr_end) + { + pango_attr_iterator_next (state->attr_iter); + update_attr_iterator (state); + } + + if (state->run_end == state->script_end) + { + pango_script_iter_next (state->script_iter); + pango_script_iter_get_range (state->script_iter, NULL, + &state->script_end, &state->script); + state->changed |= SCRIPT_CHANGED; + } + + update_end (state); + + return TRUE; +} + +static GSList * +copy_attr_slist (GSList *attr_slist) +{ + GSList *new_list = NULL; + GSList *l; + + for (l = attr_slist; l; l = l->next) + new_list = g_slist_prepend (new_list, pango_attribute_copy (l->data)); + + return g_slist_reverse (new_list); +} + +static void +itemize_state_fill_shaper (ItemizeState *state, + PangoEngineShape *shape_engine, + PangoFont *font) +{ + GList *l; + + for (l = state->result; l; l = l->next) + { + PangoItem *item = l->data; + if (item->analysis.shape_engine) + break; + item->analysis.font = g_object_ref (font); + item->analysis.shape_engine = shape_engine; + } +} + +static void +itemize_state_add_character (ItemizeState *state, + PangoEngineShape *shape_engine, + PangoFont *font, + gboolean force_break, + const char *pos) +{ + if (state->item) + { + if (!force_break && + state->item->analysis.lang_engine == state->lang_engine && + state->item->analysis.shape_engine == shape_engine && + state->item->analysis.font == font) { - /* assert that previous item got at least one char */ - g_assert (item == NULL || item->length > 0); - g_assert (item == NULL || item->num_chars > 0); - - item = pango_item_new (); - item->offset = p - text; - item->num_chars = 0; - item->analysis.level = embedding_levels[i]; - - item->analysis.shape_engine = analysis->shape_engine; - item->analysis.lang_engine = analysis->lang_engine; - - item->analysis.font = analysis->font; - item->analysis.language = analysis->language; - - /* Copy the extra attribute list if necessary */ - if (analysis->extra_attrs && i != 0 && analysis->extra_attrs == last_analysis->extra_attrs) - { - GSList *tmp_list = analysis->extra_attrs; - GSList *new_list = NULL; - while (tmp_list) - { - new_list = g_slist_prepend (new_list, - pango_attribute_copy (tmp_list->data)); - tmp_list = tmp_list->next; - } - item->analysis.extra_attrs = g_slist_reverse (new_list); - } - else - item->analysis.extra_attrs = analysis->extra_attrs; - - result = g_list_prepend (result, item); + state->item->num_chars++; + return; } - else - g_object_unref (analysis->font); - item->length = (next - text) - item->offset; - item->num_chars++; - p = next; - } + state->item->length = (pos - state->text) - state->item->offset; - g_free (analyses); - g_free (embedding_levels); - g_free (text_ucs4); + if (!state->item->analysis.shape_engine && shape_engine) + { + itemize_state_fill_shaper (state, shape_engine, font); + } + else if (state->item->analysis.shape_engine && !shape_engine) + { + font = state->item->analysis.font; + shape_engine = state->item->analysis.shape_engine; + } + } + + state->item = pango_item_new (); + state->item->offset = pos - state->text; + state->item->length = 0; + state->item->num_chars = 1; + state->item->analysis.shape_engine = shape_engine; + state->item->analysis.lang_engine = state->lang_engine; + + if (font) + g_object_ref (font); + state->item->analysis.font = font; + + state->item->analysis.level = state->embedding; + state->item->analysis.language = state->derived_lang; - return g_list_reverse (result); + if (state->copy_extra_attrs) + { + state->item->analysis.extra_attrs = copy_attr_slist (state->extra_attrs); + } + else + { + state->item->analysis.extra_attrs = state->extra_attrs; + state->copy_extra_attrs = TRUE; + } + + state->result = g_list_prepend (state->result, state->item); +} + +static void +get_engines (PangoContext *context, + PangoLanguage *lang, + PangoScript script, + GSList **exact_engines, + GSList **fallback_engines) +{ + const char *engine_type = pango_font_map_get_shape_engine_type (context->font_map); + PangoMap *shaper_map = pango_find_map (lang, + g_quark_from_string (PANGO_ENGINE_TYPE_SHAPE), + g_quark_from_string (engine_type)); + pango_map_get_engines (shaper_map, script, + exact_engines, fallback_engines); } +typedef struct { + PangoLanguage *lang; + gunichar wc; + GSList *engines; + PangoEngineShape *shape_engine; + PangoFont *font; +} GetShaperFontInfo; + static gboolean -advance_iterator_to (PangoAttrIterator *iterator, - int start_index) +get_shaper_and_font_foreach (PangoFontset *fontset, + PangoFont *font, + gpointer data) { - int start_range, end_range; - - pango_attr_iterator_range (iterator, &start_range, &end_range); + GetShaperFontInfo *info = data; + GSList *l; - while (start_index >= end_range) + for (l = info->engines; l; l = l->next) { - if (!pango_attr_iterator_next (iterator)) - return FALSE; - pango_attr_iterator_range (iterator, &start_range, &end_range); + PangoEngineShape *engine = l->data; + PangoCoverageLevel level; + + level = _pango_engine_shape_covers (engine, font, + info->lang, info->wc); + if (level != PANGO_COVERAGE_NONE) + { + info->shape_engine = engine; + info->font = g_object_ref (font); + return TRUE; + } } - if (start_range > start_index) - g_warning ("In pango_itemize(), the cached iterator passed in " - "had already moved beyond the start_index"); + return FALSE; +} - return TRUE; +static gboolean +get_shaper_and_font (ItemizeState *state, + gunichar wc, + PangoEngineShape **shape_engine, + PangoFont **font) +{ + GetShaperFontInfo info; + + if (shaper_font_cache_get (state->cache, wc, shape_engine, font)) + return *shape_engine != NULL; + + if (!state->exact_engines && !state->fallback_engines) + get_engines (state->context, state->derived_lang, state->script, + &state->exact_engines, &state->fallback_engines); + + info.lang = state->derived_lang; + info.wc = wc; + info.shape_engine = NULL; + info.font = NULL; + + info.engines = state->exact_engines; + pango_fontset_foreach (state->current_fonts, get_shaper_and_font_foreach, &info); + if (info.shape_engine) + { + *shape_engine = info.shape_engine; + *font = info.font; + + shaper_font_cache_insert (state->cache, wc, *shape_engine, *font); + + return TRUE; + } + + info.engines = state->fallback_engines; + pango_fontset_foreach (state->current_fonts, get_shaper_and_font_foreach, &info); + + *shape_engine = info.shape_engine; + *font = info.font; + + shaper_font_cache_insert (state->cache, wc, *shape_engine, *font); + + return *shape_engine != NULL; } static void -add_engines (PangoContext *context, - const gchar *text, - gint start_index, - gint length, - PangoAttrList *attrs, - PangoAttrIterator *cached_iter, - gint n_chars, - PangoAnalysis *analyses) -{ - const char *pos; - PangoLanguage *language = NULL; - int next_index; - GSList *extra_attrs = NULL; - PangoMap *lang_map = NULL; - PangoFontDescription *current_desc = NULL; - PangoFontset *current_fonts = NULL; - PangoAttrIterator *iterator; - gboolean first_iteration = TRUE; - gunichar wc; - int i = 0; +itemize_state_reset_shape_engines (ItemizeState *state) +{ + g_slist_free (state->exact_engines); + state->exact_engines = NULL; + g_slist_free (state->fallback_engines); + state->fallback_engines = NULL; +} - if (cached_iter) - iterator = cached_iter; +static PangoLanguage * +compute_derived_language (PangoLanguage *lang, + PangoScript script) +{ + PangoLanguage *derived_lang; + + /* Make sure the language tag is consistent with the derived + * script. There is no point in marking up a section of + * Arabic text with the "en" language tag. + */ + if (lang && pango_language_includes_script (lang, script)) + derived_lang = lang; else - iterator = pango_attr_list_get_iterator (attrs); + { + derived_lang = pango_script_get_sample_language (script); + if (!derived_lang) + derived_lang = lang; + } - advance_iterator_to (iterator, start_index); - - pango_attr_iterator_range (iterator, NULL, &next_index); + return derived_lang; +} + +static PangoMap * +get_lang_map (PangoLanguage *lang) +{ + static guint engine_type_id = 0; + static guint render_type_id = 0; - pos = text + start_index; - for (i=0; i<n_chars; i++) + if (engine_type_id == 0) { - PangoAnalysis *analysis = &analyses[i]; - - if (first_iteration || pos - text == next_index) - { - PangoLanguage *next_language; - PangoFontDescription *next_desc = pango_font_description_copy_static (context->font_desc); - - first_iteration = FALSE; - - /* Only advance the iterator if we've exhausted a range, - * not on the first iteration. - */ - if (pos - text == next_index) - { - pango_attr_iterator_next (iterator); - pango_attr_iterator_range (iterator, NULL, &next_index); - } - - pango_attr_iterator_get_font (iterator, next_desc, &next_language, &extra_attrs); - - if (!next_language) - next_language = context->language; - - if (i == 0 || language != next_language) - { - static guint engine_type_id = 0; - static guint render_type_id = 0; - - if (engine_type_id == 0) - { - engine_type_id = g_quark_from_static_string (PANGO_ENGINE_TYPE_LANG); - render_type_id = g_quark_from_static_string (PANGO_RENDER_TYPE_NONE); - } + engine_type_id = g_quark_from_static_string (PANGO_ENGINE_TYPE_LANG); + render_type_id = g_quark_from_static_string (PANGO_RENDER_TYPE_NONE); + } - lang_map = pango_find_map (next_language, - engine_type_id, render_type_id); - } - - if (i == 0 || - language != next_language || - !pango_font_description_equal (current_desc, next_desc)) - { - pango_font_description_free (current_desc); - current_desc = next_desc; - language = next_language; - - if (current_fonts) - g_object_unref (current_fonts); - - current_fonts = pango_font_map_load_fontset (context->font_map, - context, - current_desc, - language); - } - else - pango_font_description_free (next_desc); - } - - wc = g_utf8_get_char (pos); - pos = g_utf8_next_char (pos); - - analysis->lang_engine = (PangoEngineLang *)pango_map_get_engine (lang_map, wc); - analysis->font = pango_fontset_get_font (current_fonts, wc); - analysis->language = language; + return pango_find_map (lang, engine_type_id, render_type_id); +} + +static void +itemize_state_update_for_new_run (ItemizeState *state) +{ + if (state->changed & (SCRIPT_CHANGED | LANG_CHANGED)) + { + PangoLanguage *old_derived_lang = state->derived_lang; + state->derived_lang = compute_derived_language (state->lang, state->script); + if (old_derived_lang != state->derived_lang) + state->changed |= DERIVED_LANG_CHANGED; + } - /* FIXME: handle reference counting properly on the shapers */ - if (analysis->font) - analysis->shape_engine = pango_font_find_shaper (analysis->font, language, wc); + if ((state->changed & DERIVED_LANG_CHANGED) || !state->lang_engine) + { + PangoMap *lang_map = get_lang_map (state->derived_lang); + state->lang_engine = (PangoEngineLang *)pango_map_get_engine (lang_map, state->script); + } + + if (state->changed & (SCRIPT_CHANGED | DERIVED_LANG_CHANGED)) + itemize_state_reset_shape_engines (state); + + if (state->changed & (FONT_CHANGED | DERIVED_LANG_CHANGED) && + state->current_fonts) + { + g_object_unref (state->current_fonts); + state->current_fonts = NULL; + state->cache = NULL; + } + + if (!state->current_fonts) + { + state->current_fonts = pango_font_map_load_fontset (state->context->font_map, + state->context, + state->font_desc, + state->derived_lang); + state->cache = get_shaper_font_cache (state->current_fonts); + } +} + +static void +itemize_state_process_run (ItemizeState *state) +{ + const char *p; + gboolean last_was_tab = FALSE; + + itemize_state_update_for_new_run (state); + + for (p = state->run_start; + p < state->run_end; + p = g_utf8_next_char (p)) + { + gunichar wc = g_utf8_get_char (p); + gboolean is_tab = wc == '\t'; + PangoEngineShape *shape_engine; + PangoFont *font; + + if (!g_unichar_isgraph (wc)) + { + shape_engine = NULL; + font = NULL; + } else - analysis->shape_engine = NULL; + get_shaper_and_font (state, wc, &shape_engine, &font); + + itemize_state_add_character (state, + shape_engine, font, + is_tab || last_was_tab, + p); - if (analysis->shape_engine == NULL) - analysis->shape_engine = _pango_get_fallback_shaper (); + last_was_tab = is_tab; + } + + /* Finish the final item from the current segment */ + state->item->length = (p - state->text) - state->item->offset; + if (!state->item->analysis.shape_engine) + { + PangoEngineShape *shape_engine; + PangoFont *font; - analysis->extra_attrs = extra_attrs; + if (!get_shaper_and_font (state, ' ', &shape_engine, &font)) + { + shape_engine = _pango_get_fallback_shaper (); + font = NULL; + } + + itemize_state_fill_shaper (state, shape_engine, font); } + state->item = NULL; +} + +static void +itemize_state_finish (ItemizeState *state) +{ + g_free (state->embedding_levels); + if (state->free_attr_iter) + pango_attr_iterator_destroy (state->attr_iter); + pango_script_iter_free (state->script_iter); + pango_font_description_free (state->font_desc); + + itemize_state_reset_shape_engines (state); + if (state->current_fonts) + g_object_unref (state->current_fonts); +} + +/** + * pango_itemize: + * @context: a structure holding information that affects + the itemization process. + * @text: the text to itemize. + * @start_index: first byte in @text to process + * @length: the number of bytes (not characters) to process + * after @start_index. + * This must be >= 0. + * @attrs: the set of attributes that apply to @text. + * @cached_iter: Cached attribute iterator, or NULL + * + * Breaks a piece of text into segments with consistent + * directional level and shaping engine. Each byte of @text will + * be contained in exactly one of the items in the returned list; + * the generated list of items will be in logical order (the start + * offsets of the items are ascending). + * + * @cached_iter should be an iterator over @attrs currently positioned at a + * range before or containing @start_index; @cached_iter will be advanced to + * the range covering the position just after @start_index + @length. + * (i.e. if itemizing in a loop, just keep passing in the same @cached_iter). + * + * Return value: a GList of PangoItem structures. + */ +GList * +pango_itemize (PangoContext *context, + const char *text, + int start_index, + int length, + PangoAttrList *attrs, + PangoAttrIterator *cached_iter) +{ + ItemizeState state; + + g_return_val_if_fail (context != NULL, NULL); + g_return_val_if_fail (start_index >= 0, NULL); + g_return_val_if_fail (length >= 0, NULL); + g_return_val_if_fail (length == 0 || text != NULL, NULL); - g_assert (pos - text == start_index + length); + if (length == 0) + return NULL; - if (current_desc) - pango_font_description_free (current_desc); - if (current_fonts) - g_object_unref (current_fonts); + itemize_state_init (&state, context, text, start_index, length, + attrs, cached_iter); - if (iterator != cached_iter) - pango_attr_iterator_destroy (iterator); + do + itemize_state_process_run (&state); + while (itemize_state_next (&state)); + + itemize_state_finish (&state); + + return g_list_reverse (state.result); } /** |