summaryrefslogtreecommitdiff
path: root/pango2/pango-item.c
diff options
context:
space:
mode:
Diffstat (limited to 'pango2/pango-item.c')
-rw-r--r--pango2/pango-item.c652
1 files changed, 652 insertions, 0 deletions
diff --git a/pango2/pango-item.c b/pango2/pango-item.c
new file mode 100644
index 00000000..97b0a2b9
--- /dev/null
+++ b/pango2/pango-item.c
@@ -0,0 +1,652 @@
+/* Pango2
+ * pango-item.c: Single run handling
+ *
+ * Copyright (C) 2000 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 "pango-attributes.h"
+#include "pango-attr-private.h"
+#include "pango-item-private.h"
+#include "pango-impl-utils.h"
+
+/**
+ * Pango2Analysis:
+ *
+ * The `Pango2Analysis` structure stores information about
+ * the properties of a segment of text.
+ */
+
+/**
+ * Pango2Item:
+ *
+ * The `Pango2Item` structure stores information about
+ * a segment of text.
+ *
+ * You typically obtain `Pango2Items` by itemizing a piece
+ * of text with [func@Pango2.itemize].
+ */
+
+/**
+ * pango2_item_new:
+ *
+ * Creates a new `Pango2Item` structure initialized to default values.
+ *
+ * Return value: the newly allocated `Pango2Item`, which should
+ * be freed with [method@Pango2.Item.free].
+ */
+Pango2Item *
+pango2_item_new (void)
+{
+ Pango2Item *result = g_slice_new0 (Pango2Item);
+
+ return (Pango2Item *)result;
+}
+
+/**
+ * pango2_item_copy:
+ * @item: (nullable): a `Pango2Item`
+ *
+ * Copy an existing `Pango2Item` structure.
+ *
+ * Return value: (nullable): the newly allocated `Pango2Item`
+ */
+Pango2Item *
+pango2_item_copy (Pango2Item *item)
+{
+ GSList *extra_attrs, *tmp_list;
+ Pango2Item *result;
+
+ if (item == NULL)
+ return NULL;
+
+ result = pango2_item_new ();
+
+ result->offset = item->offset;
+ result->length = item->length;
+ result->num_chars = item->num_chars;
+ result->char_offset = item->char_offset;
+
+ result->analysis = item->analysis;
+ if (result->analysis.size_font)
+ g_object_ref (result->analysis.size_font);
+
+ if (result->analysis.font)
+ g_object_ref (result->analysis.font);
+
+ extra_attrs = NULL;
+ tmp_list = item->analysis.extra_attrs;
+ while (tmp_list)
+ {
+ extra_attrs = g_slist_prepend (extra_attrs, pango2_attribute_copy (tmp_list->data));
+ tmp_list = tmp_list->next;
+ }
+
+ result->analysis.extra_attrs = g_slist_reverse (extra_attrs);
+
+ return result;
+}
+
+/**
+ * pango2_item_free:
+ * @item: (nullable): a `Pango2Item`, may be %NULL
+ *
+ * Free a `Pango2Item` and all associated memory.
+ **/
+void
+pango2_item_free (Pango2Item *item)
+{
+ if (item == NULL)
+ return;
+
+ if (item->analysis.extra_attrs)
+ {
+ g_slist_foreach (item->analysis.extra_attrs, (GFunc)pango2_attribute_destroy, NULL);
+ g_slist_free (item->analysis.extra_attrs);
+ }
+
+ if (item->analysis.size_font)
+ g_object_unref (item->analysis.size_font);
+
+ if (item->analysis.font)
+ g_object_unref (item->analysis.font);
+
+ g_slice_free (Pango2Item, item);
+}
+
+G_DEFINE_BOXED_TYPE (Pango2Item, pango2_item,
+ pango2_item_copy,
+ pango2_item_free);
+
+/**
+ * pango2_item_split:
+ * @orig: a `Pango2Item`
+ * @split_index: byte index of position to split item, relative to the
+ * start of the item
+ * @split_offset: number of chars between start of @orig and @split_index
+ *
+ * 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).
+ * @split_offset is the length of the first item in chars, and must be
+ * provided because the text used to generate the item isn't available,
+ * so `pango2_item_split()` can't count the char length of the split items
+ * itself.
+ *
+ * Return value: new item representing text before @split_index, which
+ * should be freed with [method@Pango2.Item.free].
+ */
+Pango2Item *
+pango2_item_split (Pango2Item *orig,
+ int split_index,
+ int split_offset)
+{
+ Pango2Item *new_item;
+
+ g_return_val_if_fail (orig != NULL, NULL);
+ g_return_val_if_fail (split_index > 0, NULL);
+ g_return_val_if_fail (split_index < orig->length, NULL);
+ g_return_val_if_fail (split_offset > 0, NULL);
+ g_return_val_if_fail (split_offset < orig->num_chars, NULL);
+
+ new_item = pango2_item_copy (orig);
+ new_item->length = split_index;
+ new_item->num_chars = split_offset;
+
+ orig->offset += split_index;
+ orig->length -= split_index;
+ orig->num_chars -= split_offset;
+ orig->char_offset += split_offset;
+
+ return new_item;
+}
+
+/*< private >
+ * pango2_item_unsplit:
+ * @orig: the item to unsplit
+ * @split_index: value passed to pango2_item_split()
+ * @split_offset: value passed to pango2_item_split()
+ *
+ * Undoes the effect of a pango2_item_split() call with
+ * the same arguments.
+ *
+ * You are expected to free the new item that was returned
+ * by pango2_item_split() yourself.
+ */
+void
+pango2_item_unsplit (Pango2Item *orig,
+ int split_index,
+ int split_offset)
+{
+ orig->offset -= split_index;
+ orig->length += split_index;
+ orig->num_chars += split_offset;
+ orig->char_offset -= split_offset;
+}
+
+static int
+compare_attr (gconstpointer p1, gconstpointer p2)
+{
+ const Pango2Attribute *a1 = p1;
+ const Pango2Attribute *a2 = p2;
+ if (pango2_attribute_equal (a1, a2) &&
+ a1->start_index == a2->start_index &&
+ a1->end_index == a2->end_index)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * pango2_item_apply_attrs:
+ * @item: a `Pango2Item`
+ * @iter: a `Pango2AttrIterator`
+ *
+ * Add attributes to a `Pango2Item`.
+ *
+ * The idea is that you have attributes that don't affect itemization,
+ * such as font features, so you filter them out using
+ * [method@Pango2.AttrList.filter], itemize your text, then reapply the
+ * attributes to the resulting items using this function.
+ *
+ * The @iter should be positioned before the range of the item,
+ * and will be advanced past it. This function is meant to be called
+ * in a loop over the items resulting from itemization, while passing
+ * the iter to each call.
+ */
+void
+pango2_item_apply_attrs (Pango2Item *item,
+ Pango2AttrIterator *iter)
+{
+ int start, end;
+ GSList *attrs = NULL;
+
+ do
+ {
+ pango2_attr_iterator_range (iter, &start, &end);
+
+ if (start >= item->offset + item->length)
+ break;
+
+ if (end >= item->offset)
+ {
+ GSList *list, *l;
+
+ list = pango2_attr_iterator_get_attrs (iter);
+ for (l = list; l; l = l->next)
+ {
+ if (!g_slist_find_custom (attrs, l->data, compare_attr))
+
+ attrs = g_slist_prepend (attrs, pango2_attribute_copy (l->data));
+ }
+ g_slist_free_full (list, (GDestroyNotify)pango2_attribute_destroy);
+ }
+
+ if (end >= item->offset + item->length)
+ break;
+ }
+ while (pango2_attr_iterator_next (iter));
+
+ item->analysis.extra_attrs = g_slist_concat (item->analysis.extra_attrs, attrs);
+}
+
+void
+pango2_analysis_collect_features (const Pango2Analysis *analysis,
+ hb_feature_t *features,
+ guint length,
+ guint *num_features)
+{
+ GSList *l;
+
+ if (PANGO2_IS_HB_FONT (analysis->font))
+ {
+ const hb_feature_t *font_features;
+ guint n_font_features;
+
+ font_features = pango2_hb_font_get_features (PANGO2_HB_FONT (analysis->font),
+ &n_font_features);
+ *num_features = MIN (length, n_font_features);
+ memcpy (features, font_features, sizeof (hb_feature_t) * *num_features);
+ }
+
+ for (l = analysis->extra_attrs; l && *num_features < length; l = l->next)
+ {
+ Pango2Attribute *attr = l->data;
+ if (attr->type == PANGO2_ATTR_FONT_FEATURES)
+ {
+ const char *feat;
+ const char *end;
+ int len;
+
+ feat = attr->str_value;
+
+ while (feat != NULL && *num_features < length)
+ {
+ end = strchr (feat, ',');
+ if (end)
+ len = end - feat;
+ else
+ len = -1;
+ if (hb_feature_from_string (feat, len, &features[*num_features]))
+ {
+ features[*num_features].start = attr->start_index;
+ features[*num_features].end = attr->end_index;
+ (*num_features)++;
+ }
+
+ if (end == NULL)
+ break;
+
+ feat = end + 1;
+ }
+ }
+ }
+
+ /* Turn off ligatures when letterspacing */
+ for (l = analysis->extra_attrs; l && *num_features < length; l = l->next)
+ {
+ Pango2Attribute *attr = l->data;
+ if (attr->type == PANGO2_ATTR_LETTER_SPACING)
+ {
+ hb_tag_t tags[] = {
+ HB_TAG('l','i','g','a'),
+ HB_TAG('c','l','i','g'),
+ HB_TAG('d','l','i','g'),
+ HB_TAG('h','l','i','g'),
+ };
+ int i;
+ for (i = 0; i < G_N_ELEMENTS (tags); i++)
+ {
+ features[*num_features].tag = tags[i];
+ features[*num_features].value = 0;
+ features[*num_features].start = attr->start_index;
+ features[*num_features].end = attr->end_index;
+ (*num_features)++;
+ }
+ }
+ }
+}
+
+/*< private >
+ * pango2_analysis_set_size_font:
+ * @analysis: a `Pango2Analysis`
+ * @font: a `Pango2Font`
+ *
+ * Sets the font to use for determining the line height.
+ *
+ * This is used when scaling fonts for emulated Small Caps,
+ * to preserve the original line height.
+ */
+void
+pango2_analysis_set_size_font (Pango2Analysis *analysis,
+ Pango2Font *font)
+{
+ if (analysis->size_font)
+ g_object_unref (analysis->size_font);
+ analysis->size_font = font;
+ if (analysis->size_font)
+ g_object_ref (analysis->size_font);
+}
+
+/*< private >
+ * pango2_analysis_get_size_font:
+ * @analysis: a `Pango2Analysis`
+ *
+ * Gets the font to use for determining line height.
+ *
+ * If this returns `NULL`, use analysis->font.
+ *
+ * Returns: (nullable) (transfer none): the font
+ */
+Pango2Font *
+pango2_analysis_get_size_font (const Pango2Analysis *analysis)
+{
+ return analysis->size_font;
+}
+
+/*< private >
+ * pango2_item_get_properties:
+ * @item: a `Pango2Item`
+ * @properties: `ItemProperties` struct to populate
+ *
+ * Extract useful information from the @item's attributes.
+ *
+ * Note that letter-spacing and shape are required to be constant
+ * across items. But underline and strikethrough can vary across
+ * an item, so we collect all the values that we find.
+ */
+void
+pango2_item_get_properties (Pango2Item *item,
+ ItemProperties *properties)
+{
+ GSList *tmp_list = item->analysis.extra_attrs;
+
+ properties->uline_style = PANGO2_LINE_STYLE_NONE;
+ properties->uline_position = PANGO2_UNDERLINE_POSITION_NORMAL;
+ properties->oline_style = PANGO2_LINE_STYLE_NONE;
+ properties->strikethrough_style = PANGO2_LINE_STYLE_NONE;
+ properties->showing_space = FALSE;
+ properties->no_paragraph_break = FALSE;
+ properties->letter_spacing = 0;
+ properties->line_height = 0.0;
+ properties->absolute_line_height = 0;
+ properties->line_spacing = 0;
+ properties->shape = NULL;
+
+ while (tmp_list)
+ {
+ Pango2Attribute *attr = tmp_list->data;
+
+ switch ((int) attr->type)
+ {
+ case PANGO2_ATTR_UNDERLINE:
+ properties->uline_style = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_UNDERLINE_POSITION:
+ properties->uline_position = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_OVERLINE:
+ properties->oline_style = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_STRIKETHROUGH:
+ properties->strikethrough_style = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_LETTER_SPACING:
+ properties->letter_spacing = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_LINE_HEIGHT:
+ properties->line_height = attr->double_value;
+ break;
+
+ case PANGO2_ATTR_ABSOLUTE_LINE_HEIGHT:
+ properties->absolute_line_height = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_LINE_SPACING:
+ properties->line_spacing = attr->int_value;
+ break;
+
+ case PANGO2_ATTR_SHOW:
+ properties->showing_space = (attr->int_value & PANGO2_SHOW_SPACES) != 0;
+ break;
+
+ case PANGO2_ATTR_PARAGRAPH:
+ properties->no_paragraph_break = TRUE;
+ break;
+
+ case PANGO2_ATTR_SHAPE:
+ properties->shape = attr;
+ break;
+
+ default:
+ break;
+ }
+ tmp_list = tmp_list->next;
+ }
+}
+
+/**
+ * pango2_analysis_get_font:
+ * @analysis: a `Pango2Analysis`
+ *
+ * Returns the font that will be used for text
+ * with this `Pango2Analysis`.
+ *
+ * Return value: (transfer none): the `Pango2Font`
+ */
+Pango2Font *
+pango2_analysis_get_font (const Pango2Analysis *analysis)
+{
+ return analysis->font;
+}
+
+/**
+ * pango2_analysis_get_bidi_level:
+ * @analysis: a `Pango2Analysis`
+ *
+ * Returns the bidi embedding level for text
+ * with this `Pango2Analysis`.
+ *
+ * Return value: the bidi embedding level
+ */
+int
+pango2_analysis_get_bidi_level (const Pango2Analysis *analysis)
+{
+ return analysis->level;
+}
+
+/**
+ * pango2_analysis_get_gravity:
+ * @analysis: a `Pango2Analysis`
+ *
+ * Returns the gravity for text with this `Pango2Analysis`.
+ *
+ * Return value: the gravity
+ */
+Pango2Gravity
+pango2_analysis_get_gravity (const Pango2Analysis *analysis)
+{
+ return (Pango2Gravity) analysis->gravity;
+}
+
+/**
+ * pango2_analysis_get_flags:
+ * @analysis: a `Pango2Analysis`
+ *
+ * Returns flags for this `Pango2Analysis`.
+ *
+ * Possible flag values are
+ * `PANGO2_ANALYSIS_FLAG_CENTERED_BASELINE`,
+ * `PANGO2_ANALYSIS_FLAG_IS_ELLIPSIS` and
+ * `PANGO2_ANALYSIS_FLAG_NEED_HYPHEN`.
+ *
+ * Return value: the flags
+ */
+guint
+pango2_analysis_get_flags (const Pango2Analysis *analysis)
+{
+ return analysis->flags;
+}
+
+/**
+ * pango2_analysis_get_script:
+ * @analysis: a `Pango2Analysis`
+ *
+ * Returns the script for text with this `Pango2Analysis`.
+ *
+ * Return value: the script
+ */
+GUnicodeScript
+pango2_analysis_get_script (const Pango2Analysis *analysis)
+{
+ return (GUnicodeScript) analysis->script;
+}
+
+/**
+ * pango2_analysis_get_language:
+ * @analysis: a `Pango2Analysis`
+ *
+ * Returns the language for text with this `Pango2Analysis`.
+ *
+ * Return value: the script
+ */
+Pango2Language *
+pango2_analysis_get_language (const Pango2Analysis *analysis)
+{
+ return analysis->language;
+}
+
+/**
+ * pango2_analysis_get_extra_attributes:
+ * @analysis: a `Pango2Analysis`
+ *
+ * Returns attributes to apply to text with this
+ * `Pango2Analysis`.
+ *
+ * Return value: (transfer none) (element-type Pango2Attribute):
+ * a `GSList` with `Pango2Attribute` values
+ */
+GSList *
+pango2_analysis_get_extra_attributes (const Pango2Analysis *analysis)
+{
+ return analysis->extra_attrs;
+}
+
+/**
+ * pango2_item_get_analysis:
+ * @item: a `Pango2Item`
+ *
+ * Returns the `Pango2Analysis` of @item.
+ *
+ * Return value: (transfer none): a `Pango2Analysis`
+ */
+const Pango2Analysis *
+pango2_item_get_analysis (Pango2Item *item)
+{
+ return &item->analysis;
+}
+
+/**
+ * pango2_item_get_byte_offset:
+ * @item: a `Pango2Item`
+ *
+ * Returns the byte offset of this items
+ * text in the overall paragraph text.
+ *
+ * Return value: the byte offset
+ */
+int
+pango2_item_get_byte_offset (Pango2Item *item)
+{
+ return item->offset;
+}
+
+/**
+ * pango2_item_get_byte_length:
+ * @item: a `Pango2Item`
+ *
+ * Returns the length of this items
+ * text in bytes.
+ *
+ * Return value: the length of @item
+ */
+int
+pango2_item_get_byte_length (Pango2Item *item)
+{
+ return item->length;
+}
+
+/**
+ * pango2_item_get_char_offset:
+ * @item: a `Pango2Item`
+ *
+ * Returns the offset of this items text
+ * in the overall paragraph text, in characters.
+ *
+ * Returns value: the character offset
+ */
+int
+pango2_item_get_char_offset (Pango2Item *item)
+{
+ return item->char_offset;
+}
+
+/**
+ * pango2_item_get_char_length:
+ * @item: a `Pango2Item`
+ *
+ * Returns the number of characters in this
+ * items text.
+ *
+ * Return value: the number of characters in @item
+ */
+int
+pango2_item_get_char_length (Pango2Item *item)
+{
+ return item->num_chars;
+}