1/* Pango
2 * pango-item.c: Single run handling
3 *
4 * Copyright (C) 2000 Red Hat Software
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22#include "config.h"
23#include "pango-attributes.h"
24#include "pango-item-private.h"
25#include "pango-impl-utils.h"
26
27/**
28 * pango_item_new:
29 *
30 * Creates a new `PangoItem` structure initialized to default values.
31 *
32 * Return value: the newly allocated `PangoItem`, which should
33 * be freed with [method@Pango.Item.free].
34 */
35PangoItem *
36pango_item_new (void)
37{
38 PangoItemPrivate *result = g_slice_new0 (PangoItemPrivate);
39
40 result->analysis.flags |= PANGO_ANALYSIS_FLAG_HAS_CHAR_OFFSET;
41
42 return (PangoItem *)result;
43}
44
45/**
46 * pango_item_copy:
47 * @item: (nullable): a `PangoItem`
48 *
49 * Copy an existing `PangoItem` structure.
50 *
51 * Return value: (nullable): the newly allocated `PangoItem`
52 */
53PangoItem *
54pango_item_copy (PangoItem *item)
55{
56 GSList *extra_attrs, *tmp_list;
57 PangoItem *result;
58
59 if (item == NULL)
60 return NULL;
61
62 result = pango_item_new ();
63
64 result->offset = item->offset;
65 result->length = item->length;
66 result->num_chars = item->num_chars;
67 if (item->analysis.flags & PANGO_ANALYSIS_FLAG_HAS_CHAR_OFFSET)
68 ((PangoItemPrivate *)result)->char_offset = ((PangoItemPrivate *)item)->char_offset;
69
70 result->analysis = item->analysis;
71 if (result->analysis.lang_engine)
72 g_object_ref (result->analysis.lang_engine);
73
74 if (result->analysis.font)
75 g_object_ref (result->analysis.font);
76
77 extra_attrs = NULL;
78 tmp_list = item->analysis.extra_attrs;
79 while (tmp_list)
80 {
81 extra_attrs = g_slist_prepend (list: extra_attrs, data: pango_attribute_copy (attr: tmp_list->data));
82 tmp_list = tmp_list->next;
83 }
84
85 result->analysis.extra_attrs = g_slist_reverse (list: extra_attrs);
86
87 return result;
88}
89
90/**
91 * pango_item_free:
92 * @item: (nullable): a `PangoItem`, may be %NULL
93 *
94 * Free a `PangoItem` and all associated memory.
95 **/
96void
97pango_item_free (PangoItem *item)
98{
99 if (item == NULL)
100 return;
101
102 if (item->analysis.extra_attrs)
103 {
104 g_slist_foreach (list: item->analysis.extra_attrs, func: (GFunc)pango_attribute_destroy, NULL);
105 g_slist_free (list: item->analysis.extra_attrs);
106 }
107
108 if (item->analysis.lang_engine)
109 g_object_unref (object: item->analysis.lang_engine);
110
111 if (item->analysis.font)
112 g_object_unref (object: item->analysis.font);
113
114 if (item->analysis.flags & PANGO_ANALYSIS_FLAG_HAS_CHAR_OFFSET)
115 g_slice_free (PangoItemPrivate, (PangoItemPrivate *)item);
116 else
117 g_slice_free (PangoItem, item);
118}
119
120G_DEFINE_BOXED_TYPE (PangoItem, pango_item,
121 pango_item_copy,
122 pango_item_free);
123
124/**
125 * pango_item_split:
126 * @orig: a `PangoItem`
127 * @split_index: byte index of position to split item, relative to the
128 * start of the item
129 * @split_offset: number of chars between start of @orig and @split_index
130 *
131 * Modifies @orig to cover only the text after @split_index, and
132 * returns a new item that covers the text before @split_index that
133 * used to be in @orig.
134 *
135 * You can think of @split_index as the length of the returned item.
136 * @split_index may not be 0, and it may not be greater than or equal
137 * to the length of @orig (that is, there must be at least one byte
138 * assigned to each item, you can't create a zero-length item).
139 * @split_offset is the length of the first item in chars, and must be
140 * provided because the text used to generate the item isn't available,
141 * so `pango_item_split()` can't count the char length of the split items
142 * itself.
143 *
144 * Return value: new item representing text before @split_index, which
145 * should be freed with [method@Pango.Item.free].
146 */
147PangoItem *
148pango_item_split (PangoItem *orig,
149 int split_index,
150 int split_offset)
151{
152 PangoItem *new_item;
153
154 g_return_val_if_fail (orig != NULL, NULL);
155 g_return_val_if_fail (split_index > 0, NULL);
156 g_return_val_if_fail (split_index < orig->length, NULL);
157 g_return_val_if_fail (split_offset > 0, NULL);
158 g_return_val_if_fail (split_offset < orig->num_chars, NULL);
159
160 new_item = pango_item_copy (item: orig);
161 new_item->length = split_index;
162 new_item->num_chars = split_offset;
163
164 orig->offset += split_index;
165 orig->length -= split_index;
166 orig->num_chars -= split_offset;
167 if (orig->analysis.flags & PANGO_ANALYSIS_FLAG_HAS_CHAR_OFFSET)
168 ((PangoItemPrivate *)orig)->char_offset += split_offset;
169
170 return new_item;
171}
172
173/*< private >
174 * pango_item_unsplit:
175 * @orig: the item to unsplit
176 * @split_index: value passed to pango_item_split()
177 * @split_offset: value passed to pango_item_split()
178 *
179 * Undoes the effect of a pango_item_split() call with
180 * the same arguments.
181 *
182 * You are expected to free the new item that was returned
183 * by pango_item_split() yourself.
184 */
185void
186pango_item_unsplit (PangoItem *orig,
187 int split_index,
188 int split_offset)
189{
190 orig->offset -= split_index;
191 orig->length += split_index;
192 orig->num_chars += split_offset;
193
194 if (orig->analysis.flags & PANGO_ANALYSIS_FLAG_HAS_CHAR_OFFSET)
195 ((PangoItemPrivate *)orig)->char_offset -= split_offset;
196}
197
198static int
199compare_attr (gconstpointer p1, gconstpointer p2)
200{
201 const PangoAttribute *a1 = p1;
202 const PangoAttribute *a2 = p2;
203 if (pango_attribute_equal (attr1: a1, attr2: a2) &&
204 a1->start_index == a2->start_index &&
205 a1->end_index == a2->end_index)
206 return 0;
207
208 return 1;
209}
210
211/**
212 * pango_item_apply_attrs:
213 * @item: a `PangoItem`
214 * @iter: a `PangoAttrIterator`
215 *
216 * Add attributes to a `PangoItem`.
217 *
218 * The idea is that you have attributes that don't affect itemization,
219 * such as font features, so you filter them out using
220 * [method@Pango.AttrList.filter], itemize your text, then reapply the
221 * attributes to the resulting items using this function.
222 *
223 * The @iter should be positioned before the range of the item,
224 * and will be advanced past it. This function is meant to be called
225 * in a loop over the items resulting from itemization, while passing
226 * the iter to each call.
227 *
228 * Since: 1.44
229 */
230void
231pango_item_apply_attrs (PangoItem *item,
232 PangoAttrIterator *iter)
233{
234 int start, end;
235 GSList *attrs = NULL;
236
237 do
238 {
239 pango_attr_iterator_range (iterator: iter, start: &start, end: &end);
240
241 if (start >= item->offset + item->length)
242 break;
243
244 if (end >= item->offset)
245 {
246 GSList *list, *l;
247
248 list = pango_attr_iterator_get_attrs (iterator: iter);
249 for (l = list; l; l = l->next)
250 {
251 if (!g_slist_find_custom (list: attrs, data: l->data, func: compare_attr))
252
253 attrs = g_slist_prepend (list: attrs, data: pango_attribute_copy (attr: l->data));
254 }
255 g_slist_free_full (list, free_func: (GDestroyNotify)pango_attribute_destroy);
256 }
257
258 if (end >= item->offset + item->length)
259 break;
260 }
261 while (pango_attr_iterator_next (iterator: iter));
262
263 item->analysis.extra_attrs = g_slist_concat (list1: item->analysis.extra_attrs, list2: attrs);
264}
265
266void
267pango_analysis_collect_features (const PangoAnalysis *analysis,
268 hb_feature_t *features,
269 guint length,
270 guint *num_features)
271{
272 GSList *l;
273
274 pango_font_get_features (font: analysis->font, features, len: length, num_features);
275
276 for (l = analysis->extra_attrs; l && *num_features < length; l = l->next)
277 {
278 PangoAttribute *attr = l->data;
279 if (attr->klass->type == PANGO_ATTR_FONT_FEATURES)
280 {
281 PangoAttrFontFeatures *fattr = (PangoAttrFontFeatures *) attr;
282 const gchar *feat;
283 const gchar *end;
284 int len;
285
286 feat = fattr->features;
287
288 while (feat != NULL && *num_features < length)
289 {
290 end = strchr (s: feat, c: ',');
291 if (end)
292 len = end - feat;
293 else
294 len = -1;
295 if (hb_feature_from_string (str: feat, len, feature: &features[*num_features]))
296 {
297 features[*num_features].start = attr->start_index;
298 features[*num_features].end = attr->end_index;
299 (*num_features)++;
300 }
301
302 if (end == NULL)
303 break;
304
305 feat = end + 1;
306 }
307 }
308 }
309
310 /* Turn off ligatures when letterspacing */
311 for (l = analysis->extra_attrs; l && *num_features < length; l = l->next)
312 {
313 PangoAttribute *attr = l->data;
314 if (attr->klass->type == PANGO_ATTR_LETTER_SPACING)
315 {
316 hb_tag_t tags[] = {
317 HB_TAG('l','i','g','a'),
318 HB_TAG('c','l','i','g'),
319 HB_TAG('d','l','i','g'),
320 HB_TAG('h','l','i','g'),
321 };
322 int i;
323 for (i = 0; i < G_N_ELEMENTS (tags); i++)
324 {
325 features[*num_features].tag = tags[i];
326 features[*num_features].value = 0;
327 features[*num_features].start = attr->start_index;
328 features[*num_features].end = attr->end_index;
329 (*num_features)++;
330 }
331 }
332 }
333}
334
335/*< private >
336 * pango_analysis_set_size_font:
337 * @analysis: a `PangoAnalysis`
338 * @font: a `PangoFont`
339 *
340 * Sets the font to use for determining the line height.
341 *
342 * This is used when scaling fonts for emulated Small Caps,
343 * to preserve the original line height.
344 */
345void
346pango_analysis_set_size_font (PangoAnalysis *analysis,
347 PangoFont *font)
348{
349 PangoAnalysisPrivate *priv = (PangoAnalysisPrivate *)analysis;
350
351 if (priv->size_font)
352 g_object_unref (object: priv->size_font);
353 priv->size_font = font;
354 if (priv->size_font)
355 g_object_ref (priv->size_font);
356}
357
358/*< private >
359 * pango_analysis_get_size_font:
360 * @analysis: a `PangoAnalysis`
361 *
362 * Gets the font to use for determining line height.
363 *
364 * If this returns `NULL`, use analysis->font.
365 *
366 * Returns: (nullable) (transfer none): the font
367 */
368PangoFont *
369pango_analysis_get_size_font (const PangoAnalysis *analysis)
370{
371 PangoAnalysisPrivate *priv = (PangoAnalysisPrivate *)analysis;
372
373 return priv->size_font;
374}
375

source code of gtk/subprojects/pango/pango/pango-item.c