summaryrefslogtreecommitdiff
path: root/pango
diff options
context:
space:
mode:
Diffstat (limited to 'pango')
-rw-r--r--pango/pango-glyph-item.c255
1 files changed, 226 insertions, 29 deletions
diff --git a/pango/pango-glyph-item.c b/pango/pango-glyph-item.c
index a941f650..a18dd0d9 100644
--- a/pango/pango-glyph-item.c
+++ b/pango/pango-glyph-item.c
@@ -24,6 +24,8 @@
#include "pango-glyph-item.h"
+#define LTR(glyph_item) (((glyph_item)->item->analysis.level % 2) == 0)
+
/**
* pango_glyph_item_split:
* @orig: a #PangoItem
@@ -53,14 +55,13 @@ pango_glyph_item_split (PangoGlyphItem *orig,
int num_glyphs;
int num_remaining;
int split_offset;
- gboolean ltr = (orig->item->analysis.level % 2) == 0;
g_return_val_if_fail (orig != NULL, NULL);
g_return_val_if_fail (orig->item->length > 0, NULL);
g_return_val_if_fail (split_index > 0, NULL);
g_return_val_if_fail (split_index < orig->item->length, NULL);
- if (ltr)
+ if (LTR (orig))
{
for (i = 0; i < orig->glyphs->num_glyphs; i++)
{
@@ -99,7 +100,7 @@ pango_glyph_item_split (PangoGlyphItem *orig,
new->glyphs = pango_glyph_string_new ();
pango_glyph_string_set_size (new->glyphs, num_glyphs);
- if (ltr)
+ if (LTR (orig))
{
memcpy (new->glyphs->glyphs, orig->glyphs->glyphs, num_glyphs * sizeof (PangoGlyphInfo));
memcpy (new->glyphs->log_clusters, orig->glyphs->log_clusters, num_glyphs * sizeof (int));
@@ -123,6 +124,26 @@ 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.
+ */
+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)
@@ -131,6 +152,101 @@ append_attrs (PangoGlyphItem *glyph_item,
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;
+}
+
+/* Advance to the next logical cluster
+ */
+static gboolean
+next_cluster (ApplyAttrsState *state)
+{
+ int glyph_index = state->glyph_index;
+ PangoGlyphString *glyphs = state->glyph_item->glyphs;
+ PangoItem *item = state->glyph_item->item;
+
+ state->cluster_start = state->cluster_end;
+
+ if (LTR (state->glyph_item))
+ {
+ if (glyph_index == glyphs->num_glyphs)
+ return FALSE;
+
+ while (TRUE)
+ {
+ glyph_index++;
+
+ if (glyph_index == glyphs->num_glyphs)
+ {
+ state->cluster_end = item->offset + item->length;
+ break;
+ }
+
+ if (item->offset + glyphs->log_clusters[glyph_index] >= state->cluster_start)
+ {
+ state->cluster_end = item->offset + glyphs->log_clusters[glyph_index];
+ break;
+ }
+ }
+ }
+ else /* RTL */
+ {
+ if (glyph_index < 0)
+ return FALSE;
+
+ while (TRUE)
+ {
+ glyph_index--;
+
+ if (glyph_index < 0)
+ {
+ state->cluster_end = item->offset + item->length;
+ break;
+ }
+
+ if (item->offset + glyphs->log_clusters[glyph_index] >= state->cluster_start)
+ {
+ state->cluster_end = item->offset + glyphs->log_clusters[glyph_index];
+ break;
+ }
+ }
+ }
+
+ state->glyph_index = glyph_index;
+ return TRUE;
+}
+
+/* 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;
+
+ split_item = pango_glyph_item_split (state->glyph_item, state->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;
+
+ return split_item;
+}
+
/**
* pango_glyph_item_apply_attrs:
* @glyph_item: a shaped item
@@ -144,6 +260,13 @@ append_attrs (PangoGlyphItem *glyph_item,
* apply the shaping process and then reapply them to the result using
* this function.
*
+ * All attributes that start or end inside a cluster are applied
+ * to that cluster; for instance, if half of a cluster is underlined
+ * and the other-half strikethough, then the cluster will end
+ * up with both underline and strikethrough attributes. In these
+ * cases, it may happen that item->extra_attrs for some of the
+ * result items can have multiple attributes of the same type.
+ *
* This function takes ownership of @glyph_item; it will be reused
* as one of the elements in the list.
*
@@ -157,51 +280,125 @@ pango_glyph_item_apply_attrs (PangoGlyphItem *glyph_item,
PangoAttrList *list)
{
PangoAttrIterator *iter = pango_attr_list_get_iterator (list);
- PangoGlyphItem *new;
GSList *result = NULL;
- int start;
- int end;
+ ApplyAttrsState state;
+ gboolean start_new_segment = FALSE;
+ int range_start, range_end;
- gboolean ltr = (glyph_item->item->analysis.level % 2) == 0;
+ /* This routine works by iterating through the item cluster by
+ * cluster; we accumulate the attributes that we need to
+ * 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
+ * (attribute starts within cluster) then we need
+ * to split between the last cluster and this cluster.
+ * B) If cluster_start < attribute_end <= cluster_end,
+ * (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)
+ break;
+
+ g_assert (pango_attr_iterator_next (iter));
+ }
+ /* Short circuit the case when we don't actually need to
+ * split the item
+ */
+ if (range_start <= glyph_item->item->offset &&
+ range_end >= glyph_item->item->offset + glyph_item->item->length)
+ goto out;
+
+ state.segment_attrs = pango_attr_iterator_get_attrs (iter);
+
while (TRUE)
{
- pango_attr_iterator_range (iter, &start, &end);
+ /* Find the next logical cluster; [range_start,range_end]
+ * is the first range that intersects the new cluster.
+ */
+ if (!next_cluster (&state))
+ break;
- if (start > glyph_item->item->offset)
+ /* Split item into two, if this cluster isn't a continuation
+ * of the last cluster
+ */
+ if (start_new_segment)
{
- if (start >= glyph_item->item->offset + glyph_item->item->length)
- break;
-
- new = pango_glyph_item_split (glyph_item, text,
- start - glyph_item->item->offset);
-
- result = g_slist_prepend (result, new);
+ result = g_slist_prepend (result,
+ split_before_cluster_start (&state));
+ state.segment_attrs = pango_attr_iterator_get_attrs (iter);
}
+
+ start_new_segment = FALSE;
- if (end > glyph_item->item->offset)
+ /* Loop over all ranges that intersect this cluster; exiting
+ * leaving [range_start,range_end] being the first range that
+ * intersects the next cluster.
+ */
+ while (TRUE)
{
- if (end >= glyph_item->item->offset + glyph_item->item->length)
+ /* If any ranges end in this cluster, then the next cluster
+ * goes into a separate segment
+ */
+ if (range_end <= state.cluster_end)
+ start_new_segment = TRUE;
+
+ if (range_end > state.cluster_end) /* Range intersects next cluster */
+ break;
+
+ g_assert (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 */
{
- append_attrs (glyph_item, pango_attr_iterator_get_attrs (iter));
+ /* No gap between ranges, so previous range must of ended
+ * at cluster boundary.
+ */
+ g_assert (range_start == state.cluster_end && start_new_segment);
break;
}
- new = pango_glyph_item_split (glyph_item, text,
- end - glyph_item->item->offset);
-
- append_attrs (new, pango_attr_iterator_get_attrs (iter));
-
- result = g_slist_prepend (result, new);
+ /* 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)
+ {
+ GSList *new_attrs = attr_slist_copy (state.segment_attrs);
+ result = g_slist_prepend (result,
+ split_before_cluster_start (&state));
+ state.segment_attrs = new_attrs;
+ }
+
+ state.segment_attrs = g_slist_concat (state.segment_attrs,
+ pango_attr_iterator_get_attrs (iter));
}
-
- if (!pango_attr_iterator_next (iter))
- break;
}
+ out:
+ /* What's left in glyph_item is the remaining portion
+ */
+ append_attrs (glyph_item, state.segment_attrs);
result = g_slist_prepend (result, glyph_item);
- if (ltr)
+ if (LTR (glyph_item))
result = g_slist_reverse (result);
pango_attr_iterator_destroy (iter);