diff options
author | Owen Taylor <otaylor@redhat.com> | 2004-06-23 19:00:36 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2004-06-23 19:00:36 +0000 |
commit | 3aabb8d5ce0720c9a22cdcf975380d1d0213c4c8 (patch) | |
tree | b6a641821de5c6e8643fb869c3950cd9d167ed61 /pango/pango-glyph-item.c | |
parent | 1939a027b1d4e6c7d83af9e42e191f1bfb0cf8a0 (diff) | |
download | pango-3aabb8d5ce0720c9a22cdcf975380d1d0213c4c8.tar.gz |
Add new letter_spacing attribute.
Wed Jun 23 11:17:51 2004 Owen Taylor <otaylor@redhat.com>
* pango/pango-attributes.[ch]: Add new letter_spacing
attribute.
* pango/pango-attributes.c (pango_attr_rise_new): Correct
description; rise is in Pango units, not em-relative.
* pango/pango-glyph-item.c: Break out iteration-over-clusters
from ApplyAttrsState into a separate GlyphItemIter.
* pango/pango-glyph-item.[ch]: New function
pango_glyph_item_letter_space() to add add letter spacing
to a single glyph item.
* pango/pango-markup.c: Add a letter_spacing attribute.
* pango/pango-layout.c: Use G_DEFINE_TYPE().
* pango/pango-layout.c (pango_layout_get_item_properties):
Switch to use a structure rather than a pile of out
parameters.
* pango/pango-layout.c (pango_run_get_extents): Remove
the unused shape_set out parameter.
* pango/pangofc-decoder.[ch] pango/pangofc-fontmap.[ch]:
Doc fixes.
* pango/pango-types.h: Deprecate pango_get_mirror_char()
* pango/pango-utils.c (pango_get_mirror_char): Add docs.
* docs/pango-sections.txt docs/pango-docs.sgml: Add
PangoFcDecoder and letter spacing.
Diffstat (limited to 'pango/pango-glyph-item.c')
-rw-r--r-- | pango/pango-glyph-item.c | 246 |
1 files changed, 166 insertions, 80 deletions
diff --git a/pango/pango-glyph-item.c b/pango/pango-glyph-item.c index 087f0fa6..b62bf840 100644 --- a/pango/pango-glyph-item.c +++ b/pango/pango-glyph-item.c @@ -44,6 +44,8 @@ * it internally) * * Return value: new item representing text before @split_index + * + * Since: 1.2 **/ PangoGlyphItem * pango_glyph_item_split (PangoGlyphItem *orig, @@ -124,62 +126,41 @@ pango_glyph_item_split (PangoGlyphItem *orig, return new; } -/* Structure holding state when we're iterating over a GlyphItem for - * pango_glyph_item_apply_attrs(). cluster_start/cluster_end (and - * range_start/range_end in apply_attrs()) are offsets into the - * text, so note the difference of glyph_item->item->offset between - * them and clusters in the log_clusters[] array. +/* Structure holding state when we're iterating over a GlyphItem. + start_index/cluster_end (and range_start/range_end in + apply_attrs()) are offsets into the text, so note the difference + of glyph_item->item->offset between them and clusters in the + log_clusters[] array. */ -typedef struct +typedef struct { PangoGlyphItem *glyph_item; const gchar *text; - int glyph_index; - int cluster_start; - int cluster_end; - - GSList *segment_attrs; -} ApplyAttrsState; - -/* Tack @attrs onto the attributes of glyph_item - */ -static void -append_attrs (PangoGlyphItem *glyph_item, - GSList *attrs) -{ - glyph_item->item->analysis.extra_attrs = - g_slist_concat (glyph_item->item->analysis.extra_attrs, attrs); -} - -/* Make a deep copy of a GSlist of PangoAttribute - */ -static GSList * -attr_slist_copy (GSList *attrs) -{ - GSList *tmp_list; - GSList *new_attrs; - - new_attrs = g_slist_copy (attrs); - - for (tmp_list = new_attrs; tmp_list; tmp_list = tmp_list->next) - tmp_list->data = pango_attribute_copy (tmp_list->data); + int start_glyph; + int start_index; + int start_char; - return new_attrs; -} + int end_glyph; + int end_index; + int end_char; +} GlyphItemIter; -/* Advance to the next logical cluster +/* Advance to the next cluster, returns FALSE if + * we were already on the last cluster */ static gboolean -next_cluster (ApplyAttrsState *state) +glyph_item_iter_next_cluster (GlyphItemIter *iter) { - int glyph_index = state->glyph_index; - PangoGlyphString *glyphs = state->glyph_item->glyphs; - PangoItem *item = state->glyph_item->item; + int glyph_index = iter->end_glyph; + PangoGlyphString *glyphs = iter->glyph_item->glyphs; + PangoItem *item = iter->glyph_item->item; - state->cluster_start = state->cluster_end; + iter->start_glyph = iter->end_glyph; + iter->start_index = iter->end_index; + iter->start_char = iter->end_char; - if (LTR (state->glyph_item)) + if (LTR (iter->glyph_item)) { if (glyph_index == glyphs->num_glyphs) return FALSE; @@ -190,13 +171,16 @@ next_cluster (ApplyAttrsState *state) if (glyph_index == glyphs->num_glyphs) { - state->cluster_end = item->offset + item->length; + iter->end_index = item->offset + item->length; + iter->end_char = item->num_chars; break; } - if (item->offset + glyphs->log_clusters[glyph_index] >= state->cluster_start) + if (item->offset + glyphs->log_clusters[glyph_index] >= iter->start_index) { - state->cluster_end = item->offset + glyphs->log_clusters[glyph_index]; + iter->end_index = item->offset + glyphs->log_clusters[glyph_index]; + iter->end_char += g_utf8_strlen (iter->text + iter->start_index, + iter->end_index - iter->start_index); break; } } @@ -212,37 +196,100 @@ next_cluster (ApplyAttrsState *state) if (glyph_index < 0) { - state->cluster_end = item->offset + item->length; + iter->end_index = item->offset + item->length; + iter->end_char = item->num_chars; break; } - if (item->offset + glyphs->log_clusters[glyph_index] >= state->cluster_start) + if (item->offset + glyphs->log_clusters[glyph_index] >= iter->start_index) { - state->cluster_end = item->offset + glyphs->log_clusters[glyph_index]; + iter->end_index = item->offset + glyphs->log_clusters[glyph_index]; + iter->end_char += g_utf8_strlen (iter->text + iter->start_index, + iter->end_index - iter->start_index); break; } } } - state->glyph_index = glyph_index; + iter->end_glyph = glyph_index; return TRUE; } +/* Returns FALSE if there are no clusters in the glyph item */ +static gboolean +glyph_item_iter_init (GlyphItemIter *iter, + PangoGlyphItem *glyph_item, + const char *text) +{ + iter->glyph_item = glyph_item; + iter->text = text; + + if (LTR (glyph_item)) + iter->end_glyph = 0; + else + iter->end_glyph = glyph_item->glyphs->num_glyphs - 1; + + iter->end_index = glyph_item->item->offset; + iter->end_char = 0; + + /* Advance onto the first cluster of the glyph item */ + return glyph_item_iter_next_cluster (iter); +} + +typedef struct +{ + GlyphItemIter iter; + + GSList *segment_attrs; +} ApplyAttrsState; + +/* Tack @attrs onto the attributes of glyph_item + */ +static void +append_attrs (PangoGlyphItem *glyph_item, + GSList *attrs) +{ + glyph_item->item->analysis.extra_attrs = + g_slist_concat (glyph_item->item->analysis.extra_attrs, attrs); +} + +/* Make a deep copy of a GSlist of PangoAttribute + */ +static GSList * +attr_slist_copy (GSList *attrs) +{ + GSList *tmp_list; + GSList *new_attrs; + + new_attrs = g_slist_copy (attrs); + + for (tmp_list = new_attrs; tmp_list; tmp_list = tmp_list->next) + tmp_list->data = pango_attribute_copy (tmp_list->data); + + return new_attrs; +} + /* Split the glyph item at the start of the current cluster */ static PangoGlyphItem * split_before_cluster_start (ApplyAttrsState *state) { PangoGlyphItem *split_item; - int split_len = state->cluster_start - state->glyph_item->item->offset; + int split_len = state->iter.start_index - state->iter.glyph_item->item->offset; - split_item = pango_glyph_item_split (state->glyph_item, state->text, split_len); + split_item = pango_glyph_item_split (state->iter.glyph_item, state->iter.text, split_len); append_attrs (split_item, state->segment_attrs); /* Adjust iteration to account for the split */ - if (LTR (state->glyph_item)) - state->glyph_index -= split_item->glyphs->num_glyphs; + if (LTR (state->iter.glyph_item)) + { + state->iter.start_glyph -= split_item->glyphs->num_glyphs; + state->iter.end_glyph -= split_item->glyphs->num_glyphs; + } + + state->iter.start_char -= split_item->item->num_chars; + state->iter.end_char -= split_item->item->num_chars; return split_item; } @@ -273,6 +320,8 @@ split_before_cluster_start (ApplyAttrsState *state) * Return value: a list of glyph items resulting from splitting * @glyph_item. Free the elements using pango_glyph_item_free(), * the list using g_slist_free(). + * + * Since: 1.2 **/ GSList * pango_glyph_item_apply_attrs (PangoGlyphItem *glyph_item, @@ -283,6 +332,7 @@ pango_glyph_item_apply_attrs (PangoGlyphItem *glyph_item, GSList *result = NULL; ApplyAttrsState state; gboolean start_new_segment = FALSE; + gboolean have_cluster; int range_start, range_end; /* This routine works by iterating through the item cluster by @@ -290,30 +340,20 @@ pango_glyph_item_apply_attrs (PangoGlyphItem *glyph_item, * add to the next output item, and decide when to split * off an output item based on two criteria: * - * A) If cluster_start < attribute_start < cluster_end + * A) If start_index < attribute_start < end_index * (attribute starts within cluster) then we need * to split between the last cluster and this cluster. - * B) If cluster_start < attribute_end <= cluster_end, + * B) If start_index < attribute_end <= end_index, * (attribute ends within cluster) then we need to * split between this cluster and the next one. */ - state.glyph_item = glyph_item; - state.text = text; - - if (LTR (glyph_item)) - state.glyph_index = 0; - else - state.glyph_index = glyph_item->glyphs->num_glyphs - 1; - - state.cluster_end = glyph_item->item->offset; - /* Advance the attr iterator to the start of the item */ while (TRUE) { pango_attr_iterator_range (iter, &range_start, &range_end); - if (range_end > state.cluster_end) + if (range_end > glyph_item->item->offset) break; pango_attr_iterator_next (iter); @@ -327,14 +367,15 @@ pango_glyph_item_apply_attrs (PangoGlyphItem *glyph_item, if (range_start <= glyph_item->item->offset && range_end >= glyph_item->item->offset + glyph_item->item->length) goto out; - - while (TRUE) + + for (have_cluster = glyph_item_iter_init (&state.iter, glyph_item, text); + have_cluster; + have_cluster = glyph_item_iter_next_cluster (&state.iter)) { - /* Find the next logical cluster; [range_start,range_end] - * is the first range that intersects the new cluster. + + /* [range_start,range_end] is the first range that intersects + * the current cluster. */ - if (!next_cluster (&state)) - break; /* Split item into two, if this cluster isn't a continuation * of the last cluster @@ -357,29 +398,29 @@ pango_glyph_item_apply_attrs (PangoGlyphItem *glyph_item, /* If any ranges end in this cluster, then the next cluster * goes into a separate segment */ - if (range_end <= state.cluster_end) + if (range_end <= state.iter.end_index) start_new_segment = TRUE; - if (range_end > state.cluster_end) /* Range intersects next cluster */ + if (range_end > state.iter.end_index) /* Range intersects next cluster */ break; pango_attr_iterator_next (iter); pango_attr_iterator_range (iter, &range_start, &range_end); - if (range_start >= state.cluster_end) /* New range doesn't intersect this cluster */ + if (range_start >= state.iter.end_index) /* New range doesn't intersect this cluster */ { /* No gap between ranges, so previous range must of ended * at cluster boundary. */ - g_assert (range_start == state.cluster_end && start_new_segment); + g_assert (range_start == state.iter.end_index && start_new_segment); break; } /* If any ranges start *inside* this cluster, then we need * to split the previous cluster into a separate segment */ - if (range_start > state.cluster_start && - state.cluster_start != glyph_item->item->offset) + if (range_start > state.iter.start_index && + state.iter.start_index != glyph_item->item->offset) { GSList *new_attrs = attr_slist_copy (state.segment_attrs); result = g_slist_prepend (result, @@ -390,6 +431,9 @@ pango_glyph_item_apply_attrs (PangoGlyphItem *glyph_item, state.segment_attrs = g_slist_concat (state.segment_attrs, pango_attr_iterator_get_attrs (iter)); } + + if (!glyph_item_iter_next_cluster (&state.iter)) + break; } out: @@ -405,3 +449,45 @@ pango_glyph_item_apply_attrs (PangoGlyphItem *glyph_item, return result; } + +/** + * pango_glyph_item_letter_space: + * @glyph_item: a #PangoGlyphItem + * @text: text that @glyph_item corresponds to + * (glyph_item->item->offset is an offset from the + * start of @text) + * @log_attrs: logical attributes for the item (the + * first logical attribute refers to the position + * before the first character in the item) + * @letter_spacing: amount of letter spacing to add + * in Pango units. May be negative, though too large + * negative values will give ugly results. + * + * Adds spacing between the graphemes of @glyph_item to + * give the effect of typographic letter spacing.b + * + * Since: 1.6 + **/ +void +pango_glyph_item_letter_space (PangoGlyphItem *glyph_item, + const char *text, + PangoLogAttr *log_attrs, + int letter_spacing) +{ + GlyphItemIter iter; + gboolean have_cluster; + + for (have_cluster = glyph_item_iter_init (&iter, glyph_item, text); + have_cluster; + have_cluster = glyph_item_iter_next_cluster (&iter)) + { + if (iter.start_char > 0 && + log_attrs[iter.start_char].is_cursor_position) + { + if (iter.start_glyph < iter.end_glyph) /* LTR */ + glyph_item->glyphs->glyphs[iter.start_glyph - 1].geometry.width += letter_spacing; + else /* RTL */ + glyph_item->glyphs->glyphs[iter.start_glyph].geometry.width += letter_spacing; + } + } +} |