1/* Pango
2 * pango-attributes.c: Attributed text
3 *
4 * Copyright (C) 2000-2002 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 <string.h>
24
25#include "pango-attributes.h"
26#include "pango-attributes-private.h"
27#include "pango-impl-utils.h"
28
29
30/* {{{ Generic attribute code */
31
32G_LOCK_DEFINE_STATIC (attr_type);
33static GHashTable *name_map = NULL; /* MT-safe */
34
35/**
36 * pango_attr_type_register:
37 * @name: an identifier for the type
38 *
39 * Allocate a new attribute type ID.
40 *
41 * The attribute type name can be accessed later
42 * by using [func@Pango.AttrType.get_name].
43 *
44 * Return value: the new type ID.
45 */
46PangoAttrType
47pango_attr_type_register (const gchar *name)
48{
49 static guint current_type = 0x1000000; /* MT-safe */
50 guint type;
51
52 G_LOCK (attr_type);
53
54 type = current_type++;
55
56 if (name)
57 {
58 if (G_UNLIKELY (!name_map))
59 name_map = g_hash_table_new (NULL, NULL);
60
61 g_hash_table_insert (hash_table: name_map, GUINT_TO_POINTER (type), value: (gpointer) g_intern_string (string: name));
62 }
63
64 G_UNLOCK (attr_type);
65
66 return type;
67}
68
69/**
70 * pango_attr_type_get_name:
71 * @type: an attribute type ID to fetch the name for
72 *
73 * Fetches the attribute type name.
74 *
75 * The attribute type name is the string passed in
76 * when registering the type using
77 * [func@Pango.AttrType.register].
78 *
79 * The returned value is an interned string (see
80 * g_intern_string() for what that means) that should
81 * not be modified or freed.
82 *
83 * Return value: (nullable): the type ID name (which
84 * may be %NULL), or %NULL if @type is a built-in Pango
85 * attribute type or invalid.
86 *
87 * Since: 1.22
88 */
89const char *
90pango_attr_type_get_name (PangoAttrType type)
91{
92 const char *result = NULL;
93
94 G_LOCK (attr_type);
95
96 if (name_map)
97 result = g_hash_table_lookup (hash_table: name_map, GUINT_TO_POINTER ((guint) type));
98
99 G_UNLOCK (attr_type);
100
101 return result;
102}
103
104/**
105 * pango_attribute_init:
106 * @attr: a `PangoAttribute`
107 * @klass: a `PangoAttrClass`
108 *
109 * Initializes @attr's klass to @klass, it's start_index to
110 * %PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING and end_index to
111 * %PANGO_ATTR_INDEX_TO_TEXT_END such that the attribute applies
112 * to the entire text by default.
113 *
114 * Since: 1.20
115 */
116void
117pango_attribute_init (PangoAttribute *attr,
118 const PangoAttrClass *klass)
119{
120 g_return_if_fail (attr != NULL);
121 g_return_if_fail (klass != NULL);
122
123 attr->klass = klass;
124 attr->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
125 attr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
126}
127
128/**
129 * pango_attribute_copy:
130 * @attr: a `PangoAttribute`
131 *
132 * Make a copy of an attribute.
133 *
134 * Return value: (transfer full): the newly allocated
135 * `PangoAttribute`, which should be freed with
136 * [method@Pango.Attribute.destroy].
137 */
138PangoAttribute *
139pango_attribute_copy (const PangoAttribute *attr)
140{
141 PangoAttribute *result;
142
143 g_return_val_if_fail (attr != NULL, NULL);
144
145 result = attr->klass->copy (attr);
146 result->start_index = attr->start_index;
147 result->end_index = attr->end_index;
148
149 return result;
150}
151
152/**
153 * pango_attribute_destroy:
154 * @attr: a `PangoAttribute`.
155 *
156 * Destroy a `PangoAttribute` and free all associated memory.
157 */
158void
159pango_attribute_destroy (PangoAttribute *attr)
160{
161 g_return_if_fail (attr != NULL);
162
163 attr->klass->destroy (attr);
164}
165
166G_DEFINE_BOXED_TYPE (PangoAttribute, pango_attribute,
167 pango_attribute_copy,
168 pango_attribute_destroy);
169
170/**
171 * pango_attribute_equal:
172 * @attr1: a `PangoAttribute`
173 * @attr2: another `PangoAttribute`
174 *
175 * Compare two attributes for equality.
176 *
177 * This compares only the actual value of the two
178 * attributes and not the ranges that the attributes
179 * apply to.
180 *
181 * Return value: %TRUE if the two attributes have the same value
182 */
183gboolean
184pango_attribute_equal (const PangoAttribute *attr1,
185 const PangoAttribute *attr2)
186{
187 g_return_val_if_fail (attr1 != NULL, FALSE);
188 g_return_val_if_fail (attr2 != NULL, FALSE);
189
190 if (attr1->klass->type != attr2->klass->type)
191 return FALSE;
192
193 return attr1->klass->equal (attr1, attr2);
194}
195
196/* }}} */
197/* {{{ Attribute types */
198/* {{{ String attribute */
199static PangoAttribute *pango_attr_string_new (const PangoAttrClass *klass,
200 const char *str);
201
202static PangoAttribute *
203pango_attr_string_copy (const PangoAttribute *attr)
204{
205 return pango_attr_string_new (klass: attr->klass, str: ((PangoAttrString *)attr)->value);
206}
207
208static void
209pango_attr_string_destroy (PangoAttribute *attr)
210{
211 PangoAttrString *sattr = (PangoAttrString *)attr;
212
213 g_free (mem: sattr->value);
214 g_slice_free (PangoAttrString, sattr);
215}
216
217static gboolean
218pango_attr_string_equal (const PangoAttribute *attr1,
219 const PangoAttribute *attr2)
220{
221 return strcmp (s1: ((PangoAttrString *)attr1)->value, s2: ((PangoAttrString *)attr2)->value) == 0;
222}
223
224static PangoAttribute *
225pango_attr_string_new (const PangoAttrClass *klass,
226 const char *str)
227{
228 PangoAttrString *result = g_slice_new (PangoAttrString);
229 pango_attribute_init (attr: &result->attr, klass);
230 result->value = g_strdup (str);
231
232 return (PangoAttribute *)result;
233}
234 /* }}} */
235/* {{{ Language attribute */
236static PangoAttribute *
237pango_attr_language_copy (const PangoAttribute *attr)
238{
239 return pango_attr_language_new (language: ((PangoAttrLanguage *)attr)->value);
240}
241
242static void
243pango_attr_language_destroy (PangoAttribute *attr)
244{
245 PangoAttrLanguage *lattr = (PangoAttrLanguage *)attr;
246
247 g_slice_free (PangoAttrLanguage, lattr);
248}
249
250static gboolean
251pango_attr_language_equal (const PangoAttribute *attr1,
252 const PangoAttribute *attr2)
253{
254 return ((PangoAttrLanguage *)attr1)->value == ((PangoAttrLanguage *)attr2)->value;
255}
256/* }}}} */
257/* {{{ Color attribute */
258static PangoAttribute *pango_attr_color_new (const PangoAttrClass *klass,
259 guint16 red,
260 guint16 green,
261 guint16 blue);
262
263static PangoAttribute *
264pango_attr_color_copy (const PangoAttribute *attr)
265{
266 const PangoAttrColor *color_attr = (PangoAttrColor *)attr;
267
268 return pango_attr_color_new (klass: attr->klass,
269 red: color_attr->color.red,
270 green: color_attr->color.green,
271 blue: color_attr->color.blue);
272}
273
274static void
275pango_attr_color_destroy (PangoAttribute *attr)
276{
277 PangoAttrColor *cattr = (PangoAttrColor *)attr;
278
279 g_slice_free (PangoAttrColor, cattr);
280}
281
282static gboolean
283pango_attr_color_equal (const PangoAttribute *attr1,
284 const PangoAttribute *attr2)
285{
286 const PangoAttrColor *color_attr1 = (const PangoAttrColor *)attr1;
287 const PangoAttrColor *color_attr2 = (const PangoAttrColor *)attr2;
288
289 return (color_attr1->color.red == color_attr2->color.red &&
290 color_attr1->color.blue == color_attr2->color.blue &&
291 color_attr1->color.green == color_attr2->color.green);
292}
293
294static PangoAttribute *
295pango_attr_color_new (const PangoAttrClass *klass,
296 guint16 red,
297 guint16 green,
298 guint16 blue)
299{
300 PangoAttrColor *result = g_slice_new (PangoAttrColor);
301 pango_attribute_init (attr: &result->attr, klass);
302 result->color.red = red;
303 result->color.green = green;
304 result->color.blue = blue;
305
306 return (PangoAttribute *)result;
307}
308/* }}}} */
309/* {{{ Integer attribute */
310static PangoAttribute *pango_attr_int_new (const PangoAttrClass *klass,
311 int value);
312
313static PangoAttribute *
314pango_attr_int_copy (const PangoAttribute *attr)
315{
316 const PangoAttrInt *int_attr = (PangoAttrInt *)attr;
317
318 return pango_attr_int_new (klass: attr->klass, value: int_attr->value);
319}
320
321static void
322pango_attr_int_destroy (PangoAttribute *attr)
323{
324 PangoAttrInt *iattr = (PangoAttrInt *)attr;
325
326 g_slice_free (PangoAttrInt, iattr);
327}
328
329static gboolean
330pango_attr_int_equal (const PangoAttribute *attr1,
331 const PangoAttribute *attr2)
332{
333 const PangoAttrInt *int_attr1 = (const PangoAttrInt *)attr1;
334 const PangoAttrInt *int_attr2 = (const PangoAttrInt *)attr2;
335
336 return (int_attr1->value == int_attr2->value);
337}
338
339static PangoAttribute *
340pango_attr_int_new (const PangoAttrClass *klass,
341 int value)
342{
343 PangoAttrInt *result = g_slice_new (PangoAttrInt);
344 pango_attribute_init (attr: &result->attr, klass);
345 result->value = value;
346
347 return (PangoAttribute *)result;
348}
349/* }}} */
350/* {{{ Float attribute */
351static PangoAttribute *pango_attr_float_new (const PangoAttrClass *klass,
352 double value);
353
354static PangoAttribute *
355pango_attr_float_copy (const PangoAttribute *attr)
356{
357 const PangoAttrFloat *float_attr = (PangoAttrFloat *)attr;
358
359 return pango_attr_float_new (klass: attr->klass, value: float_attr->value);
360}
361
362static void
363pango_attr_float_destroy (PangoAttribute *attr)
364{
365 PangoAttrFloat *fattr = (PangoAttrFloat *)attr;
366
367 g_slice_free (PangoAttrFloat, fattr);
368}
369
370static gboolean
371pango_attr_float_equal (const PangoAttribute *attr1,
372 const PangoAttribute *attr2)
373{
374 const PangoAttrFloat *float_attr1 = (const PangoAttrFloat *)attr1;
375 const PangoAttrFloat *float_attr2 = (const PangoAttrFloat *)attr2;
376
377 return (float_attr1->value == float_attr2->value);
378}
379
380static PangoAttribute *
381pango_attr_float_new (const PangoAttrClass *klass,
382 double value)
383{
384 PangoAttrFloat *result = g_slice_new (PangoAttrFloat);
385 pango_attribute_init (attr: &result->attr, klass);
386 result->value = value;
387
388 return (PangoAttribute *)result;
389}
390/* }}} */
391/* {{{ Size attribute */
392static PangoAttribute *pango_attr_size_new_internal (int size,
393 gboolean absolute);
394
395static PangoAttribute *
396pango_attr_size_copy (const PangoAttribute *attr)
397{
398 const PangoAttrSize *size_attr = (PangoAttrSize *)attr;
399
400 if (attr->klass->type == PANGO_ATTR_ABSOLUTE_SIZE)
401 return pango_attr_size_new_absolute (size: size_attr->size);
402 else
403 return pango_attr_size_new (size: size_attr->size);
404}
405
406static void
407pango_attr_size_destroy (PangoAttribute *attr)
408{
409 PangoAttrSize *sattr = (PangoAttrSize *)attr;
410
411 g_slice_free (PangoAttrSize, sattr);
412}
413
414static gboolean
415pango_attr_size_equal (const PangoAttribute *attr1,
416 const PangoAttribute *attr2)
417{
418 const PangoAttrSize *size_attr1 = (const PangoAttrSize *)attr1;
419 const PangoAttrSize *size_attr2 = (const PangoAttrSize *)attr2;
420
421 return size_attr1->size == size_attr2->size;
422}
423
424static PangoAttribute *
425pango_attr_size_new_internal (int size,
426 gboolean absolute)
427{
428 PangoAttrSize *result;
429
430 static const PangoAttrClass klass = {
431 PANGO_ATTR_SIZE,
432 pango_attr_size_copy,
433 pango_attr_size_destroy,
434 pango_attr_size_equal
435 };
436 static const PangoAttrClass absolute_klass = {
437 PANGO_ATTR_ABSOLUTE_SIZE,
438 pango_attr_size_copy,
439 pango_attr_size_destroy,
440 pango_attr_size_equal
441 };
442
443 result = g_slice_new (PangoAttrSize);
444 pango_attribute_init (attr: &result->attr, klass: absolute ? &absolute_klass : &klass);
445 result->size = size;
446 result->absolute = absolute;
447
448 return (PangoAttribute *)result;
449}
450/* }}} */
451/* {{{ Font description attribute */
452static PangoAttribute *
453pango_attr_font_desc_copy (const PangoAttribute *attr)
454{
455 const PangoAttrFontDesc *desc_attr = (const PangoAttrFontDesc *)attr;
456
457 return pango_attr_font_desc_new (desc: desc_attr->desc);
458}
459
460static void
461pango_attr_font_desc_destroy (PangoAttribute *attr)
462{
463 PangoAttrFontDesc *desc_attr = (PangoAttrFontDesc *)attr;
464
465 pango_font_description_free (desc: desc_attr->desc);
466 g_slice_free (PangoAttrFontDesc, desc_attr);
467}
468
469static gboolean
470pango_attr_font_desc_equal (const PangoAttribute *attr1,
471 const PangoAttribute *attr2)
472{
473 const PangoAttrFontDesc *desc_attr1 = (const PangoAttrFontDesc *)attr1;
474 const PangoAttrFontDesc *desc_attr2 = (const PangoAttrFontDesc *)attr2;
475
476 return pango_font_description_get_set_fields (desc: desc_attr1->desc) ==
477 pango_font_description_get_set_fields (desc: desc_attr2->desc) &&
478 pango_font_description_equal (desc1: desc_attr1->desc, desc2: desc_attr2->desc);
479}
480/* }}} */
481/* {{{ Shape attribute */
482static PangoAttribute *
483pango_attr_shape_copy (const PangoAttribute *attr)
484{
485 const PangoAttrShape *shape_attr = (PangoAttrShape *)attr;
486 gpointer data;
487
488 if (shape_attr->copy_func)
489 data = shape_attr->copy_func (shape_attr->data);
490 else
491 data = shape_attr->data;
492
493 return pango_attr_shape_new_with_data (ink_rect: &shape_attr->ink_rect, logical_rect: &shape_attr->logical_rect,
494 data, copy_func: shape_attr->copy_func, destroy_func: shape_attr->destroy_func);
495}
496
497static void
498pango_attr_shape_destroy (PangoAttribute *attr)
499{
500 PangoAttrShape *shape_attr = (PangoAttrShape *)attr;
501
502 if (shape_attr->destroy_func)
503 shape_attr->destroy_func (shape_attr->data);
504
505 g_slice_free (PangoAttrShape, shape_attr);
506}
507
508static gboolean
509pango_attr_shape_equal (const PangoAttribute *attr1,
510 const PangoAttribute *attr2)
511{
512 const PangoAttrShape *shape_attr1 = (const PangoAttrShape *)attr1;
513 const PangoAttrShape *shape_attr2 = (const PangoAttrShape *)attr2;
514
515 return (shape_attr1->logical_rect.x == shape_attr2->logical_rect.x &&
516 shape_attr1->logical_rect.y == shape_attr2->logical_rect.y &&
517 shape_attr1->logical_rect.width == shape_attr2->logical_rect.width &&
518 shape_attr1->logical_rect.height == shape_attr2->logical_rect.height &&
519 shape_attr1->ink_rect.x == shape_attr2->ink_rect.x &&
520 shape_attr1->ink_rect.y == shape_attr2->ink_rect.y &&
521 shape_attr1->ink_rect.width == shape_attr2->ink_rect.width &&
522 shape_attr1->ink_rect.height == shape_attr2->ink_rect.height &&
523 shape_attr1->data == shape_attr2->data);
524}
525/* }}} */
526/* }}} */
527/* {{{ Public API */
528
529/**
530 * pango_attr_family_new:
531 * @family: the family or comma-separated list of families
532 *
533 * Create a new font family attribute.
534 *
535 * Return value: (transfer full): the newly allocated
536 * `PangoAttribute`, which should be freed with
537 * [method@Pango.Attribute.destroy]
538 */
539PangoAttribute *
540pango_attr_family_new (const char *family)
541{
542 static const PangoAttrClass klass = {
543 PANGO_ATTR_FAMILY,
544 pango_attr_string_copy,
545 pango_attr_string_destroy,
546 pango_attr_string_equal
547 };
548
549 g_return_val_if_fail (family != NULL, NULL);
550
551 return pango_attr_string_new (klass: &klass, str: family);
552}
553
554/**
555 * pango_attr_language_new:
556 * @language: language tag
557 *
558 * Create a new language tag attribute.
559 *
560 * Return value: (transfer full): the newly allocated
561 * `PangoAttribute`, which should be freed with
562 * [method@Pango.Attribute.destroy]
563 */
564PangoAttribute *
565pango_attr_language_new (PangoLanguage *language)
566{
567 PangoAttrLanguage *result;
568
569 static const PangoAttrClass klass = {
570 PANGO_ATTR_LANGUAGE,
571 pango_attr_language_copy,
572 pango_attr_language_destroy,
573 pango_attr_language_equal
574 };
575
576 result = g_slice_new (PangoAttrLanguage);
577 pango_attribute_init (attr: &result->attr, klass: &klass);
578 result->value = language;
579
580 return (PangoAttribute *)result;
581}
582
583/**
584 * pango_attr_foreground_new:
585 * @red: the red value (ranging from 0 to 65535)
586 * @green: the green value
587 * @blue: the blue value
588 *
589 * Create a new foreground color attribute.
590 *
591 * Return value: (transfer full): the newly allocated
592 * `PangoAttribute`, which should be freed with
593 * [method@Pango.Attribute.destroy]
594 */
595PangoAttribute *
596pango_attr_foreground_new (guint16 red,
597 guint16 green,
598 guint16 blue)
599{
600 static const PangoAttrClass klass = {
601 PANGO_ATTR_FOREGROUND,
602 pango_attr_color_copy,
603 pango_attr_color_destroy,
604 pango_attr_color_equal
605 };
606
607 return pango_attr_color_new (klass: &klass, red, green, blue);
608}
609
610/**
611 * pango_attr_background_new:
612 * @red: the red value (ranging from 0 to 65535)
613 * @green: the green value
614 * @blue: the blue value
615 *
616 * Create a new background color attribute.
617 *
618 * Return value: (transfer full): the newly allocated
619 * `PangoAttribute`, which should be freed with
620 * [method@Pango.Attribute.destroy]
621 */
622PangoAttribute *
623pango_attr_background_new (guint16 red,
624 guint16 green,
625 guint16 blue)
626{
627 static const PangoAttrClass klass = {
628 PANGO_ATTR_BACKGROUND,
629 pango_attr_color_copy,
630 pango_attr_color_destroy,
631 pango_attr_color_equal
632 };
633
634 return pango_attr_color_new (klass: &klass, red, green, blue);
635}
636
637/**
638 * pango_attr_size_new:
639 * @size: the font size, in %PANGO_SCALE-ths of a point
640 *
641 * Create a new font-size attribute in fractional points.
642 *
643 * Return value: (transfer full): the newly allocated
644 * `PangoAttribute`, which should be freed with
645 * [method@Pango.Attribute.destroy]
646 */
647PangoAttribute *
648pango_attr_size_new (int size)
649{
650 return pango_attr_size_new_internal (size, FALSE);
651}
652
653/**
654 * pango_attr_size_new_absolute:
655 * @size: the font size, in %PANGO_SCALE-ths of a device unit
656 *
657 * Create a new font-size attribute in device units.
658 *
659 * Return value: (transfer full): the newly allocated
660 * `PangoAttribute`, which should be freed with
661 * [method@Pango.Attribute.destroy]
662 *
663 * Since: 1.8
664 */
665PangoAttribute *
666pango_attr_size_new_absolute (int size)
667{
668 return pango_attr_size_new_internal (size, TRUE);
669}
670
671/**
672 * pango_attr_style_new:
673 * @style: the slant style
674 *
675 * Create a new font slant style attribute.
676 *
677 * Return value: (transfer full): the newly allocated
678 * `PangoAttribute`, which should be freed with
679 * [method@Pango.Attribute.destroy]
680 */
681PangoAttribute *
682pango_attr_style_new (PangoStyle style)
683{
684 static const PangoAttrClass klass = {
685 PANGO_ATTR_STYLE,
686 pango_attr_int_copy,
687 pango_attr_int_destroy,
688 pango_attr_int_equal
689 };
690
691 return pango_attr_int_new (klass: &klass, value: (int)style);
692}
693
694/**
695 * pango_attr_weight_new:
696 * @weight: the weight
697 *
698 * Create a new font weight attribute.
699 *
700 * Return value: (transfer full): the newly allocated
701 * `PangoAttribute`, which should be freed with
702 * [method@Pango.Attribute.destroy]
703 */
704PangoAttribute *
705pango_attr_weight_new (PangoWeight weight)
706{
707 static const PangoAttrClass klass = {
708 PANGO_ATTR_WEIGHT,
709 pango_attr_int_copy,
710 pango_attr_int_destroy,
711 pango_attr_int_equal
712 };
713
714 return pango_attr_int_new (klass: &klass, value: (int)weight);
715}
716
717/**
718 * pango_attr_variant_new:
719 * @variant: the variant
720 *
721 * Create a new font variant attribute (normal or small caps).
722 *
723 * Return value: (transfer full): the newly allocated `PangoAttribute`,
724 * which should be freed with [method@Pango.Attribute.destroy].
725 */
726PangoAttribute *
727pango_attr_variant_new (PangoVariant variant)
728{
729 static const PangoAttrClass klass = {
730 PANGO_ATTR_VARIANT,
731 pango_attr_int_copy,
732 pango_attr_int_destroy,
733 pango_attr_int_equal
734 };
735
736 return pango_attr_int_new (klass: &klass, value: (int)variant);
737}
738
739/**
740 * pango_attr_stretch_new:
741 * @stretch: the stretch
742 *
743 * Create a new font stretch attribute.
744 *
745 * Return value: (transfer full): the newly allocated
746 * `PangoAttribute`, which should be freed with
747 * [method@Pango.Attribute.destroy]
748 */
749PangoAttribute *
750pango_attr_stretch_new (PangoStretch stretch)
751{
752 static const PangoAttrClass klass = {
753 PANGO_ATTR_STRETCH,
754 pango_attr_int_copy,
755 pango_attr_int_destroy,
756 pango_attr_int_equal
757 };
758
759 return pango_attr_int_new (klass: &klass, value: (int)stretch);
760}
761
762/**
763 * pango_attr_font_desc_new:
764 * @desc: the font description
765 *
766 * Create a new font description attribute.
767 *
768 * This attribute allows setting family, style, weight, variant,
769 * stretch, and size simultaneously.
770 *
771 * Return value: (transfer full): the newly allocated
772 * `PangoAttribute`, which should be freed with
773 * [method@Pango.Attribute.destroy]
774 */
775PangoAttribute *
776pango_attr_font_desc_new (const PangoFontDescription *desc)
777{
778 static const PangoAttrClass klass = {
779 PANGO_ATTR_FONT_DESC,
780 pango_attr_font_desc_copy,
781 pango_attr_font_desc_destroy,
782 pango_attr_font_desc_equal
783 };
784
785 PangoAttrFontDesc *result = g_slice_new (PangoAttrFontDesc);
786 pango_attribute_init (attr: &result->attr, klass: &klass);
787 result->desc = pango_font_description_copy (desc);
788
789 return (PangoAttribute *)result;
790}
791
792/**
793 * pango_attr_underline_new:
794 * @underline: the underline style
795 *
796 * Create a new underline-style attribute.
797 *
798 * Return value: (transfer full): the newly allocated
799 * `PangoAttribute`, which should be freed with
800 * [method@Pango.Attribute.destroy]
801 */
802PangoAttribute *
803pango_attr_underline_new (PangoUnderline underline)
804{
805 static const PangoAttrClass klass = {
806 PANGO_ATTR_UNDERLINE,
807 pango_attr_int_copy,
808 pango_attr_int_destroy,
809 pango_attr_int_equal
810 };
811
812 return pango_attr_int_new (klass: &klass, value: (int)underline);
813}
814
815/**
816 * pango_attr_underline_color_new:
817 * @red: the red value (ranging from 0 to 65535)
818 * @green: the green value
819 * @blue: the blue value
820 *
821 * Create a new underline color attribute.
822 *
823 * This attribute modifies the color of underlines.
824 * If not set, underlines will use the foreground color.
825 *
826 * Return value: (transfer full): the newly allocated
827 * `PangoAttribute`, which should be freed with
828 * [method@Pango.Attribute.destroy]
829 *
830 * Since: 1.8
831 */
832PangoAttribute *
833pango_attr_underline_color_new (guint16 red,
834 guint16 green,
835 guint16 blue)
836{
837 static const PangoAttrClass klass = {
838 PANGO_ATTR_UNDERLINE_COLOR,
839 pango_attr_color_copy,
840 pango_attr_color_destroy,
841 pango_attr_color_equal
842 };
843
844 return pango_attr_color_new (klass: &klass, red, green, blue);
845}
846
847/**
848 * pango_attr_strikethrough_new:
849 * @strikethrough: %TRUE if the text should be struck-through
850 *
851 * Create a new strike-through attribute.
852 *
853 * Return value: (transfer full): the newly allocated
854 * `PangoAttribute`, which should be freed with
855 * [method@Pango.Attribute.destroy]
856 */
857PangoAttribute *
858pango_attr_strikethrough_new (gboolean strikethrough)
859{
860 static const PangoAttrClass klass = {
861 PANGO_ATTR_STRIKETHROUGH,
862 pango_attr_int_copy,
863 pango_attr_int_destroy,
864 pango_attr_int_equal
865 };
866
867 return pango_attr_int_new (klass: &klass, value: (int)strikethrough);
868}
869
870/**
871 * pango_attr_strikethrough_color_new:
872 * @red: the red value (ranging from 0 to 65535)
873 * @green: the green value
874 * @blue: the blue value
875 *
876 * Create a new strikethrough color attribute.
877 *
878 * This attribute modifies the color of strikethrough lines.
879 * If not set, strikethrough lines will use the foreground color.
880 *
881 * Return value: (transfer full): the newly allocated
882 * `PangoAttribute`, which should be freed with
883 * [method@Pango.Attribute.destroy]
884 *
885 * Since: 1.8
886 */
887PangoAttribute *
888pango_attr_strikethrough_color_new (guint16 red,
889 guint16 green,
890 guint16 blue)
891{
892 static const PangoAttrClass klass = {
893 PANGO_ATTR_STRIKETHROUGH_COLOR,
894 pango_attr_color_copy,
895 pango_attr_color_destroy,
896 pango_attr_color_equal
897 };
898
899 return pango_attr_color_new (klass: &klass, red, green, blue);
900}
901
902/**
903 * pango_attr_rise_new:
904 * @rise: the amount that the text should be displaced vertically,
905 * in Pango units. Positive values displace the text upwards.
906 *
907 * Create a new baseline displacement attribute.
908 *
909 * Return value: (transfer full): the newly allocated
910 * `PangoAttribute`, which should be freed with
911 * [method@Pango.Attribute.destroy]
912 */
913PangoAttribute *
914pango_attr_rise_new (int rise)
915{
916 static const PangoAttrClass klass = {
917 PANGO_ATTR_RISE,
918 pango_attr_int_copy,
919 pango_attr_int_destroy,
920 pango_attr_int_equal
921 };
922
923 return pango_attr_int_new (klass: &klass, value: (int)rise);
924}
925
926/**
927 * pango_attr_baseline_shift_new:
928 * @shift: either a `PangoBaselineShift` enumeration value or an absolute value (> 1024)
929 * in Pango units, relative to the baseline of the previous run.
930 * Positive values displace the text upwards.
931 *
932 * Create a new baseline displacement attribute.
933 *
934 * The effect of this attribute is to shift the baseline of a run,
935 * relative to the run of preceding run.
936 *
937 * <picture>
938 * <source srcset="baseline-shift-dark.png" media="(prefers-color-scheme: dark)">
939 * <img alt="Baseline Shift" src="baseline-shift-light.png">
940 * </picture>
941
942 * Return value: (transfer full): the newly allocated
943 * `PangoAttribute`, which should be freed with
944 * [method@Pango.Attribute.destroy]
945 *
946 * Since: 1.50
947 */
948PangoAttribute *
949pango_attr_baseline_shift_new (int rise)
950{
951 static const PangoAttrClass klass = {
952 PANGO_ATTR_BASELINE_SHIFT,
953 pango_attr_int_copy,
954 pango_attr_int_destroy,
955 pango_attr_int_equal
956 };
957
958 return pango_attr_int_new (klass: &klass, value: (int)rise);
959}
960
961/**
962 * pango_attr_font_scale_new:
963 * @scale: a `PangoFontScale` value, which indicates font size change relative
964 * to the size of the previous run.
965 *
966 *
967 * Create a new font scale attribute.
968 *
969 * The effect of this attribute is to change the font size of a run,
970 * relative to the size of preceding run.
971 *
972 * Return value: (transfer full): the newly allocated
973 * `PangoAttribute`, which should be freed with
974 * [method@Pango.Attribute.destroy]
975 *
976 * Since: 1.50
977 */
978PangoAttribute *
979pango_attr_font_scale_new (PangoFontScale scale)
980{
981 static const PangoAttrClass klass = {
982 PANGO_ATTR_FONT_SCALE,
983 pango_attr_int_copy,
984 pango_attr_int_destroy,
985 pango_attr_int_equal
986 };
987
988 return pango_attr_int_new (klass: &klass, value: (int)scale);
989}
990
991/**
992 * pango_attr_scale_new:
993 * @scale_factor: factor to scale the font
994 *
995 * Create a new font size scale attribute.
996 *
997 * The base font for the affected text will have
998 * its size multiplied by @scale_factor.
999 *
1000 * Return value: (transfer full): the newly allocated
1001 * `PangoAttribute`, which should be freed with
1002 * [method@Pango.Attribute.destroy]
1003 */
1004PangoAttribute*
1005pango_attr_scale_new (double scale_factor)
1006{
1007 static const PangoAttrClass klass = {
1008 PANGO_ATTR_SCALE,
1009 pango_attr_float_copy,
1010 pango_attr_float_destroy,
1011 pango_attr_float_equal
1012 };
1013
1014 return pango_attr_float_new (klass: &klass, value: scale_factor);
1015}
1016
1017/**
1018 * pango_attr_fallback_new:
1019 * @enable_fallback: %TRUE if we should fall back on other fonts
1020 * for characters the active font is missing
1021 *
1022 * Create a new font fallback attribute.
1023 *
1024 * If fallback is disabled, characters will only be
1025 * used from the closest matching font on the system.
1026 * No fallback will be done to other fonts on the system
1027 * that might contain the characters in the text.
1028 *
1029 * Return value: (transfer full): the newly allocated
1030 * `PangoAttribute`, which should be freed with
1031 * [method@Pango.Attribute.destroy]
1032 *
1033 * Since: 1.4
1034 */
1035PangoAttribute *
1036pango_attr_fallback_new (gboolean enable_fallback)
1037{
1038 static const PangoAttrClass klass = {
1039 PANGO_ATTR_FALLBACK,
1040 pango_attr_int_copy,
1041 pango_attr_int_destroy,
1042 pango_attr_int_equal,
1043 };
1044
1045 return pango_attr_int_new (klass: &klass, value: (int)enable_fallback);
1046}
1047
1048/**
1049 * pango_attr_letter_spacing_new:
1050 * @letter_spacing: amount of extra space to add between
1051 * graphemes of the text, in Pango units
1052 *
1053 * Create a new letter-spacing attribute.
1054 *
1055 * Return value: (transfer full): the newly allocated
1056 * `PangoAttribute`, which should be freed with
1057 * [method@Pango.Attribute.destroy]
1058 *
1059 * Since: 1.6
1060 */
1061PangoAttribute *
1062pango_attr_letter_spacing_new (int letter_spacing)
1063{
1064 static const PangoAttrClass klass = {
1065 PANGO_ATTR_LETTER_SPACING,
1066 pango_attr_int_copy,
1067 pango_attr_int_destroy,
1068 pango_attr_int_equal
1069 };
1070
1071 return pango_attr_int_new (klass: &klass, value: letter_spacing);
1072}
1073
1074/**
1075 * pango_attr_shape_new_with_data:
1076 * @ink_rect: ink rectangle to assign to each character
1077 * @logical_rect: logical rectangle to assign to each character
1078 * @data: user data pointer
1079 * @copy_func: (nullable): function to copy @data when the
1080 * attribute is copied. If %NULL, @data is simply copied
1081 * as a pointer
1082 * @destroy_func: (nullable): function to free @data when the
1083 * attribute is freed
1084 *
1085 * Creates a new shape attribute.
1086 *
1087 * Like [func@Pango.AttrShape.new], but a user data pointer
1088 * is also provided; this pointer can be accessed when later
1089 * rendering the glyph.
1090 *
1091 * Return value: (transfer full): the newly allocated
1092 * `PangoAttribute`, which should be freed with
1093 * [method@Pango.Attribute.destroy]
1094 *
1095 * Since: 1.8
1096 */
1097PangoAttribute *
1098pango_attr_shape_new_with_data (const PangoRectangle *ink_rect,
1099 const PangoRectangle *logical_rect,
1100 gpointer data,
1101 PangoAttrDataCopyFunc copy_func,
1102 GDestroyNotify destroy_func)
1103{
1104 static const PangoAttrClass klass = {
1105 PANGO_ATTR_SHAPE,
1106 pango_attr_shape_copy,
1107 pango_attr_shape_destroy,
1108 pango_attr_shape_equal
1109 };
1110
1111 PangoAttrShape *result;
1112
1113 g_return_val_if_fail (ink_rect != NULL, NULL);
1114 g_return_val_if_fail (logical_rect != NULL, NULL);
1115
1116 result = g_slice_new (PangoAttrShape);
1117 pango_attribute_init (attr: &result->attr, klass: &klass);
1118 result->ink_rect = *ink_rect;
1119 result->logical_rect = *logical_rect;
1120 result->data = data;
1121 result->copy_func = copy_func;
1122 result->destroy_func = destroy_func;
1123
1124 return (PangoAttribute *)result;
1125}
1126
1127/**
1128 * pango_attr_shape_new:
1129 * @ink_rect: ink rectangle to assign to each character
1130 * @logical_rect: logical rectangle to assign to each character
1131 *
1132 * Create a new shape attribute.
1133 *
1134 * A shape is used to impose a particular ink and logical
1135 * rectangle on the result of shaping a particular glyph.
1136 * This might be used, for instance, for embedding a picture
1137 * or a widget inside a `PangoLayout`.
1138 *
1139 * Return value: (transfer full): the newly allocated
1140 * `PangoAttribute`, which should be freed with
1141 * [method@Pango.Attribute.destroy]
1142 */
1143PangoAttribute *
1144pango_attr_shape_new (const PangoRectangle *ink_rect,
1145 const PangoRectangle *logical_rect)
1146{
1147 g_return_val_if_fail (ink_rect != NULL, NULL);
1148 g_return_val_if_fail (logical_rect != NULL, NULL);
1149
1150 return pango_attr_shape_new_with_data (ink_rect, logical_rect,
1151 NULL, NULL, NULL);
1152}
1153
1154/**
1155 * pango_attr_gravity_new:
1156 * @gravity: the gravity value; should not be %PANGO_GRAVITY_AUTO
1157 *
1158 * Create a new gravity attribute.
1159 *
1160 * Return value: (transfer full): the newly allocated
1161 * `PangoAttribute`, which should be freed with
1162 * [method@Pango.Attribute.destroy]
1163 *
1164 * Since: 1.16
1165 */
1166PangoAttribute *
1167pango_attr_gravity_new (PangoGravity gravity)
1168{
1169 static const PangoAttrClass klass = {
1170 PANGO_ATTR_GRAVITY,
1171 pango_attr_int_copy,
1172 pango_attr_int_destroy,
1173 pango_attr_int_equal
1174 };
1175
1176 g_return_val_if_fail (gravity != PANGO_GRAVITY_AUTO, NULL);
1177
1178 return pango_attr_int_new (klass: &klass, value: (int)gravity);
1179}
1180
1181/**
1182 * pango_attr_gravity_hint_new:
1183 * @hint: the gravity hint value
1184 *
1185 * Create a new gravity hint attribute.
1186 *
1187 * Return value: (transfer full): the newly allocated
1188 * `PangoAttribute`, which should be freed with
1189 * [method@Pango.Attribute.destroy]
1190 *
1191 * Since: 1.16
1192 */
1193PangoAttribute *
1194pango_attr_gravity_hint_new (PangoGravityHint hint)
1195{
1196 static const PangoAttrClass klass = {
1197 PANGO_ATTR_GRAVITY_HINT,
1198 pango_attr_int_copy,
1199 pango_attr_int_destroy,
1200 pango_attr_int_equal
1201 };
1202
1203 return pango_attr_int_new (klass: &klass, value: (int)hint);
1204}
1205
1206/**
1207 * pango_attr_font_features_new:
1208 * @features: a string with OpenType font features, with the syntax of the [CSS
1209 * font-feature-settings property](https://www.w3.org/TR/css-fonts-4/#font-rend-desc)
1210 *
1211 * Create a new font features tag attribute.
1212 *
1213 * You can use this attribute to select OpenType font features like small-caps,
1214 * alternative glyphs, ligatures, etc. for fonts that support them.
1215 *
1216 * Return value: (transfer full): the newly allocated
1217 * `PangoAttribute`, which should be freed with
1218 * [method@Pango.Attribute.destroy]
1219 *
1220 * Since: 1.38
1221 */
1222PangoAttribute *
1223pango_attr_font_features_new (const gchar *features)
1224{
1225 static const PangoAttrClass klass = {
1226 PANGO_ATTR_FONT_FEATURES,
1227 pango_attr_string_copy,
1228 pango_attr_string_destroy,
1229 pango_attr_string_equal
1230 };
1231
1232 g_return_val_if_fail (features != NULL, NULL);
1233
1234 return pango_attr_string_new (klass: &klass, str: features);
1235}
1236
1237/**
1238 * pango_attr_foreground_alpha_new:
1239 * @alpha: the alpha value, between 1 and 65536
1240 *
1241 * Create a new foreground alpha attribute.
1242 *
1243 * Return value: (transfer full): the newly allocated
1244 * `PangoAttribute`, which should be freed with
1245 * [method@Pango.Attribute.destroy]
1246 *
1247 * Since: 1.38
1248 */
1249PangoAttribute *
1250pango_attr_foreground_alpha_new (guint16 alpha)
1251{
1252 static const PangoAttrClass klass = {
1253 PANGO_ATTR_FOREGROUND_ALPHA,
1254 pango_attr_int_copy,
1255 pango_attr_int_destroy,
1256 pango_attr_int_equal
1257 };
1258
1259 return pango_attr_int_new (klass: &klass, value: (int)alpha);
1260}
1261
1262/**
1263 * pango_attr_background_alpha_new:
1264 * @alpha: the alpha value, between 1 and 65536
1265 *
1266 * Create a new background alpha attribute.
1267 *
1268 * Return value: (transfer full): the newly allocated
1269 * `PangoAttribute`, which should be freed with
1270 * [method@Pango.Attribute.destroy]
1271 *
1272 * Since: 1.38
1273 */
1274PangoAttribute *
1275pango_attr_background_alpha_new (guint16 alpha)
1276{
1277 static const PangoAttrClass klass = {
1278 PANGO_ATTR_BACKGROUND_ALPHA,
1279 pango_attr_int_copy,
1280 pango_attr_int_destroy,
1281 pango_attr_int_equal
1282 };
1283
1284 return pango_attr_int_new (klass: &klass, value: (int)alpha);
1285}
1286
1287/**
1288 * pango_attr_allow_breaks_new:
1289 * @allow_breaks: %TRUE if we line breaks are allowed
1290 *
1291 * Create a new allow-breaks attribute.
1292 *
1293 * If breaks are disabled, the range will be kept in a
1294 * single run, as far as possible.
1295 *
1296 * Return value: (transfer full): the newly allocated
1297 * `PangoAttribute`, which should be freed with
1298 * [method@Pango.Attribute.destroy]
1299 *
1300 * Since: 1.44
1301 */
1302PangoAttribute *
1303pango_attr_allow_breaks_new (gboolean allow_breaks)
1304{
1305 static const PangoAttrClass klass = {
1306 PANGO_ATTR_ALLOW_BREAKS,
1307 pango_attr_int_copy,
1308 pango_attr_int_destroy,
1309 pango_attr_int_equal,
1310 };
1311
1312 return pango_attr_int_new (klass: &klass, value: (int)allow_breaks);
1313}
1314
1315/**
1316 * pango_attr_insert_hyphens_new:
1317 * @insert_hyphens: %TRUE if hyphens should be inserted
1318 *
1319 * Create a new insert-hyphens attribute.
1320 *
1321 * Pango will insert hyphens when breaking lines in
1322 * the middle of a word. This attribute can be used
1323 * to suppress the hyphen.
1324 *
1325 * Return value: (transfer full): the newly allocated
1326 * `PangoAttribute`, which should be freed with
1327 * [method@Pango.Attribute.destroy]
1328 *
1329 * Since: 1.44
1330 */
1331PangoAttribute *
1332pango_attr_insert_hyphens_new (gboolean insert_hyphens)
1333{
1334 static const PangoAttrClass klass = {
1335 PANGO_ATTR_INSERT_HYPHENS,
1336 pango_attr_int_copy,
1337 pango_attr_int_destroy,
1338 pango_attr_int_equal,
1339 };
1340
1341 return pango_attr_int_new (klass: &klass, value: (int)insert_hyphens);
1342}
1343
1344/**
1345 * pango_attr_show_new:
1346 * @flags: `PangoShowFlags` to apply
1347 *
1348 * Create a new attribute that influences how invisible
1349 * characters are rendered.
1350 *
1351 * Return value: (transfer full): the newly allocated
1352 * `PangoAttribute`, which should be freed with
1353 * [method@Pango.Attribute.destroy]
1354 *
1355 * Since: 1.44
1356 **/
1357PangoAttribute *
1358pango_attr_show_new (PangoShowFlags flags)
1359{
1360 static const PangoAttrClass klass = {
1361 PANGO_ATTR_SHOW,
1362 pango_attr_int_copy,
1363 pango_attr_int_destroy,
1364 pango_attr_int_equal,
1365 };
1366
1367 return pango_attr_int_new (klass: &klass, value: (int)flags);
1368}
1369
1370/**
1371 * pango_attr_word_new:
1372 *
1373 * Marks the range of the attribute as a single word.
1374 *
1375 * Note that this may require adjustments to word and
1376 * sentence classification around the range.
1377 *
1378 * Return value: (transfer full): the newly allocated
1379 * `PangoAttribute`, which should be freed with
1380 * [method@Pango.Attribute.destroy]
1381 *
1382 * Since: 1.50
1383 */
1384PangoAttribute *
1385pango_attr_word_new (void)
1386{
1387 static const PangoAttrClass klass = {
1388 PANGO_ATTR_WORD,
1389 pango_attr_int_copy,
1390 pango_attr_int_destroy,
1391 pango_attr_int_equal,
1392 };
1393
1394 return pango_attr_int_new (klass: &klass, value: 1);
1395}
1396
1397/**
1398 * pango_attr_sentence_new:
1399 *
1400 * Marks the range of the attribute as a single sentence.
1401 *
1402 * Note that this may require adjustments to word and
1403 * sentence classification around the range.
1404 *
1405 * Return value: (transfer full): the newly allocated
1406 * `PangoAttribute`, which should be freed with
1407 * [method@Pango.Attribute.destroy]
1408 *
1409 * Since: 1.50
1410 */
1411PangoAttribute *
1412pango_attr_sentence_new (void)
1413{
1414 static const PangoAttrClass klass = {
1415 PANGO_ATTR_SENTENCE,
1416 pango_attr_int_copy,
1417 pango_attr_int_destroy,
1418 pango_attr_int_equal,
1419 };
1420
1421 return pango_attr_int_new (klass: &klass, value: 1);
1422}
1423
1424/**
1425 * pango_attr_overline_new:
1426 * @overline: the overline style
1427 *
1428 * Create a new overline-style attribute.
1429 *
1430 * Return value: (transfer full): the newly allocated
1431 * `PangoAttribute`, which should be freed with
1432 * [method@Pango.Attribute.destroy]
1433 *
1434 * Since: 1.46
1435 */
1436PangoAttribute *
1437pango_attr_overline_new (PangoOverline overline)
1438{
1439 static const PangoAttrClass klass = {
1440 PANGO_ATTR_OVERLINE,
1441 pango_attr_int_copy,
1442 pango_attr_int_destroy,
1443 pango_attr_int_equal
1444 };
1445
1446 return pango_attr_int_new (klass: &klass, value: (int)overline);
1447}
1448
1449/**
1450 * pango_attr_overline_color_new:
1451 * @red: the red value (ranging from 0 to 65535)
1452 * @green: the green value
1453 * @blue: the blue value
1454 *
1455 * Create a new overline color attribute.
1456 *
1457 * This attribute modifies the color of overlines.
1458 * If not set, overlines will use the foreground color.
1459 *
1460 * Return value: (transfer full): the newly allocated
1461 * `PangoAttribute`, which should be freed with
1462 * [method@Pango.Attribute.destroy]
1463 *
1464 * Since: 1.46
1465 */
1466PangoAttribute *
1467pango_attr_overline_color_new (guint16 red,
1468 guint16 green,
1469 guint16 blue)
1470{
1471 static const PangoAttrClass klass = {
1472 PANGO_ATTR_OVERLINE_COLOR,
1473 pango_attr_color_copy,
1474 pango_attr_color_destroy,
1475 pango_attr_color_equal
1476 };
1477
1478 return pango_attr_color_new (klass: &klass, red, green, blue);
1479}
1480
1481/**
1482 * pango_attr_line_height_new:
1483 * @factor: the scaling factor to apply to the logical height
1484 *
1485 * Modify the height of logical line extents by a factor.
1486 *
1487 * This affects the values returned by
1488 * [method@Pango.LayoutLine.get_extents],
1489 * [method@Pango.LayoutLine.get_pixel_extents] and
1490 * [method@Pango.LayoutIter.get_line_extents].
1491 *
1492 *
1493 * Since: 1.50
1494 */
1495PangoAttribute *
1496pango_attr_line_height_new (double factor)
1497{
1498 static const PangoAttrClass klass = {
1499 PANGO_ATTR_LINE_HEIGHT,
1500 pango_attr_float_copy,
1501 pango_attr_float_destroy,
1502 pango_attr_float_equal
1503 };
1504
1505 return pango_attr_float_new (klass: &klass, value: factor);
1506}
1507
1508/**
1509 * pango_attr_line_height_new_absolute:
1510 * @height: the line height, in %PANGO_SCALE-ths of a point
1511 *
1512 * Override the height of logical line extents to be @height.
1513 *
1514 * This affects the values returned by
1515 * [method@Pango.LayoutLine.get_extents],
1516 * [method@Pango.LayoutLine.get_pixel_extents] and
1517 * [method@Pango.LayoutIter.get_line_extents].
1518 *
1519 * Since: 1.50
1520 */
1521PangoAttribute *
1522pango_attr_line_height_new_absolute (int height)
1523{
1524 static const PangoAttrClass klass = {
1525 PANGO_ATTR_ABSOLUTE_LINE_HEIGHT,
1526 pango_attr_int_copy,
1527 pango_attr_int_destroy,
1528 pango_attr_int_equal
1529 };
1530
1531 return pango_attr_int_new (klass: &klass, value: height);
1532}
1533
1534/**
1535 * pango_attr_text_transform_new:
1536 * @transform: `PangoTextTransform` to apply
1537 *
1538 * Create a new attribute that influences how characters
1539 * are transformed during shaping.
1540 *
1541 * Return value: (transfer full): the newly allocated
1542 * `PangoAttribute`, which should be freed with
1543 * [method@Pango.Attribute.destroy]
1544 *
1545 * Since: 1.50
1546 */
1547PangoAttribute *
1548pango_attr_text_transform_new (PangoTextTransform transform)
1549{
1550 static const PangoAttrClass klass = {
1551 PANGO_ATTR_TEXT_TRANSFORM,
1552 pango_attr_int_copy,
1553 pango_attr_int_destroy,
1554 pango_attr_int_equal
1555 };
1556
1557 return pango_attr_int_new (klass: &klass, value: transform);
1558}
1559/* }}} */
1560/* {{{ Binding helpers */
1561
1562/**
1563 * pango_attribute_as_int:
1564 * @attr: A `PangoAttribute` such as weight
1565 *
1566 * Returns the attribute cast to `PangoAttrInt`.
1567 *
1568 * This is mainly useful for language bindings.
1569 *
1570 * Returns: (nullable) (transfer none): The attribute as `PangoAttrInt`,
1571 * or %NULL if it's not an integer attribute
1572 *
1573 * Since: 1.50
1574 */
1575PangoAttrInt *
1576pango_attribute_as_int (PangoAttribute *attr)
1577{
1578 switch ((int)attr->klass->type)
1579 {
1580 case PANGO_ATTR_STYLE:
1581 case PANGO_ATTR_WEIGHT:
1582 case PANGO_ATTR_VARIANT:
1583 case PANGO_ATTR_STRETCH:
1584 case PANGO_ATTR_UNDERLINE:
1585 case PANGO_ATTR_STRIKETHROUGH:
1586 case PANGO_ATTR_RISE:
1587 case PANGO_ATTR_FALLBACK:
1588 case PANGO_ATTR_LETTER_SPACING:
1589 case PANGO_ATTR_GRAVITY:
1590 case PANGO_ATTR_GRAVITY_HINT:
1591 case PANGO_ATTR_FOREGROUND_ALPHA:
1592 case PANGO_ATTR_BACKGROUND_ALPHA:
1593 case PANGO_ATTR_ALLOW_BREAKS:
1594 case PANGO_ATTR_SHOW:
1595 case PANGO_ATTR_INSERT_HYPHENS:
1596 case PANGO_ATTR_OVERLINE:
1597 case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
1598 case PANGO_ATTR_TEXT_TRANSFORM:
1599 case PANGO_ATTR_WORD:
1600 case PANGO_ATTR_SENTENCE:
1601 case PANGO_ATTR_BASELINE_SHIFT:
1602 case PANGO_ATTR_FONT_SCALE:
1603 return (PangoAttrInt *)attr;
1604
1605 default:
1606 return NULL;
1607 }
1608}
1609
1610/**
1611 * pango_attribute_as_float:
1612 * @attr: A `PangoAttribute` such as scale
1613 *
1614 * Returns the attribute cast to `PangoAttrFloat`.
1615 *
1616 * This is mainly useful for language bindings.
1617 *
1618 * Returns: (nullable) (transfer none): The attribute as `PangoAttrFloat`,
1619 * or %NULL if it's not a floating point attribute
1620 *
1621 * Since: 1.50
1622 */
1623PangoAttrFloat *
1624pango_attribute_as_float (PangoAttribute *attr)
1625{
1626 switch ((int)attr->klass->type)
1627 {
1628 case PANGO_ATTR_SCALE:
1629 case PANGO_ATTR_LINE_HEIGHT:
1630 return (PangoAttrFloat *)attr;
1631
1632 default:
1633 return NULL;
1634 }
1635}
1636
1637/**
1638 * pango_attribute_as_string:
1639 * @attr: A `PangoAttribute` such as family
1640 *
1641 * Returns the attribute cast to `PangoAttrString`.
1642 *
1643 * This is mainly useful for language bindings.
1644 *
1645 * Returns: (nullable) (transfer none): The attribute as `PangoAttrString`,
1646 * or %NULL if it's not a string attribute
1647 *
1648 * Since: 1.50
1649 */
1650PangoAttrString *
1651pango_attribute_as_string (PangoAttribute *attr)
1652{
1653 switch ((int)attr->klass->type)
1654 {
1655 case PANGO_ATTR_FAMILY:
1656 return (PangoAttrString *)attr;
1657
1658 default:
1659 return NULL;
1660 }
1661}
1662
1663/**
1664 * pango_attribute_as_size:
1665 * @attr: A `PangoAttribute` representing a size
1666 *
1667 * Returns the attribute cast to `PangoAttrSize`.
1668 *
1669 * This is mainly useful for language bindings.
1670 *
1671 * Returns: (nullable) (transfer none): The attribute as `PangoAttrSize`,
1672 * or NULL if it's not a size attribute
1673 *
1674 * Since: 1.50
1675 */
1676PangoAttrSize *
1677pango_attribute_as_size (PangoAttribute *attr)
1678{
1679 switch ((int)attr->klass->type)
1680 {
1681 case PANGO_ATTR_SIZE:
1682 case PANGO_ATTR_ABSOLUTE_SIZE:
1683 return (PangoAttrSize *)attr;
1684
1685 default:
1686 return NULL;
1687 }
1688}
1689
1690/**
1691 * pango_attribute_as_color:
1692 * @attr: A `PangoAttribute` such as foreground
1693 *
1694 * Returns the attribute cast to `PangoAttrColor`.
1695 *
1696 * This is mainly useful for language bindings.
1697 *
1698 * Returns: (nullable) (transfer none): The attribute as `PangoAttrColor`,
1699 * or %NULL if it's not a color attribute
1700 *
1701 * Since: 1.50
1702 */
1703PangoAttrColor *
1704pango_attribute_as_color (PangoAttribute *attr)
1705{
1706 switch ((int)attr->klass->type)
1707 {
1708 case PANGO_ATTR_FOREGROUND:
1709 case PANGO_ATTR_BACKGROUND:
1710 case PANGO_ATTR_UNDERLINE_COLOR:
1711 case PANGO_ATTR_STRIKETHROUGH_COLOR:
1712 case PANGO_ATTR_OVERLINE_COLOR:
1713 return (PangoAttrColor *)attr;
1714
1715 default:
1716 return NULL;
1717 }
1718}
1719
1720/**
1721 * pango_attribute_as_font_desc:
1722 * @attr: A `PangoAttribute` representing a font description
1723 *
1724 * Returns the attribute cast to `PangoAttrFontDesc`.
1725 *
1726 * This is mainly useful for language bindings.
1727 *
1728 * Returns: (nullable) (transfer none): The attribute as `PangoAttrFontDesc`,
1729 * or %NULL if it's not a font description attribute
1730 *
1731 * Since: 1.50
1732 */
1733PangoAttrFontDesc *
1734pango_attribute_as_font_desc (PangoAttribute *attr)
1735{
1736 switch ((int)attr->klass->type)
1737 {
1738 case PANGO_ATTR_FONT_DESC:
1739 return (PangoAttrFontDesc *)attr;
1740
1741 default:
1742 return NULL;
1743 }
1744}
1745
1746/**
1747 * pango_attribute_as_font_features:
1748 * @attr: A `PangoAttribute` representing font features
1749 *
1750 * Returns the attribute cast to `PangoAttrFontFeatures`.
1751 *
1752 * This is mainly useful for language bindings.
1753 *
1754 * Returns: (nullable) (transfer none): The attribute as `PangoAttrFontFeatures`,
1755 * or %NULL if it's not a font features attribute
1756 *
1757 * Since: 1.50
1758 */
1759PangoAttrFontFeatures *
1760pango_attribute_as_font_features (PangoAttribute *attr)
1761{
1762 switch ((int)attr->klass->type)
1763 {
1764 case PANGO_ATTR_FONT_FEATURES:
1765 return (PangoAttrFontFeatures *)attr;
1766
1767 default:
1768 return NULL;
1769 }
1770}
1771
1772/**
1773 * pango_attribute_as_language:
1774 * @attr: A `PangoAttribute` representing a language
1775 *
1776 * Returns the attribute cast to `PangoAttrLanguage`.
1777 *
1778 * This is mainly useful for language bindings.
1779 *
1780 * Returns: (nullable) (transfer none): The attribute as `PangoAttrLanguage`,
1781 * or %NULL if it's not a language attribute
1782 *
1783 * Since: 1.50
1784 */
1785PangoAttrLanguage *
1786pango_attribute_as_language (PangoAttribute *attr)
1787{
1788 switch ((int)attr->klass->type)
1789 {
1790 case PANGO_ATTR_LANGUAGE:
1791 return (PangoAttrLanguage *)attr;
1792
1793 default:
1794 return NULL;
1795 }
1796}
1797
1798/**
1799 * pango_attribute_as_shape:
1800 * @attr: A `PangoAttribute` representing a shape
1801 *
1802 * Returns the attribute cast to `PangoAttrShape`.
1803 *
1804 * This is mainly useful for language bindings.
1805 *
1806 * Returns: (nullable) (transfer none): The attribute as `PangoAttrShape`,
1807 * or %NULL if it's not a shape attribute
1808 *
1809 * Since: 1.50
1810 */
1811PangoAttrShape *
1812pango_attribute_as_shape (PangoAttribute *attr)
1813{
1814 switch ((int)attr->klass->type)
1815 {
1816 case PANGO_ATTR_SHAPE:
1817 return (PangoAttrShape *)attr;
1818
1819 default:
1820 return NULL;
1821 }
1822}
1823
1824/* }}} */
1825/* {{{ Attribute List */
1826
1827G_DEFINE_BOXED_TYPE (PangoAttrList, pango_attr_list,
1828 pango_attr_list_copy,
1829 pango_attr_list_unref);
1830
1831void
1832_pango_attr_list_init (PangoAttrList *list)
1833{
1834 list->ref_count = 1;
1835 list->attributes = NULL;
1836}
1837
1838/**
1839 * pango_attr_list_new:
1840 *
1841 * Create a new empty attribute list with a reference
1842 * count of one.
1843 *
1844 * Return value: (transfer full): the newly allocated
1845 * `PangoAttrList`, which should be freed with
1846 * [method@Pango.AttrList.unref]
1847 */
1848PangoAttrList *
1849pango_attr_list_new (void)
1850{
1851 PangoAttrList *list = g_slice_new (PangoAttrList);
1852
1853 _pango_attr_list_init (list);
1854
1855 return list;
1856}
1857
1858/**
1859 * pango_attr_list_ref:
1860 * @list: (nullable): a `PangoAttrList`
1861 *
1862 * Increase the reference count of the given attribute
1863 * list by one.
1864 *
1865 * Return value: The attribute list passed in
1866 *
1867 * Since: 1.10
1868 */
1869PangoAttrList *
1870pango_attr_list_ref (PangoAttrList *list)
1871{
1872 if (list == NULL)
1873 return NULL;
1874
1875 g_atomic_int_inc ((int *) &list->ref_count);
1876
1877 return list;
1878}
1879
1880void
1881_pango_attr_list_destroy (PangoAttrList *list)
1882{
1883 guint i, p;
1884
1885 if (!list->attributes)
1886 return;
1887
1888 for (i = 0, p = list->attributes->len; i < p; i++)
1889 {
1890 PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
1891
1892 attr->klass->destroy (attr);
1893 }
1894
1895 g_ptr_array_free (array: list->attributes, TRUE);
1896}
1897
1898/**
1899 * pango_attr_list_unref:
1900 * @list: (nullable): a `PangoAttrList`
1901 *
1902 * Decrease the reference count of the given attribute
1903 * list by one.
1904 *
1905 * If the result is zero, free the attribute list
1906 * and the attributes it contains.
1907 */
1908void
1909pango_attr_list_unref (PangoAttrList *list)
1910{
1911 if (list == NULL)
1912 return;
1913
1914 g_return_if_fail (list->ref_count > 0);
1915
1916 if (g_atomic_int_dec_and_test ((int *) &list->ref_count))
1917 {
1918 _pango_attr_list_destroy (list);
1919 g_slice_free (PangoAttrList, list);
1920 }
1921}
1922
1923/**
1924 * pango_attr_list_copy:
1925 * @list: (nullable): a `PangoAttrList`
1926 *
1927 * Copy @list and return an identical new list.
1928 *
1929 * Return value: (nullable): the newly allocated
1930 * `PangoAttrList`, with a reference count of one,
1931 * which should be freed with [method@Pango.AttrList.unref].
1932 * Returns %NULL if @list was %NULL.
1933 */
1934PangoAttrList *
1935pango_attr_list_copy (PangoAttrList *list)
1936{
1937 PangoAttrList *new;
1938
1939 if (list == NULL)
1940 return NULL;
1941
1942 new = pango_attr_list_new ();
1943 if (!list->attributes || list->attributes->len == 0)
1944 return new;
1945
1946 new->attributes = g_ptr_array_copy (array: list->attributes, func: (GCopyFunc)pango_attribute_copy, NULL);
1947
1948 return new;
1949}
1950
1951static void
1952pango_attr_list_insert_internal (PangoAttrList *list,
1953 PangoAttribute *attr,
1954 gboolean before)
1955{
1956 const guint start_index = attr->start_index;
1957 PangoAttribute *last_attr;
1958
1959 if (G_UNLIKELY (!list->attributes))
1960 list->attributes = g_ptr_array_new ();
1961
1962 if (list->attributes->len == 0)
1963 {
1964 g_ptr_array_add (array: list->attributes, data: attr);
1965 return;
1966 }
1967
1968 g_assert (list->attributes->len > 0);
1969
1970 last_attr = g_ptr_array_index (list->attributes, list->attributes->len - 1);
1971
1972 if (last_attr->start_index < start_index ||
1973 (!before && last_attr->start_index == start_index))
1974 {
1975 g_ptr_array_add (array: list->attributes, data: attr);
1976 }
1977 else
1978 {
1979 guint i, p;
1980
1981 for (i = 0, p = list->attributes->len; i < p; i++)
1982 {
1983 PangoAttribute *cur = g_ptr_array_index (list->attributes, i);
1984
1985 if (cur->start_index > start_index ||
1986 (before && cur->start_index == start_index))
1987 {
1988 g_ptr_array_insert (array: list->attributes, index_: i, data: attr);
1989 break;
1990 }
1991 }
1992 }
1993}
1994
1995/**
1996 * pango_attr_list_insert:
1997 * @list: a `PangoAttrList`
1998 * @attr: (transfer full): the attribute to insert
1999 *
2000 * Insert the given attribute into the `PangoAttrList`.
2001 *
2002 * It will be inserted after all other attributes with a
2003 * matching @start_index.
2004 */
2005void
2006pango_attr_list_insert (PangoAttrList *list,
2007 PangoAttribute *attr)
2008{
2009 g_return_if_fail (list != NULL);
2010 g_return_if_fail (attr != NULL);
2011
2012 pango_attr_list_insert_internal (list, attr, FALSE);
2013}
2014
2015/**
2016 * pango_attr_list_insert_before:
2017 * @list: a `PangoAttrList`
2018 * @attr: (transfer full): the attribute to insert
2019 *
2020 * Insert the given attribute into the `PangoAttrList`.
2021 *
2022 * It will be inserted before all other attributes with a
2023 * matching @start_index.
2024 */
2025void
2026pango_attr_list_insert_before (PangoAttrList *list,
2027 PangoAttribute *attr)
2028{
2029 g_return_if_fail (list != NULL);
2030 g_return_if_fail (attr != NULL);
2031
2032 pango_attr_list_insert_internal (list, attr, TRUE);
2033}
2034
2035/**
2036 * pango_attr_list_change:
2037 * @list: a `PangoAttrList`
2038 * @attr: (transfer full): the attribute to insert
2039 *
2040 * Insert the given attribute into the `PangoAttrList`.
2041 *
2042 * It will replace any attributes of the same type
2043 * on that segment and be merged with any adjoining
2044 * attributes that are identical.
2045 *
2046 * This function is slower than [method@Pango.AttrList.insert]
2047 * for creating an attribute list in order (potentially
2048 * much slower for large lists). However,
2049 * [method@Pango.AttrList.insert] is not suitable for
2050 * continually changing a set of attributes since it
2051 * never removes or combines existing attributes.
2052 */
2053void
2054pango_attr_list_change (PangoAttrList *list,
2055 PangoAttribute *attr)
2056{
2057 guint i, p;
2058 guint start_index = attr->start_index;
2059 guint end_index = attr->end_index;
2060 gboolean inserted;
2061
2062 g_return_if_fail (list != NULL);
2063
2064 if (start_index == end_index) /* empty, nothing to do */
2065 {
2066 pango_attribute_destroy (attr);
2067 return;
2068 }
2069
2070 if (!list->attributes || list->attributes->len == 0)
2071 {
2072 pango_attr_list_insert (list, attr);
2073 return;
2074 }
2075
2076 inserted = FALSE;
2077 for (i = 0, p = list->attributes->len; i < p; i++)
2078 {
2079 PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i);
2080
2081 if (tmp_attr->start_index > start_index)
2082 {
2083 g_ptr_array_insert (array: list->attributes, index_: i, data: attr);
2084 inserted = TRUE;
2085 break;
2086 }
2087
2088 if (tmp_attr->klass->type != attr->klass->type)
2089 continue;
2090
2091 if (tmp_attr->end_index < start_index)
2092 continue; /* This attr does not overlap with the new one */
2093
2094 g_assert (tmp_attr->start_index <= start_index);
2095 g_assert (tmp_attr->end_index >= start_index);
2096
2097 if (pango_attribute_equal (attr1: tmp_attr, attr2: attr))
2098 {
2099 /* We can merge the new attribute with this attribute
2100 */
2101 if (tmp_attr->end_index >= end_index)
2102 {
2103 /* We are totally overlapping the previous attribute.
2104 * No action is needed.
2105 */
2106 pango_attribute_destroy (attr);
2107 return;
2108 }
2109
2110 tmp_attr->end_index = end_index;
2111 pango_attribute_destroy (attr);
2112
2113 attr = tmp_attr;
2114 inserted = TRUE;
2115 break;
2116 }
2117 else
2118 {
2119 /* Split, truncate, or remove the old attribute
2120 */
2121 if (tmp_attr->end_index > end_index)
2122 {
2123 PangoAttribute *end_attr = pango_attribute_copy (attr: tmp_attr);
2124
2125 end_attr->start_index = end_index;
2126 pango_attr_list_insert (list, attr: end_attr);
2127 }
2128
2129 if (tmp_attr->start_index == start_index)
2130 {
2131 pango_attribute_destroy (attr: tmp_attr);
2132 g_ptr_array_remove_index (array: list->attributes, index_: i);
2133 break;
2134 }
2135 else
2136 {
2137 tmp_attr->end_index = start_index;
2138 }
2139 }
2140 }
2141
2142 if (!inserted)
2143 /* we didn't insert attr yet */
2144 pango_attr_list_insert (list, attr);
2145
2146 /* We now have the range inserted into the list one way or the
2147 * other. Fix up the remainder
2148 */
2149 /* Attention: No i = 0 here. */
2150 for (i = i + 1, p = list->attributes->len; i < p; i++)
2151 {
2152 PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i);
2153
2154 if (tmp_attr->start_index > end_index)
2155 break;
2156
2157 if (tmp_attr->klass->type != attr->klass->type)
2158 continue;
2159
2160 if (tmp_attr == attr)
2161 continue;
2162
2163 if (tmp_attr->end_index <= attr->end_index ||
2164 pango_attribute_equal (attr1: tmp_attr, attr2: attr))
2165 {
2166 /* We can merge the new attribute with this attribute. */
2167 attr->end_index = MAX (end_index, tmp_attr->end_index);
2168 pango_attribute_destroy (attr: tmp_attr);
2169 g_ptr_array_remove_index (array: list->attributes, index_: i);
2170 i--;
2171 p--;
2172 continue;
2173 }
2174 else
2175 {
2176 /* Trim the start of this attribute that it begins at the end
2177 * of the new attribute. This may involve moving it in the list
2178 * to maintain the required non-decreasing order of start indices.
2179 */
2180 int k, m;
2181
2182 tmp_attr->start_index = attr->end_index;
2183
2184 for (k = i + 1, m = list->attributes->len; k < m; k++)
2185 {
2186 PangoAttribute *tmp_attr2 = g_ptr_array_index (list->attributes, k);
2187
2188 if (tmp_attr2->start_index >= tmp_attr->start_index)
2189 break;
2190
2191 g_ptr_array_index (list->attributes, k - 1) = tmp_attr2;
2192 g_ptr_array_index (list->attributes, k) = tmp_attr;
2193 }
2194 }
2195 }
2196}
2197
2198/**
2199 * pango_attr_list_update:
2200 * @list: a `PangoAttrList`
2201 * @pos: the position of the change
2202 * @remove: the number of removed bytes
2203 * @add: the number of added bytes
2204 *
2205 * Update indices of attributes in @list for a change in the
2206 * text they refer to.
2207 *
2208 * The change that this function applies is removing @remove
2209 * bytes at position @pos and inserting @add bytes instead.
2210 *
2211 * Attributes that fall entirely in the (@pos, @pos + @remove)
2212 * range are removed.
2213 *
2214 * Attributes that start or end inside the (@pos, @pos + @remove)
2215 * range are shortened to reflect the removal.
2216 *
2217 * Attributes start and end positions are updated if they are
2218 * behind @pos + @remove.
2219 *
2220 * Since: 1.44
2221 */
2222void
2223pango_attr_list_update (PangoAttrList *list,
2224 int pos,
2225 int remove,
2226 int add)
2227{
2228 guint i, p;
2229
2230 g_return_if_fail (pos >= 0);
2231 g_return_if_fail (remove >= 0);
2232 g_return_if_fail (add >= 0);
2233
2234 if (list->attributes)
2235 for (i = 0, p = list->attributes->len; i < p; i++)
2236 {
2237 PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
2238
2239 if (attr->start_index >= pos &&
2240 attr->end_index < pos + remove)
2241 {
2242 pango_attribute_destroy (attr);
2243 g_ptr_array_remove_index (array: list->attributes, index_: i);
2244 i--; /* Look at this index again */
2245 p--;
2246 continue;
2247 }
2248
2249 if (attr->start_index != PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING)
2250 {
2251 if (attr->start_index >= pos &&
2252 attr->start_index < pos + remove)
2253 {
2254 attr->start_index = pos + add;
2255 }
2256 else if (attr->start_index >= pos + remove)
2257 {
2258 attr->start_index += add - remove;
2259 }
2260 }
2261
2262 if (attr->end_index != PANGO_ATTR_INDEX_TO_TEXT_END)
2263 {
2264 if (attr->end_index >= pos &&
2265 attr->end_index < pos + remove)
2266 {
2267 attr->end_index = pos;
2268 }
2269 else if (attr->end_index >= pos + remove)
2270 {
2271 if (add > remove &&
2272 G_MAXUINT - attr->end_index < add - remove)
2273 attr->end_index = G_MAXUINT;
2274 else
2275 attr->end_index += add - remove;
2276 }
2277 }
2278 }
2279}
2280
2281/**
2282 * pango_attr_list_splice:
2283 * @list: a `PangoAttrList`
2284 * @other: another `PangoAttrList`
2285 * @pos: the position in @list at which to insert @other
2286 * @len: the length of the spliced segment. (Note that this
2287 * must be specified since the attributes in @other may only
2288 * be present at some subsection of this range)
2289 *
2290 * This function opens up a hole in @list, fills it
2291 * in with attributes from the left, and then merges
2292 * @other on top of the hole.
2293 *
2294 * This operation is equivalent to stretching every attribute
2295 * that applies at position @pos in @list by an amount @len,
2296 * and then calling [method@Pango.AttrList.change] with a copy
2297 * of each attribute in @other in sequence (offset in position
2298 * by @pos, and limited in length to @len).
2299 *
2300 * This operation proves useful for, for instance, inserting
2301 * a pre-edit string in the middle of an edit buffer.
2302 *
2303 * For backwards compatibility, the function behaves differently
2304 * when @len is 0. In this case, the attributes from @other are
2305 * not imited to @len, and are just overlayed on top of @list.
2306 *
2307 * This mode is useful for merging two lists of attributes together.
2308 */
2309void
2310pango_attr_list_splice (PangoAttrList *list,
2311 PangoAttrList *other,
2312 gint pos,
2313 gint len)
2314{
2315 guint i, p;
2316 guint upos, ulen;
2317 guint end;
2318
2319 g_return_if_fail (list != NULL);
2320 g_return_if_fail (other != NULL);
2321 g_return_if_fail (pos >= 0);
2322 g_return_if_fail (len >= 0);
2323
2324 upos = (guint)pos;
2325 ulen = (guint)len;
2326
2327/* This definition only works when a and b are unsigned; overflow
2328 * isn't defined in the C standard for signed integers
2329 */
2330#define CLAMP_ADD(a,b) (((a) + (b) < (a)) ? G_MAXUINT : (a) + (b))
2331
2332 end = CLAMP_ADD (upos, ulen);
2333
2334 if (list->attributes)
2335 for (i = 0, p = list->attributes->len; i < p; i++)
2336 {
2337 PangoAttribute *attr = g_ptr_array_index (list->attributes, i);;
2338
2339 if (attr->start_index <= upos)
2340 {
2341 if (attr->end_index > upos)
2342 attr->end_index = CLAMP_ADD (attr->end_index, ulen);
2343 }
2344 else
2345 {
2346 /* This could result in a zero length attribute if it
2347 * gets squashed up against G_MAXUINT, but deleting such
2348 * an element could (in theory) suprise the caller, so
2349 * we don't delete it.
2350 */
2351 attr->start_index = CLAMP_ADD (attr->start_index, ulen);
2352 attr->end_index = CLAMP_ADD (attr->end_index, ulen);
2353 }
2354 }
2355
2356 if (!other->attributes || other->attributes->len == 0)
2357 return;
2358
2359 for (i = 0, p = other->attributes->len; i < p; i++)
2360 {
2361 PangoAttribute *attr = pango_attribute_copy (g_ptr_array_index (other->attributes, i));
2362 if (ulen > 0)
2363 {
2364 attr->start_index = MIN (CLAMP_ADD (attr->start_index, upos), end);
2365 attr->end_index = MIN (CLAMP_ADD (attr->end_index, upos), end);
2366 }
2367 else
2368 {
2369 attr->start_index = CLAMP_ADD (attr->start_index, upos);
2370 attr->end_index = CLAMP_ADD (attr->end_index, upos);
2371 }
2372
2373 /* Same as above, the attribute could be squashed to zero-length; here
2374 * pango_attr_list_change() will take care of deleting it.
2375 */
2376 pango_attr_list_change (list, attr);
2377 }
2378#undef CLAMP_ADD
2379}
2380
2381/**
2382 * pango_attr_list_get_attributes:
2383 * @list: a `PangoAttrList`
2384 *
2385 * Gets a list of all attributes in @list.
2386 *
2387 * Return value: (element-type Pango.Attribute) (transfer full):
2388 * a list of all attributes in @list. To free this value,
2389 * call [method@Pango.Attribute.destroy] on each value and
2390 * g_slist_free() on the list.
2391 *
2392 * Since: 1.44
2393 */
2394GSList *
2395pango_attr_list_get_attributes (PangoAttrList *list)
2396{
2397 GSList *result = NULL;
2398 guint i, p;
2399
2400 g_return_val_if_fail (list != NULL, NULL);
2401
2402 if (!list->attributes || list->attributes->len == 0)
2403 return NULL;
2404
2405 for (i = 0, p = list->attributes->len; i < p; i++)
2406 {
2407 PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
2408
2409 result = g_slist_prepend (list: result, data: pango_attribute_copy (attr));
2410 }
2411
2412 return g_slist_reverse (list: result);
2413}
2414
2415/**
2416 * pango_attr_list_equal:
2417 * @list: a `PangoAttrList`
2418 * @other_list: the other `PangoAttrList`
2419 *
2420 * Checks whether @list and @other_list contain the same
2421 * attributes and whether those attributes apply to the
2422 * same ranges.
2423 *
2424 * Beware that this will return wrong values if any list
2425 * contains duplicates.
2426 *
2427 * Return value: %TRUE if the lists are equal, %FALSE if
2428 * they aren't
2429 *
2430 * Since: 1.46
2431 */
2432gboolean
2433pango_attr_list_equal (PangoAttrList *list,
2434 PangoAttrList *other_list)
2435{
2436 GPtrArray *attrs, *other_attrs;
2437 guint64 skip_bitmask = 0;
2438 guint i;
2439
2440 if (list == other_list)
2441 return TRUE;
2442
2443 if (list == NULL || other_list == NULL)
2444 return FALSE;
2445
2446 if (list->attributes == NULL || other_list->attributes == NULL)
2447 return list->attributes == other_list->attributes;
2448
2449 attrs = list->attributes;
2450 other_attrs = other_list->attributes;
2451
2452 if (attrs->len != other_attrs->len)
2453 return FALSE;
2454
2455 for (i = 0; i < attrs->len; i++)
2456 {
2457 PangoAttribute *attr = g_ptr_array_index (attrs, i);
2458 gboolean attr_equal = FALSE;
2459 guint other_attr_index;
2460
2461 for (other_attr_index = 0; other_attr_index < other_attrs->len; other_attr_index++)
2462 {
2463 PangoAttribute *other_attr = g_ptr_array_index (other_attrs, other_attr_index);
2464 guint64 other_attr_bitmask = other_attr_index < 64 ? 1 << other_attr_index : 0;
2465
2466 if ((skip_bitmask & other_attr_bitmask) != 0)
2467 continue;
2468
2469 if (attr->start_index == other_attr->start_index &&
2470 attr->end_index == other_attr->end_index &&
2471 pango_attribute_equal (attr1: attr, attr2: other_attr))
2472 {
2473 skip_bitmask |= other_attr_bitmask;
2474 attr_equal = TRUE;
2475 break;
2476 }
2477
2478 }
2479
2480 if (!attr_equal)
2481 return FALSE;
2482 }
2483
2484 return TRUE;
2485}
2486
2487gboolean
2488_pango_attr_list_has_attributes (const PangoAttrList *list)
2489{
2490 return list && list->attributes != NULL && list->attributes->len > 0;
2491}
2492
2493/**
2494 * pango_attr_list_filter:
2495 * @list: a `PangoAttrList`
2496 * @func: (scope call) (closure data): callback function;
2497 * returns %TRUE if an attribute should be filtered out
2498 * @data: (closure): Data to be passed to @func
2499 *
2500 * Given a `PangoAttrList` and callback function, removes
2501 * any elements of @list for which @func returns %TRUE and
2502 * inserts them into a new list.
2503 *
2504 * Return value: (transfer full) (nullable): the new
2505 * `PangoAttrList` or %NULL if no attributes of the
2506 * given types were found
2507 *
2508 * Since: 1.2
2509 */
2510PangoAttrList *
2511pango_attr_list_filter (PangoAttrList *list,
2512 PangoAttrFilterFunc func,
2513 gpointer data)
2514
2515{
2516 PangoAttrList *new = NULL;
2517 guint i, p;
2518
2519 g_return_val_if_fail (list != NULL, NULL);
2520
2521 if (!list->attributes || list->attributes->len == 0)
2522 return NULL;
2523
2524 for (i = 0, p = list->attributes->len; i < p; i++)
2525 {
2526 PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i);
2527
2528 if ((*func) (tmp_attr, data))
2529 {
2530 g_ptr_array_remove_index (array: list->attributes, index_: i);
2531 i--; /* Need to look at this index again */
2532 p--;
2533
2534 if (G_UNLIKELY (!new))
2535 {
2536 new = pango_attr_list_new ();
2537 new->attributes = g_ptr_array_new ();
2538 }
2539
2540 g_ptr_array_add (array: new->attributes, data: tmp_attr);
2541 }
2542 }
2543
2544 return new;
2545}
2546
2547/* {{{ PangoAttrList serialization */
2548
2549/* We serialize attribute lists to strings. The format
2550 * is a comma-separated list of the attributes in the order
2551 * in which they are in the list, with each attribute having
2552 * this format:
2553 *
2554 * START END NICK VALUE
2555 *
2556 * Values that can contain a comma, such as font descriptions
2557 * are quoted with "".
2558 */
2559
2560static const char *
2561get_attr_type_nick (PangoAttrType attr_type)
2562{
2563 GEnumClass *enum_class;
2564 GEnumValue *enum_value;
2565
2566 enum_class = g_type_class_ref (type: pango_attr_type_get_type ());
2567 enum_value = g_enum_get_value (enum_class, value: attr_type);
2568 g_type_class_unref (g_class: enum_class);
2569
2570 return enum_value->value_nick;
2571}
2572
2573static GType
2574get_attr_value_type (PangoAttrType type)
2575{
2576 switch ((int)type)
2577 {
2578 case PANGO_ATTR_STYLE: return PANGO_TYPE_STYLE;
2579 case PANGO_ATTR_WEIGHT: return PANGO_TYPE_WEIGHT;
2580 case PANGO_ATTR_VARIANT: return PANGO_TYPE_VARIANT;
2581 case PANGO_ATTR_STRETCH: return PANGO_TYPE_STRETCH;
2582 case PANGO_ATTR_GRAVITY: return PANGO_TYPE_GRAVITY;
2583 case PANGO_ATTR_GRAVITY_HINT: return PANGO_TYPE_GRAVITY_HINT;
2584 case PANGO_ATTR_UNDERLINE: return PANGO_TYPE_UNDERLINE;
2585 case PANGO_ATTR_OVERLINE: return PANGO_TYPE_OVERLINE;
2586 case PANGO_ATTR_BASELINE_SHIFT: return PANGO_TYPE_BASELINE_SHIFT;
2587 case PANGO_ATTR_FONT_SCALE: return PANGO_TYPE_FONT_SCALE;
2588 case PANGO_ATTR_TEXT_TRANSFORM: return PANGO_TYPE_TEXT_TRANSFORM;
2589 default: return G_TYPE_INVALID;
2590 }
2591}
2592
2593static void
2594append_enum_value (GString *str,
2595 GType type,
2596 int value)
2597{
2598 GEnumClass *enum_class;
2599 GEnumValue *enum_value;
2600
2601 enum_class = g_type_class_ref (type);
2602 enum_value = g_enum_get_value (enum_class, value);
2603 g_type_class_unref (g_class: enum_class);
2604
2605 if (enum_value)
2606 g_string_append_printf (string: str, format: " %s", enum_value->value_nick);
2607 else
2608 g_string_append_printf (string: str, format: " %d", value);
2609}
2610
2611static void
2612attr_print (GString *str,
2613 PangoAttribute *attr)
2614{
2615 PangoAttrString *string;
2616 PangoAttrLanguage *lang;
2617 PangoAttrInt *integer;
2618 PangoAttrFloat *flt;
2619 PangoAttrFontDesc *font;
2620 PangoAttrColor *color;
2621 PangoAttrShape *shape;
2622 PangoAttrSize *size;
2623 PangoAttrFontFeatures *features;
2624
2625 g_string_append_printf (string: str, format: "%u %u ", attr->start_index, attr->end_index);
2626
2627 g_string_append (string: str, val: get_attr_type_nick (attr_type: attr->klass->type));
2628
2629 if (attr->klass->type == PANGO_ATTR_WEIGHT ||
2630 attr->klass->type == PANGO_ATTR_STYLE ||
2631 attr->klass->type == PANGO_ATTR_STRETCH ||
2632 attr->klass->type == PANGO_ATTR_VARIANT ||
2633 attr->klass->type == PANGO_ATTR_GRAVITY ||
2634 attr->klass->type == PANGO_ATTR_GRAVITY_HINT ||
2635 attr->klass->type == PANGO_ATTR_UNDERLINE ||
2636 attr->klass->type == PANGO_ATTR_OVERLINE ||
2637 attr->klass->type == PANGO_ATTR_BASELINE_SHIFT ||
2638 attr->klass->type == PANGO_ATTR_FONT_SCALE ||
2639 attr->klass->type == PANGO_ATTR_TEXT_TRANSFORM)
2640 append_enum_value (str, type: get_attr_value_type (type: attr->klass->type), value: ((PangoAttrInt *)attr)->value);
2641 else if (attr->klass->type == PANGO_ATTR_STRIKETHROUGH ||
2642 attr->klass->type == PANGO_ATTR_ALLOW_BREAKS ||
2643 attr->klass->type == PANGO_ATTR_INSERT_HYPHENS ||
2644 attr->klass->type == PANGO_ATTR_FALLBACK)
2645 g_string_append (string: str, val: ((PangoAttrInt *)attr)->value ? " true" : " false");
2646 else if ((string = pango_attribute_as_string (attr)) != NULL)
2647 g_string_append_printf (string: str, format: " %s", string->value);
2648 else if ((lang = pango_attribute_as_language (attr)) != NULL)
2649 g_string_append_printf (string: str, format: " %s", pango_language_to_string (lang->value));
2650 else if ((integer = pango_attribute_as_int (attr)) != NULL)
2651 g_string_append_printf (string: str, format: " %d", integer->value);
2652 else if ((flt = pango_attribute_as_float (attr)) != NULL)
2653 {
2654 char buf[20];
2655 g_ascii_formatd (buffer: buf, buf_len: 20, format: "%f", d: flt->value);
2656 g_string_append_printf (string: str, format: " %s", buf);
2657 }
2658 else if ((font = pango_attribute_as_font_desc (attr)) != NULL)
2659 {
2660 char *s = pango_font_description_to_string (desc: font->desc);
2661 g_string_append_printf (string: str, format: " \"%s\"", s);
2662 g_free (mem: s);
2663 }
2664 else if ((color = pango_attribute_as_color (attr)) != NULL)
2665 {
2666 char *s = pango_color_to_string (color: &color->color);
2667 g_string_append_printf (string: str, format: " %s", s);
2668 g_free (mem: s);
2669 }
2670 else if ((shape = pango_attribute_as_shape (attr)) != NULL)
2671 g_string_append (string: str, val: "shape"); /* FIXME */
2672 else if ((size = pango_attribute_as_size (attr)) != NULL)
2673 g_string_append_printf (string: str, format: " %d", size->size);
2674 else if ((features = pango_attribute_as_font_features (attr)) != NULL)
2675 g_string_append_printf (string: str, format: " \"%s\"", features->features);
2676 else
2677 g_assert_not_reached ();
2678}
2679
2680/**
2681 * pango_attr_list_to_string:
2682 * @list: a `PangoAttrList`
2683 *
2684 * Serializes a `PangoAttrList` to a string.
2685 *
2686 * No guarantees are made about the format of the string,
2687 * it may change between Pango versions.
2688 *
2689 * The intended use of this function is testing and
2690 * debugging. The format is not meant as a permanent
2691 * storage format.
2692 *
2693 * Returns: (transfer full): a newly allocated string
2694 * Since: 1.50
2695 */
2696char *
2697pango_attr_list_to_string (PangoAttrList *list)
2698{
2699 GString *s;
2700
2701 s = g_string_new (init: "");
2702
2703 if (list->attributes)
2704 for (int i = 0; i < list->attributes->len; i++)
2705 {
2706 PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
2707
2708 if (i > 0)
2709 g_string_append (string: s, val: "\n");
2710 attr_print (str: s, attr);
2711 }
2712
2713 return g_string_free (string: s, FALSE);
2714}
2715
2716static PangoAttrType
2717get_attr_type_by_nick (const char *nick,
2718 int len)
2719{
2720 GEnumClass *enum_class;
2721
2722 enum_class = g_type_class_ref (type: pango_attr_type_get_type ());
2723 for (GEnumValue *ev = enum_class->values; ev->value_name; ev++)
2724 {
2725 if (ev->value_nick && strncmp (s1: ev->value_nick, s2: nick, n: len) == 0)
2726 {
2727 g_type_class_unref (g_class: enum_class);
2728 return (PangoAttrType) ev->value;
2729 }
2730 }
2731
2732 g_type_class_unref (g_class: enum_class);
2733 return PANGO_ATTR_INVALID;
2734}
2735
2736static int
2737get_attr_value (PangoAttrType type,
2738 const char *str,
2739 int len)
2740{
2741 GEnumClass *enum_class;
2742 char *endp;
2743 int value;
2744
2745 enum_class = g_type_class_ref (type: get_attr_value_type (type));
2746 for (GEnumValue *ev = enum_class->values; ev->value_name; ev++)
2747 {
2748 if (ev->value_nick && strncmp (s1: ev->value_nick, s2: str, n: len) == 0)
2749 {
2750 g_type_class_unref (g_class: enum_class);
2751 return ev->value;
2752 }
2753 }
2754 g_type_class_unref (g_class: enum_class);
2755
2756 value = g_ascii_strtoll (nptr: str, endptr: &endp, base: 10);
2757 if (endp - str == len)
2758 return value;
2759
2760 return -1;
2761}
2762
2763static gboolean
2764is_valid_end_char (char c)
2765{
2766 return c == ',' || c == '\n' || c == '\0';
2767}
2768
2769/**
2770 * pango_attr_list_from_string:
2771 * @text: a string
2772 *
2773 * Deserializes a `PangoAttrList` from a string.
2774 *
2775 * This is the counterpart to [method@Pango.AttrList.to_string].
2776 * See that functions for details about the format.
2777 *
2778 * Returns: (transfer full) (nullable): a new `PangoAttrList`
2779 * Since: 1.50
2780 */
2781PangoAttrList *
2782pango_attr_list_from_string (const char *text)
2783{
2784 PangoAttrList *list;
2785 const char *p;
2786
2787 g_return_val_if_fail (text != NULL, NULL);
2788
2789 list = pango_attr_list_new ();
2790
2791 if (*text == '\0')
2792 return list;
2793
2794 list->attributes = g_ptr_array_new ();
2795
2796 p = text + strspn (s: text, accept: " \t\n");
2797 while (*p)
2798 {
2799 char *endp;
2800 gint64 start_index;
2801 gint64 end_index;
2802 char *str;
2803 PangoAttrType attr_type;
2804 PangoAttribute *attr;
2805 PangoLanguage *lang;
2806 gint64 integer;
2807 PangoFontDescription *desc;
2808 PangoColor color;
2809 double num;
2810
2811 start_index = g_ascii_strtoll (nptr: p, endptr: &endp, base: 10);
2812 if (*endp != ' ')
2813 goto fail;
2814
2815 p = endp + strspn (s: endp, accept: " ");
2816 if (!*p)
2817 goto fail;
2818
2819 end_index = g_ascii_strtoll (nptr: p, endptr: &endp, base: 10);
2820 if (*endp != ' ')
2821 goto fail;
2822
2823 p = endp + strspn (s: endp, accept: " ");
2824
2825 endp = (char *)p + strcspn (s: p, reject: " ");
2826 attr_type = get_attr_type_by_nick (nick: p, len: endp - p);
2827
2828 p = endp + strspn (s: endp, accept: " ");
2829 if (*p == '\0')
2830 goto fail;
2831
2832#define INT_ATTR(name,type) \
2833 integer = g_ascii_strtoll (p, &endp, 10); \
2834 if (!is_valid_end_char (*endp)) goto fail; \
2835 attr = pango_attr_##name##_new ((type)integer);
2836
2837#define BOOLEAN_ATTR(name,type) \
2838 if (strncmp (p, "true", strlen ("true")) == 0) \
2839 { \
2840 integer = 1; \
2841 endp = (char *)(p + strlen ("true")); \
2842 } \
2843 else if (strncmp (p, "false", strlen ("false")) == 0) \
2844 { \
2845 integer = 0; \
2846 endp = (char *)(p + strlen ("false")); \
2847 } \
2848 else \
2849 integer = g_ascii_strtoll (p, &endp, 10); \
2850 if (!is_valid_end_char (*endp)) goto fail; \
2851 attr = pango_attr_##name##_new ((type)integer);
2852
2853#define ENUM_ATTR(name, type, min, max) \
2854 endp = (char *)p + strcspn (p, ",\n"); \
2855 integer = get_attr_value (attr_type, p, endp - p); \
2856 attr = pango_attr_##name##_new ((type) CLAMP (integer, min, max));
2857
2858#define FLOAT_ATTR(name) \
2859 num = g_ascii_strtod (p, &endp); \
2860 if (!is_valid_end_char (*endp)) goto fail; \
2861 attr = pango_attr_##name##_new ((float)num);
2862
2863#define COLOR_ATTR(name) \
2864 endp = (char *)p + strcspn (p, ",\n"); \
2865 if (!is_valid_end_char (*endp)) goto fail; \
2866 str = g_strndup (p, endp - p); \
2867 if (!pango_color_parse (&color, str)) \
2868 { \
2869 g_free (str); \
2870 goto fail; \
2871 } \
2872 attr = pango_attr_##name##_new (color.red, color.green, color.blue); \
2873 g_free (str);
2874
2875 switch (attr_type)
2876 {
2877 case PANGO_ATTR_INVALID:
2878 pango_attr_list_unref (list);
2879 return NULL;
2880
2881 case PANGO_ATTR_LANGUAGE:
2882 endp = (char *)p + strcspn (s: p, reject: ",\n");
2883 if (!is_valid_end_char (c: *endp)) goto fail;
2884 str = g_strndup (str: p, n: endp - p);
2885 lang = pango_language_from_string (language: str);
2886 attr = pango_attr_language_new (language: lang);
2887 g_free (mem: str);
2888 break;
2889
2890 case PANGO_ATTR_FAMILY:
2891 endp = (char *)p + strcspn (s: p, reject: ",\n");
2892 if (!is_valid_end_char (c: *endp)) goto fail;
2893 str = g_strndup (str: p, n: endp - p);
2894 attr = pango_attr_family_new (family: str);
2895 g_free (mem: str);
2896 break;
2897
2898 case PANGO_ATTR_STYLE:
2899 ENUM_ATTR(style, PangoStyle, PANGO_STYLE_NORMAL, PANGO_STYLE_ITALIC);
2900 break;
2901
2902 case PANGO_ATTR_WEIGHT:
2903 ENUM_ATTR(weight, PangoWeight, PANGO_WEIGHT_THIN, PANGO_WEIGHT_ULTRAHEAVY);
2904 break;
2905
2906 case PANGO_ATTR_VARIANT:
2907 ENUM_ATTR(variant, PangoVariant, PANGO_VARIANT_NORMAL, PANGO_VARIANT_TITLE_CAPS);
2908 break;
2909
2910 case PANGO_ATTR_STRETCH:
2911 ENUM_ATTR(stretch, PangoStretch, PANGO_STRETCH_ULTRA_CONDENSED, PANGO_STRETCH_ULTRA_EXPANDED);
2912 break;
2913
2914 case PANGO_ATTR_SIZE:
2915 INT_ATTR(size, int);
2916 break;
2917
2918 case PANGO_ATTR_FONT_DESC:
2919 p++;
2920 endp = strchr (s: p, c: '"');
2921 if (!endp) goto fail;
2922 str = g_strndup (str: p, n: endp - p);
2923 desc = pango_font_description_from_string (str);
2924 attr = pango_attr_font_desc_new (desc);
2925 pango_font_description_free (desc);
2926 g_free (mem: str);
2927 endp++;
2928 if (!is_valid_end_char (c: *endp)) goto fail;
2929 break;
2930
2931 case PANGO_ATTR_FOREGROUND:
2932 COLOR_ATTR(foreground);
2933 break;
2934
2935 case PANGO_ATTR_BACKGROUND:
2936 COLOR_ATTR(background);
2937 break;
2938
2939 case PANGO_ATTR_UNDERLINE:
2940 ENUM_ATTR(underline, PangoUnderline, PANGO_UNDERLINE_NONE, PANGO_UNDERLINE_ERROR_LINE);
2941 break;
2942
2943 case PANGO_ATTR_STRIKETHROUGH:
2944 BOOLEAN_ATTR(strikethrough, gboolean);
2945 break;
2946
2947 case PANGO_ATTR_RISE:
2948 INT_ATTR(rise, int);
2949 break;
2950
2951 case PANGO_ATTR_SHAPE:
2952 endp = (char *)p + strcspn (s: p, reject: ",\n");
2953 continue; /* FIXME */
2954
2955 case PANGO_ATTR_SCALE:
2956 FLOAT_ATTR(scale);
2957 break;
2958
2959 case PANGO_ATTR_FALLBACK:
2960 BOOLEAN_ATTR(fallback, gboolean);
2961 break;
2962
2963 case PANGO_ATTR_LETTER_SPACING:
2964 INT_ATTR(letter_spacing, int);
2965 break;
2966
2967 case PANGO_ATTR_UNDERLINE_COLOR:
2968 COLOR_ATTR(underline_color);
2969 break;
2970
2971 case PANGO_ATTR_STRIKETHROUGH_COLOR:
2972 COLOR_ATTR(strikethrough_color);
2973 break;
2974
2975 case PANGO_ATTR_ABSOLUTE_SIZE:
2976 integer = g_ascii_strtoll (nptr: p, endptr: &endp, base: 10);
2977 if (!is_valid_end_char (c: *endp)) goto fail;
2978 attr = pango_attr_size_new_absolute (size: integer);
2979 break;
2980
2981 case PANGO_ATTR_GRAVITY:
2982 ENUM_ATTR(gravity, PangoGravity, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_WEST);
2983 break;
2984
2985 case PANGO_ATTR_FONT_FEATURES:
2986 p++;
2987 endp = strchr (s: p, c: '"');
2988 if (!endp) goto fail;
2989 str = g_strndup (str: p, n: endp - p);
2990 attr = pango_attr_font_features_new (features: str);
2991 g_free (mem: str);
2992 endp++;
2993 if (!is_valid_end_char (c: *endp)) goto fail;
2994 break;
2995
2996 case PANGO_ATTR_GRAVITY_HINT:
2997 ENUM_ATTR(gravity_hint, PangoGravityHint, PANGO_GRAVITY_HINT_NATURAL, PANGO_GRAVITY_HINT_LINE);
2998 break;
2999
3000 case PANGO_ATTR_FOREGROUND_ALPHA:
3001 INT_ATTR(foreground_alpha, int);
3002 break;
3003
3004 case PANGO_ATTR_BACKGROUND_ALPHA:
3005 INT_ATTR(background_alpha, int);
3006 break;
3007
3008 case PANGO_ATTR_ALLOW_BREAKS:
3009 BOOLEAN_ATTR(allow_breaks, gboolean);
3010 break;
3011
3012 case PANGO_ATTR_SHOW:
3013 INT_ATTR(show, PangoShowFlags);
3014 break;
3015
3016 case PANGO_ATTR_INSERT_HYPHENS:
3017 BOOLEAN_ATTR(insert_hyphens, gboolean);
3018 break;
3019
3020 case PANGO_ATTR_OVERLINE:
3021 ENUM_ATTR(overline, PangoOverline, PANGO_OVERLINE_NONE, PANGO_OVERLINE_SINGLE);
3022 break;
3023
3024 case PANGO_ATTR_OVERLINE_COLOR:
3025 COLOR_ATTR(overline_color);
3026 break;
3027
3028 case PANGO_ATTR_LINE_HEIGHT:
3029 FLOAT_ATTR(line_height);
3030 break;
3031
3032 case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
3033 integer = g_ascii_strtoll (nptr: p, endptr: &endp, base: 10);
3034 if (!is_valid_end_char (c: *endp)) goto fail;
3035 attr = pango_attr_line_height_new_absolute (height: integer);
3036 break;
3037
3038 case PANGO_ATTR_TEXT_TRANSFORM:
3039 ENUM_ATTR(text_transform, PangoTextTransform, PANGO_TEXT_TRANSFORM_NONE, PANGO_TEXT_TRANSFORM_CAPITALIZE);
3040 break;
3041
3042 case PANGO_ATTR_WORD:
3043 integer = g_ascii_strtoll (nptr: p, endptr: &endp, base: 10);
3044 if (!is_valid_end_char (c: *endp)) goto fail;
3045 attr = pango_attr_word_new ();
3046 break;
3047
3048 case PANGO_ATTR_SENTENCE:
3049 integer = g_ascii_strtoll (nptr: p, endptr: &endp, base: 10);
3050 if (!is_valid_end_char (c: *endp)) goto fail;
3051 attr = pango_attr_sentence_new ();
3052 break;
3053
3054 case PANGO_ATTR_BASELINE_SHIFT:
3055 ENUM_ATTR(baseline_shift, PangoBaselineShift, 0, G_MAXINT);
3056 break;
3057
3058 case PANGO_ATTR_FONT_SCALE:
3059 ENUM_ATTR(font_scale, PangoFontScale, PANGO_FONT_SCALE_NONE, PANGO_FONT_SCALE_SMALL_CAPS);
3060 break;
3061
3062 default:
3063 g_assert_not_reached ();
3064 }
3065
3066 attr->start_index = (guint)start_index;
3067 attr->end_index = (guint)end_index;
3068 g_ptr_array_add (array: list->attributes, data: attr);
3069
3070 p = endp;
3071 if (*p)
3072 {
3073 if (*p == ',')
3074 p++;
3075 p += strspn (s: p, accept: " \n");
3076 }
3077 }
3078
3079 goto success;
3080
3081fail:
3082 pango_attr_list_unref (list);
3083 list = NULL;
3084
3085success:
3086 return list;
3087}
3088
3089/* }}} */
3090/* {{{ Attribute Iterator */
3091
3092G_DEFINE_BOXED_TYPE (PangoAttrIterator,
3093 pango_attr_iterator,
3094 pango_attr_iterator_copy,
3095 pango_attr_iterator_destroy)
3096
3097void
3098_pango_attr_list_get_iterator (PangoAttrList *list,
3099 PangoAttrIterator *iterator)
3100{
3101 iterator->attribute_stack = NULL;
3102 iterator->attrs = list->attributes;
3103 iterator->n_attrs = iterator->attrs ? iterator->attrs->len : 0;
3104
3105 iterator->attr_index = 0;
3106 iterator->start_index = 0;
3107 iterator->end_index = 0;
3108
3109 if (!pango_attr_iterator_next (iterator))
3110 iterator->end_index = G_MAXUINT;
3111}
3112
3113/**
3114 * pango_attr_list_get_iterator:
3115 * @list: a `PangoAttrList`
3116 *
3117 * Create a iterator initialized to the beginning of the list.
3118 *
3119 * @list must not be modified until this iterator is freed.
3120 *
3121 * Return value: (transfer full): the newly allocated
3122 * `PangoAttrIterator`, which should be freed with
3123 * [method@Pango.AttrIterator.destroy]
3124 */
3125PangoAttrIterator *
3126pango_attr_list_get_iterator (PangoAttrList *list)
3127{
3128 PangoAttrIterator *iterator;
3129
3130 g_return_val_if_fail (list != NULL, NULL);
3131
3132 iterator = g_slice_new (PangoAttrIterator);
3133 _pango_attr_list_get_iterator (list, iterator);
3134
3135 return iterator;
3136}
3137
3138/**
3139 * pango_attr_iterator_range:
3140 * @iterator: a PangoAttrIterator
3141 * @start: (out): location to store the start of the range
3142 * @end: (out): location to store the end of the range
3143 *
3144 * Get the range of the current segment.
3145 *
3146 * Note that the stored return values are signed, not unsigned
3147 * like the values in `PangoAttribute`. To deal with this API
3148 * oversight, stored return values that wouldn't fit into
3149 * a signed integer are clamped to %G_MAXINT.
3150 */
3151void
3152pango_attr_iterator_range (PangoAttrIterator *iterator,
3153 gint *start,
3154 gint *end)
3155{
3156 g_return_if_fail (iterator != NULL);
3157
3158 if (start)
3159 *start = MIN (iterator->start_index, G_MAXINT);
3160 if (end)
3161 *end = MIN (iterator->end_index, G_MAXINT);
3162}
3163
3164/**
3165 * pango_attr_iterator_next:
3166 * @iterator: a `PangoAttrIterator`
3167 *
3168 * Advance the iterator until the next change of style.
3169 *
3170 * Return value: %FALSE if the iterator is at the end
3171 * of the list, otherwise %TRUE
3172 */
3173gboolean
3174pango_attr_iterator_next (PangoAttrIterator *iterator)
3175{
3176 int i;
3177
3178 g_return_val_if_fail (iterator != NULL, FALSE);
3179
3180 if (iterator->attr_index >= iterator->n_attrs &&
3181 (!iterator->attribute_stack || iterator->attribute_stack->len == 0))
3182 return FALSE;
3183
3184 iterator->start_index = iterator->end_index;
3185 iterator->end_index = G_MAXUINT;
3186
3187 if (iterator->attribute_stack)
3188 {
3189 for (i = iterator->attribute_stack->len - 1; i>= 0; i--)
3190 {
3191 const PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
3192
3193 if (attr->end_index == iterator->start_index)
3194 g_ptr_array_remove_index (array: iterator->attribute_stack, index_: i); /* Can't use index_fast :( */
3195 else
3196 iterator->end_index = MIN (iterator->end_index, attr->end_index);
3197 }
3198 }
3199
3200 while (1)
3201 {
3202 PangoAttribute *attr;
3203
3204 if (iterator->attr_index >= iterator->n_attrs)
3205 break;
3206
3207 attr = g_ptr_array_index (iterator->attrs, iterator->attr_index);
3208
3209 if (attr->start_index != iterator->start_index)
3210 break;
3211
3212 if (attr->end_index > iterator->start_index)
3213 {
3214 if (G_UNLIKELY (!iterator->attribute_stack))
3215 iterator->attribute_stack = g_ptr_array_new ();
3216
3217 g_ptr_array_add (array: iterator->attribute_stack, data: attr);
3218
3219 iterator->end_index = MIN (iterator->end_index, attr->end_index);
3220 }
3221
3222 iterator->attr_index++; /* NEXT! */
3223 }
3224
3225 if (iterator->attr_index < iterator->n_attrs)
3226 {
3227 PangoAttribute *attr = g_ptr_array_index (iterator->attrs, iterator->attr_index);
3228
3229 iterator->end_index = MIN (iterator->end_index, attr->start_index);
3230 }
3231
3232 return TRUE;
3233}
3234
3235/**
3236 * pango_attr_iterator_copy:
3237 * @iterator: a `PangoAttrIterator`
3238 *
3239 * Copy a `PangoAttrIterator`.
3240 *
3241 * Return value: (transfer full): the newly allocated
3242 * `PangoAttrIterator`, which should be freed with
3243 * [method@Pango.AttrIterator.destroy]
3244 */
3245PangoAttrIterator *
3246pango_attr_iterator_copy (PangoAttrIterator *iterator)
3247{
3248 PangoAttrIterator *copy;
3249
3250 g_return_val_if_fail (iterator != NULL, NULL);
3251
3252 copy = g_slice_new (PangoAttrIterator);
3253
3254 *copy = *iterator;
3255
3256 if (iterator->attribute_stack)
3257 copy->attribute_stack = g_ptr_array_copy (array: iterator->attribute_stack, NULL, NULL);
3258 else
3259 copy->attribute_stack = NULL;
3260
3261 return copy;
3262}
3263
3264void
3265_pango_attr_iterator_destroy (PangoAttrIterator *iterator)
3266{
3267 if (iterator->attribute_stack)
3268 g_ptr_array_free (array: iterator->attribute_stack, TRUE);
3269}
3270
3271/**
3272 * pango_attr_iterator_destroy:
3273 * @iterator: a `PangoAttrIterator`
3274 *
3275 * Destroy a `PangoAttrIterator` and free all associated memory.
3276 */
3277void
3278pango_attr_iterator_destroy (PangoAttrIterator *iterator)
3279{
3280 g_return_if_fail (iterator != NULL);
3281
3282 _pango_attr_iterator_destroy (iterator);
3283 g_slice_free (PangoAttrIterator, iterator);
3284}
3285
3286/**
3287 * pango_attr_iterator_get:
3288 * @iterator: a `PangoAttrIterator`
3289 * @type: the type of attribute to find
3290 *
3291 * Find the current attribute of a particular type
3292 * at the iterator location.
3293 *
3294 * When multiple attributes of the same type overlap,
3295 * the attribute whose range starts closest to the
3296 * current location is used.
3297 *
3298 * Return value: (nullable) (transfer none): the current
3299 * attribute of the given type, or %NULL if no attribute
3300 * of that type applies to the current location.
3301 */
3302PangoAttribute *
3303pango_attr_iterator_get (PangoAttrIterator *iterator,
3304 PangoAttrType type)
3305{
3306 int i;
3307
3308 g_return_val_if_fail (iterator != NULL, NULL);
3309
3310 if (!iterator->attribute_stack)
3311 return NULL;
3312
3313 for (i = iterator->attribute_stack->len - 1; i>= 0; i--)
3314 {
3315 PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
3316
3317 if (attr->klass->type == type)
3318 return attr;
3319 }
3320
3321 return NULL;
3322}
3323
3324/**
3325 * pango_attr_iterator_get_font:
3326 * @iterator: a `PangoAttrIterator`
3327 * @desc: a `PangoFontDescription` to fill in with the current
3328 * values. The family name in this structure will be set using
3329 * [method@Pango.FontDescription.set_family_static] using
3330 * values from an attribute in the `PangoAttrList` associated
3331 * with the iterator, so if you plan to keep it around, you
3332 * must call:
3333 * `pango_font_description_set_family (desc, pango_font_description_get_family (desc))`.
3334 * @language: (out) (optional): location to store language tag
3335 * for item, or %NULL if none is found.
3336 * @extra_attrs: (out) (optional) (element-type Pango.Attribute) (transfer full):
3337 * location in which to store a list of non-font attributes
3338 * at the the current position; only the highest priority
3339 * value of each attribute will be added to this list. In
3340 * order to free this value, you must call
3341 * [method@Pango.Attribute.destroy] on each member.
3342 *
3343 * Get the font and other attributes at the current
3344 * iterator position.
3345 */
3346void
3347pango_attr_iterator_get_font (PangoAttrIterator *iterator,
3348 PangoFontDescription *desc,
3349 PangoLanguage **language,
3350 GSList **extra_attrs)
3351{
3352 PangoFontMask mask = 0;
3353 gboolean have_language = FALSE;
3354 gdouble scale = 0;
3355 gboolean have_scale = FALSE;
3356 int i;
3357
3358 g_return_if_fail (iterator != NULL);
3359 g_return_if_fail (desc != NULL);
3360
3361 if (language)
3362 *language = NULL;
3363
3364 if (extra_attrs)
3365 *extra_attrs = NULL;
3366
3367 if (!iterator->attribute_stack)
3368 return;
3369
3370 for (i = iterator->attribute_stack->len - 1; i >= 0; i--)
3371 {
3372 const PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
3373
3374 switch ((int) attr->klass->type)
3375 {
3376 case PANGO_ATTR_FONT_DESC:
3377 {
3378 PangoFontMask new_mask = pango_font_description_get_set_fields (desc: ((PangoAttrFontDesc *)attr)->desc) & ~mask;
3379 mask |= new_mask;
3380 pango_font_description_unset_fields (desc, to_unset: new_mask);
3381 pango_font_description_merge_static (desc, desc_to_merge: ((PangoAttrFontDesc *)attr)->desc, FALSE);
3382
3383 break;
3384 }
3385 case PANGO_ATTR_FAMILY:
3386 if (!(mask & PANGO_FONT_MASK_FAMILY))
3387 {
3388 mask |= PANGO_FONT_MASK_FAMILY;
3389 pango_font_description_set_family (desc, family: ((PangoAttrString *)attr)->value);
3390 }
3391 break;
3392 case PANGO_ATTR_STYLE:
3393 if (!(mask & PANGO_FONT_MASK_STYLE))
3394 {
3395 mask |= PANGO_FONT_MASK_STYLE;
3396 pango_font_description_set_style (desc, style: ((PangoAttrInt *)attr)->value);
3397 }
3398 break;
3399 case PANGO_ATTR_VARIANT:
3400 if (!(mask & PANGO_FONT_MASK_VARIANT))
3401 {
3402 mask |= PANGO_FONT_MASK_VARIANT;
3403 pango_font_description_set_variant (desc, variant: ((PangoAttrInt *)attr)->value);
3404 }
3405 break;
3406 case PANGO_ATTR_WEIGHT:
3407 if (!(mask & PANGO_FONT_MASK_WEIGHT))
3408 {
3409 mask |= PANGO_FONT_MASK_WEIGHT;
3410 pango_font_description_set_weight (desc, weight: ((PangoAttrInt *)attr)->value);
3411 }
3412 break;
3413 case PANGO_ATTR_STRETCH:
3414 if (!(mask & PANGO_FONT_MASK_STRETCH))
3415 {
3416 mask |= PANGO_FONT_MASK_STRETCH;
3417 pango_font_description_set_stretch (desc, stretch: ((PangoAttrInt *)attr)->value);
3418 }
3419 break;
3420 case PANGO_ATTR_SIZE:
3421 if (!(mask & PANGO_FONT_MASK_SIZE))
3422 {
3423 mask |= PANGO_FONT_MASK_SIZE;
3424 pango_font_description_set_size (desc, size: ((PangoAttrSize *)attr)->size);
3425 }
3426 break;
3427 case PANGO_ATTR_ABSOLUTE_SIZE:
3428 if (!(mask & PANGO_FONT_MASK_SIZE))
3429 {
3430 mask |= PANGO_FONT_MASK_SIZE;
3431 pango_font_description_set_absolute_size (desc, size: ((PangoAttrSize *)attr)->size);
3432 }
3433 break;
3434 case PANGO_ATTR_SCALE:
3435 if (!have_scale)
3436 {
3437 have_scale = TRUE;
3438 scale = ((PangoAttrFloat *)attr)->value;
3439 }
3440 break;
3441 case PANGO_ATTR_LANGUAGE:
3442 if (language)
3443 {
3444 if (!have_language)
3445 {
3446 have_language = TRUE;
3447 *language = ((PangoAttrLanguage *)attr)->value;
3448 }
3449 }
3450 break;
3451 default:
3452 if (extra_attrs)
3453 {
3454 gboolean found = FALSE;
3455
3456 /* Hack: special-case FONT_FEATURES, BASELINE_SHIFT and FONT_SCALE.
3457 * We don't want these to accumulate, not override each other,
3458 * so we never merge them.
3459 * This needs to be handled more systematically.
3460 */
3461 if (attr->klass->type != PANGO_ATTR_FONT_FEATURES &&
3462 attr->klass->type != PANGO_ATTR_BASELINE_SHIFT &&
3463 attr->klass->type != PANGO_ATTR_FONT_SCALE)
3464 {
3465 GSList *tmp_list = *extra_attrs;
3466 while (tmp_list)
3467 {
3468 PangoAttribute *old_attr = tmp_list->data;
3469 if (attr->klass->type == old_attr->klass->type)
3470 {
3471 found = TRUE;
3472 break;
3473 }
3474
3475 tmp_list = tmp_list->next;
3476 }
3477 }
3478
3479 if (!found)
3480 *extra_attrs = g_slist_prepend (list: *extra_attrs, data: pango_attribute_copy (attr));
3481 }
3482 }
3483 }
3484
3485 if (have_scale)
3486 {
3487 /* We need to use a local variable to ensure that the compiler won't
3488 * implicitly cast it to integer while the result is kept in registers,
3489 * leading to a wrong approximation in i386 (with 387 FPU)
3490 */
3491 volatile double size = scale * pango_font_description_get_size (desc);
3492
3493 if (pango_font_description_get_size_is_absolute (desc))
3494 pango_font_description_set_absolute_size (desc, size);
3495 else
3496 pango_font_description_set_size (desc, size);
3497 }
3498}
3499
3500/**
3501 * pango_attr_iterator_get_attrs:
3502 * @iterator: a `PangoAttrIterator`
3503 *
3504 * Gets a list of all attributes at the current position of the
3505 * iterator.
3506 *
3507 * Return value: (element-type Pango.Attribute) (transfer full):
3508 * a list of all attributes for the current range. To free
3509 * this value, call [method@Pango.Attribute.destroy] on each
3510 * value and g_slist_free() on the list.
3511 *
3512 * Since: 1.2
3513 */
3514GSList *
3515pango_attr_iterator_get_attrs (PangoAttrIterator *iterator)
3516{
3517 GSList *attrs = NULL;
3518 int i;
3519
3520 if (!iterator->attribute_stack ||
3521 iterator->attribute_stack->len == 0)
3522 return NULL;
3523
3524 for (i = iterator->attribute_stack->len - 1; i >= 0; i--)
3525 {
3526 PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
3527 GSList *tmp_list2;
3528 gboolean found = FALSE;
3529
3530 if (attr->klass->type != PANGO_ATTR_FONT_DESC &&
3531 attr->klass->type != PANGO_ATTR_BASELINE_SHIFT &&
3532 attr->klass->type != PANGO_ATTR_FONT_SCALE)
3533 for (tmp_list2 = attrs; tmp_list2; tmp_list2 = tmp_list2->next)
3534 {
3535 PangoAttribute *old_attr = tmp_list2->data;
3536 if (attr->klass->type == old_attr->klass->type)
3537 {
3538 found = TRUE;
3539 break;
3540 }
3541 }
3542
3543 if (!found)
3544 attrs = g_slist_prepend (list: attrs, data: pango_attribute_copy (attr));
3545 }
3546
3547 return attrs;
3548}
3549
3550gboolean
3551pango_attr_iterator_advance (PangoAttrIterator *iterator,
3552 int index)
3553{
3554 int start_range, end_range;
3555
3556 pango_attr_iterator_range (iterator, start: &start_range, end: &end_range);
3557
3558 while (index >= end_range)
3559 {
3560 if (!pango_attr_iterator_next (iterator))
3561 return FALSE;
3562 pango_attr_iterator_range (iterator, start: &start_range, end: &end_range);
3563 }
3564
3565 if (start_range > index)
3566 g_warning ("pango_attr_iterator_advance(): iterator had already "
3567 "moved beyond the index");
3568
3569 return TRUE;
3570}
3571/* }}} */
3572
3573/* vim:set foldmethod=marker expandtab: */
3574

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