diff options
Diffstat (limited to 'pango2/pango-item.c')
-rw-r--r-- | pango2/pango-item.c | 652 |
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; +} |