summaryrefslogtreecommitdiff
path: root/pango/pango-glyph-item.c
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@redhat.com>2004-06-23 19:00:36 +0000
committerOwen Taylor <otaylor@src.gnome.org>2004-06-23 19:00:36 +0000
commit3aabb8d5ce0720c9a22cdcf975380d1d0213c4c8 (patch)
treeb6a641821de5c6e8643fb869c3950cd9d167ed61 /pango/pango-glyph-item.c
parent1939a027b1d4e6c7d83af9e42e191f1bfb0cf8a0 (diff)
downloadpango-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.c246
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;
+ }
+ }
+}