summaryrefslogtreecommitdiff
path: root/trunk/pango/pango-glyph-item.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/pango/pango-glyph-item.c')
-rw-r--r--trunk/pango/pango-glyph-item.c664
1 files changed, 664 insertions, 0 deletions
diff --git a/trunk/pango/pango-glyph-item.c b/trunk/pango/pango-glyph-item.c
new file mode 100644
index 00000000..157bb150
--- /dev/null
+++ b/trunk/pango/pango-glyph-item.c
@@ -0,0 +1,664 @@
+/* Pango
+ * pango-glyph-item.c: Pair of PangoItem and a glyph string
+ *
+ * Copyright (C) 2002 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "pango-glyph-item.h"
+#include "pango-glyph-item-private.h"
+
+#define LTR(glyph_item) (((glyph_item)->item->analysis.level % 2) == 0)
+
+/**
+ * pango_glyph_item_split:
+ * @orig: a #PangoItem
+ * @text: text to which positions in @orig apply
+ * @split_index: byte index of position to split item, relative to the start of the item
+ *
+ * Modifies @orig to cover only the text after @split_index, and
+ * returns a new item that covers the text before @split_index that
+ * used to be in @orig. You can think of @split_index as the length of
+ * the returned item. @split_index may not be 0, and it may not be
+ * greater than or equal to the length of @orig (that is, there must
+ * be at least one byte assigned to each item, you can't create a
+ * zero-length item).
+ *
+ * This function is similar in function to pango_item_split() (and uses
+ * it internally.)
+ *
+ * Return value: the newly allocated item representing text before
+ * @split_index, which should be freed
+ * with pango_glyph_item_free().
+ *
+ * Since: 1.2
+ **/
+PangoGlyphItem *
+pango_glyph_item_split (PangoGlyphItem *orig,
+ const char *text,
+ int split_index)
+{
+ PangoGlyphItem *new;
+ int i;
+ int num_glyphs;
+ int num_remaining;
+ int split_offset;
+
+ 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 (orig))
+ {
+ for (i = 0; i < orig->glyphs->num_glyphs; i++)
+ {
+ if (orig->glyphs->log_clusters[i] >= split_index)
+ break;
+ }
+
+ if (i == orig->glyphs->num_glyphs) /* No splitting necessary */
+ return NULL;
+
+ split_index = orig->glyphs->log_clusters[i];
+ num_glyphs = i;
+ }
+ else
+ {
+ for (i = orig->glyphs->num_glyphs - 1; i >= 0; i--)
+ {
+ if (orig->glyphs->log_clusters[i] >= split_index)
+ break;
+ }
+
+ if (i < 0) /* No splitting necessary */
+ return NULL;
+
+ split_index = orig->glyphs->log_clusters[i];
+ num_glyphs = orig->glyphs->num_glyphs - 1 - i;
+ }
+
+ num_remaining = orig->glyphs->num_glyphs - num_glyphs;
+
+ new = g_slice_new (PangoGlyphItem);
+ split_offset = g_utf8_pointer_to_offset (text + orig->item->offset,
+ text + orig->item->offset + split_index);
+ new->item = pango_item_split (orig->item, split_index, split_offset);
+
+ new->glyphs = pango_glyph_string_new ();
+ pango_glyph_string_set_size (new->glyphs, num_glyphs);
+
+ 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));
+
+ memmove (orig->glyphs->glyphs, orig->glyphs->glyphs + num_glyphs,
+ num_remaining * sizeof (PangoGlyphInfo));
+ for (i = num_glyphs; i < orig->glyphs->num_glyphs; i++)
+ orig->glyphs->log_clusters[i - num_glyphs] = orig->glyphs->log_clusters[i] - split_index;
+ }
+ else
+ {
+ memcpy (new->glyphs->glyphs, orig->glyphs->glyphs + num_remaining, num_glyphs * sizeof (PangoGlyphInfo));
+ memcpy (new->glyphs->log_clusters, orig->glyphs->log_clusters + num_remaining, num_glyphs * sizeof (int));
+
+ for (i = 0; i < num_remaining; i++)
+ orig->glyphs->log_clusters[i] = orig->glyphs->log_clusters[i] - split_index;
+ }
+
+ pango_glyph_string_set_size (orig->glyphs, orig->glyphs->num_glyphs - num_glyphs);
+
+ return new;
+}
+
+/**
+ * pango_glyph_item_free:
+ * @glyph_item: a #PangoGlyphItem
+ *
+ * Frees a #PangoGlyphItem and memory to which it points.
+ *
+ * Since: 1.6
+ **/
+void
+pango_glyph_item_free (PangoGlyphItem *glyph_item)
+{
+ if (glyph_item->item)
+ pango_item_free (glyph_item->item);
+ if (glyph_item->glyphs)
+ pango_glyph_string_free (glyph_item->glyphs);
+
+ g_slice_free (PangoGlyphItem, glyph_item);
+}
+
+/**
+ * _pango_glyph_item_iter_next_cluster:
+ * @iter: a #PangoGlyphItemIter
+ *
+ * Advances the iterator to the next cluster in the glyph item.
+ *
+ * Return value: %TRUE if the iterator was advanced, %FALSE if we were already on the
+ * last cluster.
+ **/
+gboolean
+_pango_glyph_item_iter_next_cluster (PangoGlyphItemIter *iter)
+{
+ int glyph_index = iter->end_glyph;
+ PangoGlyphString *glyphs = iter->glyph_item->glyphs;
+ int cluster;
+ PangoItem *item = iter->glyph_item->item;
+
+ if (LTR (iter->glyph_item))
+ {
+ if (glyph_index == glyphs->num_glyphs)
+ return FALSE;
+ }
+ else
+ {
+ if (glyph_index < 0)
+ return FALSE;
+ }
+
+ iter->start_glyph = iter->end_glyph;
+ iter->start_index = iter->end_index;
+ iter->start_char = iter->end_char;
+
+ if (LTR (iter->glyph_item))
+ {
+ cluster = glyphs->log_clusters[glyph_index];
+ while (TRUE)
+ {
+ glyph_index++;
+
+ if (glyph_index == glyphs->num_glyphs)
+ {
+ iter->end_index = item->offset + item->length;
+ iter->end_char = item->num_chars;
+ break;
+ }
+
+ if (glyphs->log_clusters[glyph_index] != cluster)
+ {
+ 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;
+ }
+ }
+ }
+ else /* RTL */
+ {
+ cluster = glyphs->log_clusters[glyph_index];
+ while (TRUE)
+ {
+ glyph_index--;
+
+ if (glyph_index < 0)
+ {
+ iter->end_index = item->offset + item->length;
+ iter->end_char = item->num_chars;
+ break;
+ }
+
+ if (glyphs->log_clusters[glyph_index] != cluster)
+ {
+ 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;
+ }
+ }
+ }
+
+ iter->end_glyph = glyph_index;
+ return TRUE;
+}
+
+/**
+ * _pango_glyph_item_iter_prev_cluster:
+ * @iter: a #PangoGlyphItemIter
+ *
+ * Moves the iterator to the preceding cluster in the glyph item.
+ *
+ * Return value: %TRUE if the iterator was moved, %FALSE if we were already on the
+ * first cluster.
+ **/
+gboolean
+_pango_glyph_item_iter_prev_cluster (PangoGlyphItemIter *iter)
+{
+ int glyph_index = iter->start_glyph;
+ PangoGlyphString *glyphs = iter->glyph_item->glyphs;
+ int cluster;
+ PangoItem *item = iter->glyph_item->item;
+
+ if (LTR (iter->glyph_item))
+ {
+ if (glyph_index == 0)
+ return FALSE;
+ }
+ else
+ {
+ if (glyph_index == glyphs->num_glyphs - 1)
+ return FALSE;
+
+ }
+
+ iter->end_glyph = iter->start_glyph;
+ iter->end_index = iter->start_index;
+ iter->end_char = iter->start_char;
+
+ if (LTR (iter->glyph_item))
+ {
+ cluster = glyphs->log_clusters[glyph_index - 1];
+ while (TRUE)
+ {
+ glyph_index--;
+
+ if (glyph_index == 0)
+ {
+ iter->start_index = item->offset;
+ iter->start_char = 0;
+ break;
+ }
+
+ if (glyphs->log_clusters[glyph_index] != cluster)
+ {
+ iter->start_index = item->offset + glyphs->log_clusters[glyph_index];
+ iter->start_char -= g_utf8_strlen (iter->text + iter->start_index,
+ iter->end_index - iter->start_index);
+ break;
+ }
+ }
+ }
+ else /* RTL */
+ {
+ cluster = glyphs->log_clusters[glyph_index + 1];
+ while (TRUE)
+ {
+ glyph_index++;
+
+ if (glyph_index == glyphs->num_glyphs - 1)
+ {
+ iter->start_index = item->offset;
+ iter->start_char = 0;
+ break;
+ }
+
+ if (glyphs->log_clusters[glyph_index] != cluster)
+ {
+ iter->start_index = item->offset + glyphs->log_clusters[glyph_index];
+ iter->start_char -= g_utf8_strlen (iter->text + iter->start_index,
+ iter->end_index - iter->start_index);
+ break;
+ }
+ }
+ }
+
+ iter->start_glyph = glyph_index;
+ return TRUE;
+}
+
+/**
+ * _pango_glyph_item_iter_init_start:
+ * @iter: pointer to a #PangoGlyphItemIter structure
+ * @glyph_item: the glyph item that @iter points into
+ * @text: text corresponding to the glyph item
+ *
+ * Initializes a #PangoGlyphItemIter structure to point to the
+ * first cluster in a glyph item.
+ *
+ * Return value: %FALSE if there are no clusters in the glyph item;
+ * in this case, the state of @iter is undefined.
+ **/
+gboolean
+_pango_glyph_item_iter_init_start (PangoGlyphItemIter *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 _pango_glyph_item_iter_next_cluster (iter);
+}
+
+/**
+ * _pango_glyph_item_iter_init_end:
+ * @iter: pointer to a #PangoGlyphItemIter structure
+ * @glyph_item: the glyph item that @iter points into
+ * @text: text corresponding to the glyph item
+ *
+ * Initializes a #PangoGlyphItemIter structure to point to the
+ * last cluster in a glyph item.
+ *
+ * Return value: %FALSE if there are no clusters in the glyph item;
+ * in this case, the state of @iter is undefined.
+ **/
+gboolean
+_pango_glyph_item_iter_init_end (PangoGlyphItemIter *iter,
+ PangoGlyphItem *glyph_item,
+ const char *text)
+{
+ iter->glyph_item = glyph_item;
+ iter->text = text;
+
+ if (LTR (glyph_item))
+ iter->start_glyph = glyph_item->glyphs->num_glyphs;
+ else
+ iter->start_glyph = -1;
+
+ iter->start_index = glyph_item->item->offset + glyph_item->item->length;
+ iter->start_char = glyph_item->item->num_chars;
+
+ /* Advance onto the first cluster of the glyph item */
+ return _pango_glyph_item_iter_prev_cluster (iter);
+}
+
+typedef struct
+{
+ PangoGlyphItemIter 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->iter.start_index - state->iter.glyph_item->item->offset;
+
+ 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->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;
+}
+
+/**
+ * pango_glyph_item_apply_attrs:
+ * @glyph_item: a shaped item
+ * @text: text that @list applies to
+ * @list: a #PangoAttrList
+ *
+ * Splits a shaped item (PangoGlyphItem) into multiple items based
+ * on an attribute list. The idea is that if you have attributes
+ * that don't affect shaping, such as color or underline, to avoid
+ * affecting shaping, you filter them out (pango_attr_list_filter()),
+ * 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 strikethrough, 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.
+ *
+ * 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,
+ const char *text,
+ PangoAttrList *list)
+{
+ PangoAttrIterator *iter = pango_attr_list_get_iterator (list);
+ 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
+ * 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 start_index < attribute_start < end_index
+ * (attribute starts within cluster) then we need
+ * to split between the last cluster and this cluster.
+ * B) If start_index < attribute_end <= end_index,
+ * (attribute ends within cluster) then we need to
+ * split between this cluster and the next one.
+ */
+
+ /* Advance the attr iterator to the start of the item
+ */
+ do
+ {
+ pango_attr_iterator_range (iter, &range_start, &range_end);
+ if (range_end > glyph_item->item->offset)
+ break;
+ }
+ while (pango_attr_iterator_next (iter));
+
+ state.segment_attrs = pango_attr_iterator_get_attrs (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;
+
+ for (have_cluster = _pango_glyph_item_iter_init_start (&state.iter, glyph_item, text);
+ have_cluster;
+ have_cluster = _pango_glyph_item_iter_next_cluster (&state.iter))
+ {
+ gboolean have_next;
+
+ /* [range_start,range_end] is the first range that intersects
+ * the current cluster.
+ */
+
+ /* Split item into two, if this cluster isn't a continuation
+ * of the last cluster
+ */
+ if (start_new_segment)
+ {
+ result = g_slist_prepend (result,
+ split_before_cluster_start (&state));
+ state.segment_attrs = pango_attr_iterator_get_attrs (iter);
+ }
+
+ start_new_segment = FALSE;
+
+ /* Loop over all ranges that intersect this cluster; exiting
+ * leaving [range_start,range_end] being the first range that
+ * intersects the next cluster.
+ */
+ do
+ {
+ if (range_end > state.iter.end_index) /* Range intersects next cluster */
+ break;
+
+ /* Since ranges end in this cluster, the next cluster goes into a
+ * separate segment
+ */
+ start_new_segment = TRUE;
+
+ have_next = pango_attr_iterator_next (iter);
+ pango_attr_iterator_range (iter, &range_start, &range_end);
+
+ 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.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.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,
+ 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));
+ }
+ while (have_next);
+ }
+
+ 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 (glyph_item))
+ result = g_slist_reverse (result);
+
+ pango_attr_iterator_destroy (iter);
+
+ 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.
+ *
+ * Since: 1.6
+ **/
+void
+pango_glyph_item_letter_space (PangoGlyphItem *glyph_item,
+ const char *text,
+ PangoLogAttr *log_attrs,
+ int letter_spacing)
+{
+ PangoGlyphItemIter iter;
+ PangoGlyphInfo *glyphs = glyph_item->glyphs->glyphs;
+ gboolean have_cluster;
+ int space_left, space_right;
+
+ space_left = letter_spacing / 2;
+
+ /* hinting */
+ if ((letter_spacing & (PANGO_SCALE - 1)) == 0)
+ {
+ space_left = PANGO_UNITS_ROUND (space_left);
+ }
+
+ space_right = letter_spacing - space_left;
+
+ for (have_cluster = _pango_glyph_item_iter_init_start (&iter, glyph_item, text);
+ have_cluster;
+ have_cluster = _pango_glyph_item_iter_next_cluster (&iter))
+ {
+ if (!log_attrs[iter.start_char].is_cursor_position)
+ continue;
+
+ if (iter.start_glyph < iter.end_glyph) /* LTR */
+ {
+ if (iter.start_char > 0)
+ {
+ glyphs[iter.start_glyph].geometry.width += space_left ;
+ glyphs[iter.start_glyph].geometry.x_offset += space_left ;
+ }
+ if (iter.end_char < glyph_item->item->num_chars)
+ {
+ glyphs[iter.end_glyph-1].geometry.width += space_right;
+ }
+ }
+ else /* RTL */
+ {
+ if (iter.start_char > 0)
+ {
+ glyphs[iter.start_glyph].geometry.width += space_right;
+ }
+ if (iter.end_char < glyph_item->item->num_chars)
+ {
+ glyphs[iter.end_glyph+1].geometry.x_offset += space_left ;
+ glyphs[iter.end_glyph+1].geometry.width += space_left ;
+ }
+ }
+ }
+}