2 * pango-layout.c: High-level layout driver
4 * Copyright (C) 2000, 2001, 2006 Red Hat Software
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.
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.
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.
23 #include "pango-glyph.h" /* For pango_shape() */
24 #include "pango-break.h"
25 #include "pango-item.h"
26 #include "pango-engine.h"
27 #include "pango-impl-utils.h"
28 #include "pango-glyph-item.h"
31 #include "pango-layout-private.h"
34 typedef struct _Extents Extents;
35 typedef struct _ItemProperties ItemProperties;
36 typedef struct _ParaBreakState ParaBreakState;
40 /* Vertical position of the line's baseline in layout coords */
43 /* Line extents in layout coords */
44 PangoRectangle ink_rect;
45 PangoRectangle logical_rect;
48 struct _ItemProperties
51 gboolean strikethrough;
55 PangoRectangle *shape_ink_rect;
56 PangoRectangle *shape_logical_rect;
59 struct _PangoLayoutIter
62 GSList *line_list_link;
63 PangoLayoutLine *line;
65 /* If run is NULL, it means we're on a "virtual run"
66 * at the end of the line with 0 width
68 GSList *run_list_link;
69 PangoLayoutRun *run; /* FIXME nuke this, just keep the link */
72 /* list of Extents for each line in layout coordinates */
74 GSList *line_extents_link;
76 /* X position of the current run */
79 /* Width of the current run */
82 /* this run is left-to-right */
85 /* X position of the left side of the current cluster */
88 /* The width of the current cluster */
91 /* glyph offset to the current cluster start */
94 /* first glyph in the next cluster */
95 int next_cluster_glyph;
97 /* number of Unicode chars in current cluster */
98 int cluster_num_chars;
100 /* visual position of current character within the cluster */
101 int character_position;
103 /* the real width of layout */
107 typedef struct _PangoLayoutLinePrivate PangoLayoutLinePrivate;
109 struct _PangoLayoutLinePrivate
111 PangoLayoutLine line;
114 /* Extents cache status:
116 * LEAKED means that the user has access to this line structure or a
117 * run included in this line, and so can change the glyphs/glyph-widths.
118 * If this is true, extents caching will be disabled.
125 PangoRectangle ink_rect;
126 PangoRectangle logical_rect;
129 struct _PangoLayoutClass
131 GObjectClass parent_class;
136 #define LINE_IS_VALID(line) ((line)->layout != NULL)
138 #ifdef G_DISABLE_CHECKS
139 #define ITER_IS_INVALID(iter) FALSE
141 #define ITER_IS_INVALID(iter) G_UNLIKELY (check_invalid ((iter), G_STRLOC))
143 check_invalid (PangoLayoutIter *iter,
146 if (iter->line->layout == NULL)
148 g_warning ("%s: PangoLayout changed since PangoLayoutIter was created, iterator invalid", loc);
158 static void pango_layout_clear_lines (PangoLayout *layout);
159 static void pango_layout_check_lines (PangoLayout *layout);
161 static PangoAttrList *pango_layout_get_effective_attributes (PangoLayout *layout);
163 static PangoLayoutLine * pango_layout_line_new (PangoLayout *layout);
164 static void pango_layout_line_postprocess (PangoLayoutLine *line,
165 ParaBreakState *state,
168 static int *pango_layout_line_get_log2vis_map (PangoLayoutLine *line,
170 static int *pango_layout_line_get_vis2log_map (PangoLayoutLine *line,
172 static void pango_layout_line_leaked (PangoLayoutLine *line);
174 /* doesn't leak line */
175 static PangoLayoutLine* _pango_layout_iter_get_line (PangoLayoutIter *iter);
177 static void pango_layout_get_item_properties (PangoItem *item,
178 ItemProperties *properties);
180 static void pango_layout_get_empty_extents_at_index (PangoLayout *layout,
182 PangoRectangle *logical_rect);
184 static void pango_layout_finalize (GObject *object);
186 G_DEFINE_TYPE (PangoLayout, pango_layout, G_TYPE_OBJECT)
189 pango_layout_init (PangoLayout *layout)
191 layout->attrs = NULL;
192 layout->font_desc = NULL;
199 layout->alignment = PANGO_ALIGN_LEFT;
200 layout->justify = FALSE;
201 layout->auto_dir = TRUE;
203 layout->log_attrs = NULL;
204 layout->lines = NULL;
205 layout->line_count = 0;
207 layout->tab_width = -1;
208 layout->unknown_glyphs_count = -1;
210 layout->wrap = PANGO_WRAP_WORD;
211 layout->is_wrapped = FALSE;
212 layout->ellipsize = PANGO_ELLIPSIZE_NONE;
213 layout->is_ellipsized = FALSE;
217 pango_layout_class_init (PangoLayoutClass *klass)
219 GObjectClass *object_class = G_OBJECT_CLASS (klass);
221 object_class->finalize = pango_layout_finalize;
225 pango_layout_finalize (GObject *object)
229 layout = PANGO_LAYOUT (object);
231 pango_layout_clear_lines (layout);
234 g_object_unref (layout->context);
237 pango_attr_list_unref (layout->attrs);
239 g_free (layout->text);
241 if (layout->font_desc)
242 pango_font_description_free (layout->font_desc);
245 pango_tab_array_free (layout->tabs);
247 G_OBJECT_CLASS (pango_layout_parent_class)->finalize (object);
253 * @context: a #PangoContext
255 * Create a new #PangoLayout object with attributes initialized to
256 * default values for a particular #PangoContext.
258 * Return value: the newly allocated #PangoLayout, with a reference
259 * count of one, which should be freed with
263 pango_layout_new (PangoContext *context)
267 g_return_val_if_fail (context != NULL, NULL);
269 layout = g_object_new (PANGO_TYPE_LAYOUT, NULL);
271 layout->context = context;
272 g_object_ref (context);
279 * @src: a #PangoLayout
281 * Does a deep copy-by-value of the @src layout. The attribute list,
282 * tab array, and text from the original layout are all copied by
285 * Return value: the newly allocated #PangoLayout, with a reference
286 * count of one, which should be freed with
290 pango_layout_copy (PangoLayout *src)
294 g_return_val_if_fail (PANGO_IS_LAYOUT (src), NULL);
296 /* Copy referenced members */
298 layout = pango_layout_new (src->context);
300 layout->attrs = pango_attr_list_copy (src->attrs);
302 layout->font_desc = pango_font_description_copy (src->font_desc);
304 layout->tabs = pango_tab_array_copy (src->tabs);
307 layout->text = g_strdup (src->text);
310 memcpy (&layout->copy_begin, &src->copy_begin,
311 G_STRUCT_OFFSET (PangoLayout, copy_end) - G_STRUCT_OFFSET (PangoLayout, copy_begin));
317 * pango_layout_get_context:
318 * @layout: a #PangoLayout
320 * Retrieves the #PangoContext used for this layout.
322 * Return value: the #PangoContext for the layout. This does not
323 * have an additional refcount added, so if you want to keep
324 * a copy of this around, you must reference it yourself.
327 pango_layout_get_context (PangoLayout *layout)
329 g_return_val_if_fail (layout != NULL, NULL);
331 return layout->context;
335 * pango_layout_set_width:
336 * @layout: a #PangoLayout.
337 * @width: the desired width in Pango units, or -1 to indicate that no
338 * wrapping or ellipsization should be performed.
340 * Sets the width to which the lines of the #PangoLayout should wrap or
341 * ellipsized. The default value is -1: no width set.
344 pango_layout_set_width (PangoLayout *layout,
347 g_return_if_fail (layout != NULL);
349 if (width != layout->width)
351 layout->width = width;
352 pango_layout_clear_lines (layout);
357 * pango_layout_get_width:
358 * @layout: a #PangoLayout
360 * Gets the width to which the lines of the #PangoLayout should wrap.
362 * Return value: the width in Pango units, or -1 if no width set.
365 pango_layout_get_width (PangoLayout *layout)
367 g_return_val_if_fail (layout != NULL, 0);
368 return layout->width;
372 * pango_layout_set_height:
373 * @layout: a #PangoLayout.
374 * @height: the desired height of the layout in Pango units if positive,
375 * or desired number of lines if negative.
377 * Sets the height to which the #PangoLayout should be ellipsized at. There
378 * are two different behaviors, based on whether @height is positive or
381 * If @height is positive, it will be the maximum height of the layout. Only
382 * lines would be shown that would fit, and if there is any text omitted,
383 * an ellipsis added. At least one line is included in each paragraph regardless
384 * of how small the height value is. A value of zero will render exactly one
385 * line for the entire layout.
387 * If @height is negative, it will be the (negative of) maximum number of lines per
388 * paragraph. That is, the total number of lines shown may well be more than
389 * this value if the layout contains multiple paragraphs of text.
390 * The default value of -1 means that first line of each paragraph is ellipsized.
391 * This behvaior may be changed in the future to act per layout instead of per
392 * paragraph. File a bug against pango at <ulink
393 * url="http://bugzilla.gnome.org/">http://bugzilla.gnome.org/</ulink> if your
394 * code relies on this behavior.
396 * Height setting only has effect if a positive width is set on
397 * @layout and ellipsization mode of @layout is not %PANGO_ELLIPSIZE_NONE.
398 * The behavior is undefined if a height other than -1 is set and
399 * ellipsization mode is set to %PANGO_ELLIPSIZE_NONE, and may change in the
405 pango_layout_set_height (PangoLayout *layout,
408 g_return_if_fail (layout != NULL);
410 if (height != layout->height)
412 layout->height = height;
414 /* Do not invalidate if the number of lines requested is
415 * larger than the total number of lines in layout.
418 if (layout->ellipsize != PANGO_ELLIPSIZE_NONE &&
419 !(layout->lines && layout->is_ellipsized == FALSE &&
420 height < 0 && layout->line_count <= (guint) -height))
421 pango_layout_clear_lines (layout);
426 * pango_layout_get_height:
427 * @layout: a #PangoLayout
429 * Gets the height of layout used for ellipsization. See
430 * pango_layout_set_height() for details.
432 * Return value: the height, in Pango units if positive, or
433 * number of lines if negative.
438 pango_layout_get_height (PangoLayout *layout)
440 g_return_val_if_fail (layout != NULL, 0);
441 return layout->height;
445 * pango_layout_set_wrap:
446 * @layout: a #PangoLayout
447 * @wrap: the wrap mode
449 * Sets the wrap mode; the wrap mode only has effect if a width
450 * is set on the layout with pango_layout_set_width().
451 * To turn off wrapping, set the width to -1.
454 pango_layout_set_wrap (PangoLayout *layout,
457 g_return_if_fail (PANGO_IS_LAYOUT (layout));
459 if (layout->wrap != wrap)
463 if (layout->width != -1)
464 pango_layout_clear_lines (layout);
469 * pango_layout_get_wrap:
470 * @layout: a #PangoLayout
472 * Gets the wrap mode for the layout.
474 * Use pango_layout_is_wrapped() to query whether any paragraphs
475 * were actually wrapped.
477 * Return value: active wrap mode.
480 pango_layout_get_wrap (PangoLayout *layout)
482 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), 0);
488 * pango_layout_is_wrapped:
489 * @layout: a #PangoLayout
491 * Queries whether the layout had to wrap any paragraphs.
493 * This returns %TRUE if a positive width is set on @layout,
494 * ellipsization mode of @layout is set to %PANGO_ELLIPSIZE_NONE,
495 * and there are paragraphs exceeding the layout width that have
498 * Return value: %TRUE if any paragraphs had to be wrapped, %FALSE
504 pango_layout_is_wrapped (PangoLayout *layout)
506 g_return_val_if_fail (layout != NULL, FALSE);
508 pango_layout_check_lines (layout);
510 return layout->is_wrapped;
514 * pango_layout_set_indent
515 * @layout: a #PangoLayout.
516 * @indent: the amount by which to indent.
518 * Sets the width in Pango units to indent each paragraph. A negative value
519 * of @indent will produce a hanging indentation. That is, the first line will
520 * have the full width, and subsequent lines will be indented by the
521 * absolute value of @indent.
523 * The indent setting is ignored if layout alignment is set to
524 * %PANGO_ALIGN_CENTER.
527 pango_layout_set_indent (PangoLayout *layout,
530 g_return_if_fail (layout != NULL);
532 if (indent != layout->indent)
534 layout->indent = indent;
535 pango_layout_clear_lines (layout);
540 * pango_layout_get_indent:
541 * @layout: a #PangoLayout
543 * Gets the paragraph indent width in Pango units. A negative value
544 * indicates a hanging indentation.
546 * Return value: the indent in Pango units.
549 pango_layout_get_indent (PangoLayout *layout)
551 g_return_val_if_fail (layout != NULL, 0);
552 return layout->indent;
556 * pango_layout_set_spacing:
557 * @layout: a #PangoLayout.
558 * @spacing: the amount of spacing
560 * Sets the amount of spacing in Pango unit between the lines of the
564 pango_layout_set_spacing (PangoLayout *layout,
567 g_return_if_fail (layout != NULL);
569 if (spacing != layout->spacing)
571 layout->spacing = spacing;
572 pango_layout_clear_lines (layout);
577 * pango_layout_get_spacing:
578 * @layout: a #PangoLayout
580 * Gets the amount of spacing between the lines of the layout.
582 * Return value: the spacing in Pango units.
585 pango_layout_get_spacing (PangoLayout *layout)
587 g_return_val_if_fail (layout != NULL, 0);
588 return layout->spacing;
592 * pango_layout_set_attributes:
593 * @layout: a #PangoLayout
594 * @attrs: a #PangoAttrList, can be %NULL
596 * Sets the text attributes for a layout object.
597 * References @attrs, so the caller can unref its reference.
600 pango_layout_set_attributes (PangoLayout *layout,
601 PangoAttrList *attrs)
603 PangoAttrList *old_attrs;
604 g_return_if_fail (layout != NULL);
606 old_attrs = layout->attrs;
608 /* We always clear lines such that this function can be called
609 * whenever attrs changes.
612 layout->attrs = attrs;
614 pango_attr_list_ref (layout->attrs);
615 pango_layout_clear_lines (layout);
618 pango_attr_list_unref (old_attrs);
619 layout->tab_width = -1;
623 * pango_layout_get_attributes:
624 * @layout: a #PangoLayout
626 * Gets the attribute list for the layout, if any.
628 * Return value: a #PangoAttrList.
631 pango_layout_get_attributes (PangoLayout *layout)
633 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
635 return layout->attrs;
639 * pango_layout_set_font_description:
640 * @layout: a #PangoLayout
641 * @desc: the new #PangoFontDescription, or %NULL to unset the
642 * current font description
644 * Sets the default font description for the layout. If no font
645 * description is set on the layout, the font description from
646 * the layout's context is used.
649 pango_layout_set_font_description (PangoLayout *layout,
650 const PangoFontDescription *desc)
652 g_return_if_fail (layout != NULL);
654 if (desc != layout->font_desc &&
655 (!desc || !layout->font_desc || !pango_font_description_equal(desc, layout->font_desc)))
657 if (layout->font_desc)
658 pango_font_description_free (layout->font_desc);
660 layout->font_desc = desc ? pango_font_description_copy (desc) : NULL;
662 pango_layout_clear_lines (layout);
663 layout->tab_width = -1;
668 * pango_layout_get_font_description:
669 * @layout: a #PangoLayout
671 * Gets the font description for the layout, if any.
673 * Return value: a pointer to the layout's font description,
674 * or %NULL if the font description from the layout's
675 * context is inherited. This value is owned by the layout
676 * and must not be modified or freed.
680 G_CONST_RETURN PangoFontDescription *
681 pango_layout_get_font_description (PangoLayout *layout)
683 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
685 return layout->font_desc;
689 * pango_layout_set_justify:
690 * @layout: a #PangoLayout
691 * @justify: whether the lines in the layout should be justified.
693 * Sets whether each complete line should be stretched to
694 * fill the entire width of the layout. This stretching is typically
695 * done by adding whitespace, but for some scripts (such as Arabic),
696 * the justification may be done in more complex ways, like extending
699 * Note that this setting is not implemented and so is ignored in Pango
703 pango_layout_set_justify (PangoLayout *layout,
706 g_return_if_fail (layout != NULL);
708 if (justify != layout->justify)
710 layout->justify = justify;
712 if (layout->is_ellipsized || layout->is_wrapped)
713 pango_layout_clear_lines (layout);
718 * pango_layout_get_justify:
719 * @layout: a #PangoLayout
721 * Gets whether each complete line should be stretched to fill the entire
722 * width of the layout.
724 * Return value: the justify.
727 pango_layout_get_justify (PangoLayout *layout)
729 g_return_val_if_fail (layout != NULL, FALSE);
730 return layout->justify;
734 * pango_layout_set_auto_dir:
735 * @layout: a #PangoLayout
736 * @auto_dir: if %TRUE, compute the bidirectional base direction
737 * from the layout's contents.
739 * Sets whether to calculate the bidirectional base direction
740 * for the layout according to the contents of the layout;
741 * when this flag is on (the default), then paragraphs in
742 @layout that begin with strong right-to-left characters
743 * (Arabic and Hebrew principally), will have right-to-left
744 * layout, paragraphs with letters from other scripts will
745 * have left-to-right layout. Paragraphs with only neutral
746 * characters get their direction from the surrounding paragraphs.
748 * When %FALSE, the choice between left-to-right and
749 * right-to-left layout is done according to the base direction
750 * of the layout's #PangoContext. (See pango_context_set_base_dir()).
752 * When the auto-computed direction of a paragraph differs from the
753 * base direction of the context, the interpretation of
754 * %PANGO_ALIGN_LEFT and %PANGO_ALIGN_RIGHT are swapped.
759 pango_layout_set_auto_dir (PangoLayout *layout,
762 g_return_if_fail (PANGO_IS_LAYOUT (layout));
764 auto_dir = auto_dir != FALSE;
766 if (auto_dir != layout->auto_dir)
768 layout->auto_dir = auto_dir;
769 pango_layout_clear_lines (layout);
774 * pango_layout_get_auto_dir:
775 * @layout: a #PangoLayout
777 * Gets whether to calculate the bidirectional base direction
778 * for the layout according to the contents of the layout.
779 * See pango_layout_set_auto_dir().
781 * Return value: %TRUE if the bidirectional base direction
782 * is computed from the layout's contents, %FALSE otherwise.
787 pango_layout_get_auto_dir (PangoLayout *layout)
789 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE);
791 return layout->auto_dir;
795 * pango_layout_set_alignment:
796 * @layout: a #PangoLayout
797 * @alignment: the alignment
799 * Sets the alignment for the layout: how partial lines are
800 * positioned within the horizontal space available.
803 pango_layout_set_alignment (PangoLayout *layout,
804 PangoAlignment alignment)
806 g_return_if_fail (layout != NULL);
808 if (alignment != layout->alignment)
810 layout->alignment = alignment;
811 pango_layout_clear_lines (layout);
816 * pango_layout_get_alignment:
817 * @layout: a #PangoLayout
819 * Gets the alignment for the layout: how partial lines are
820 * positioned within the horizontal space available.
822 * Return value: the alignment.
825 pango_layout_get_alignment (PangoLayout *layout)
827 g_return_val_if_fail (layout != NULL, PANGO_ALIGN_LEFT);
828 return layout->alignment;
833 * pango_layout_set_tabs:
834 * @layout: a #PangoLayout
835 * @tabs: a #PangoTabArray, or %NULL
837 * Sets the tabs to use for @layout, overriding the default tabs
838 * (by default, tabs are every 8 spaces). If @tabs is %NULL, the default
839 * tabs are reinstated. @tabs is copied into the layout; you must
840 * free your copy of @tabs yourself.
843 pango_layout_set_tabs (PangoLayout *layout,
846 g_return_if_fail (PANGO_IS_LAYOUT (layout));
849 if (tabs != layout->tabs)
852 pango_tab_array_free (layout->tabs);
854 layout->tabs = tabs ? pango_tab_array_copy (tabs) : NULL;
856 pango_layout_clear_lines (layout);
861 * pango_layout_get_tabs:
862 * @layout: a #PangoLayout
864 * Gets the current #PangoTabArray used by this layout. If no
865 * #PangoTabArray has been set, then the default tabs are in use
866 * and %NULL is returned. Default tabs are every 8 spaces.
867 * The return value should be freed with pango_tab_array_free().
869 * Return value: a copy of the tabs for this layout, or %NULL.
872 pango_layout_get_tabs (PangoLayout *layout)
874 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
877 return pango_tab_array_copy (layout->tabs);
883 * pango_layout_set_single_paragraph_mode:
884 * @layout: a #PangoLayout
885 * @setting: new setting
887 * If @setting is %TRUE, do not treat newlines and similar characters
888 * as paragraph separators; instead, keep all text in a single paragraph,
889 * and display a glyph for paragraph separator characters. Used when
890 * you want to allow editing of newlines on a single text line.
893 pango_layout_set_single_paragraph_mode (PangoLayout *layout,
896 g_return_if_fail (PANGO_IS_LAYOUT (layout));
898 setting = setting != FALSE;
900 if (layout->single_paragraph != setting)
902 layout->single_paragraph = setting;
904 pango_layout_clear_lines (layout);
909 * pango_layout_get_single_paragraph_mode:
910 * @layout: a #PangoLayout
912 * Obtains the value set by pango_layout_set_single_paragraph_mode().
914 * Return value: %TRUE if the layout does not break paragraphs at
915 * paragraph separator characters, %FALSE otherwise.
918 pango_layout_get_single_paragraph_mode (PangoLayout *layout)
920 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE);
922 return layout->single_paragraph;
926 * pango_layout_set_ellipsize:
927 * @layout: a #PangoLayout
928 * @ellipsize: the new ellipsization mode for @layout
930 * Sets the type of ellipsization being performed for @layout.
931 * Depending on the ellipsization mode @ellipsize text is
932 * removed from the start, middle, or end of text so they
933 * fit within the width and height of layout set with
934 * pango_layout_set_width() and pango_layout_set_height().
936 * If the layout contains characters such as newlines that
937 * force it to be layed out in multiple paragraphs, then whether
938 * each paragraph is ellipsized separately or the entire layout
939 * is ellipsized as a whole depends on the set height of the layout.
940 * See pango_layout_set_height() for details.
945 pango_layout_set_ellipsize (PangoLayout *layout,
946 PangoEllipsizeMode ellipsize)
948 g_return_if_fail (PANGO_IS_LAYOUT (layout));
950 if (ellipsize != layout->ellipsize)
952 layout->ellipsize = ellipsize;
954 if (layout->is_ellipsized || layout->is_wrapped)
955 pango_layout_clear_lines (layout);
960 * pango_layout_get_ellipsize:
961 * @layout: a #PangoLayout
963 * Gets the type of ellipsization being performed for @layout.
964 * See pango_layout_set_ellipsize()
966 * Return value: the current ellipsization mode for @layout.
968 * Use pango_layout_is_ellipsized() to query whether any paragraphs
969 * were actually ellipsized.
974 pango_layout_get_ellipsize (PangoLayout *layout)
976 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), PANGO_ELLIPSIZE_NONE);
978 return layout->ellipsize;
982 * pango_layout_is_ellipsized:
983 * @layout: a #PangoLayout
985 * Queries whether the layout had to ellipsize any paragraphs.
987 * This returns %TRUE if the ellipsization mode for @layout
988 * is not %PANGO_ELLIPSIZE_NONE, a positive width is set on @layout,
989 * and there are paragraphs exceeding that width that have to be
992 * Return value: %TRUE if any paragraphs had to be ellipsized, %FALSE
998 pango_layout_is_ellipsized (PangoLayout *layout)
1000 g_return_val_if_fail (layout != NULL, FALSE);
1002 pango_layout_check_lines (layout);
1004 return layout->is_ellipsized;
1008 * pango_layout_set_text:
1009 * @layout: a #PangoLayout
1010 * @text: a valid UTF-8 string
1011 * @length: maximum length of @text, in bytes. -1 indicates that
1012 * the string is nul-terminated and the length should be
1013 * calculated. The text will also be truncated on
1014 * encountering a nul-termination even when @length is
1017 * Sets the text of the layout.
1019 * Note that if you have used
1020 * pango_layout_set_markup() or pango_layout_set_markup_with_accel() on
1021 * @layout before, you may want to call pango_layout_set_attributes() to clear
1022 * the attributes set on the layout from the markup as this function does not
1026 pango_layout_set_text (PangoLayout *layout,
1030 char *old_text, *start, *end;
1032 g_return_if_fail (layout != NULL);
1033 g_return_if_fail (length == 0 || text != NULL);
1035 old_text = layout->text;
1038 layout->text = g_strdup (text);
1039 else if (length > 0)
1040 /* This is not exactly what we want. We don't need the padding...
1042 layout->text = g_strndup (text, length);
1044 layout->text = g_malloc0 (1);
1046 layout->length = strlen (layout->text);
1048 /* validate it, and replace invalid bytes with '?'
1050 start = layout->text;
1054 valid = g_utf8_validate (start, -1, (const char **)&end);
1059 /* Replace invalid bytes with -1. The -1 will be converted to
1060 * ((gunichar) -1) by glib, and that in turn yields a glyph value of
1061 * ((PangoGlyph) -1) by PANGO_GET_UNKNOWN_GLYPH(-1),
1062 * and that's PANGO_GLYPH_INVALID_INPUT.
1070 if (start != layout->text)
1071 /* TODO: Write out the beginning excerpt of text? */
1072 g_warning ("Invalid UTF-8 string passed to pango_layout_set_text()");
1074 layout->n_chars = pango_utf8_strlen (layout->text, -1);
1076 pango_layout_clear_lines (layout);
1082 * pango_layout_get_text:
1083 * @layout: a #PangoLayout
1085 * Gets the text in the layout. The returned text should not
1086 * be freed or modified.
1088 * Return value: the text in the @layout.
1090 G_CONST_RETURN char*
1091 pango_layout_get_text (PangoLayout *layout)
1093 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
1095 return layout->text;
1099 * pango_layout_set_markup:
1100 * @layout: a #PangoLayout
1101 * @markup: marked-up text
1102 * @length: length of marked-up text in bytes, or -1 if @markup is
1105 * Same as pango_layout_set_markup_with_accel(), but
1106 * the markup text isn't scanned for accelerators.
1110 pango_layout_set_markup (PangoLayout *layout,
1114 pango_layout_set_markup_with_accel (layout, markup, length, 0, NULL);
1118 * pango_layout_set_markup_with_accel:
1119 * @layout: a #PangoLayout
1120 * @markup: marked-up text
1121 * (see <link linkend="PangoMarkupFormat">markup format</link>)
1122 * @length: length of marked-up text in bytes, or -1 if @markup is
1124 * @accel_marker: marker for accelerators in the text
1125 * @accel_char: return location for first located accelerator, or %NULL
1127 * Sets the layout text and attribute list from marked-up text (see
1128 * <link linkend="PangoMarkupFormat">markup format</link>). Replaces
1129 * the current text and attribute list.
1131 * If @accel_marker is nonzero, the given character will mark the
1132 * character following it as an accelerator. For example, @accel_marker
1133 * might be an ampersand or underscore. All characters marked
1134 * as an accelerator will receive a %PANGO_UNDERLINE_LOW attribute,
1135 * and the first character so marked will be returned in @accel_char.
1136 * Two @accel_marker characters following each other produce a single
1137 * literal @accel_marker character.
1140 pango_layout_set_markup_with_accel (PangoLayout *layout,
1143 gunichar accel_marker,
1144 gunichar *accel_char)
1146 PangoAttrList *list = NULL;
1150 g_return_if_fail (PANGO_IS_LAYOUT (layout));
1151 g_return_if_fail (markup != NULL);
1154 if (!pango_parse_markup (markup, length,
1160 g_warning ("pango_layout_set_markup_with_accel: %s", error->message);
1161 g_error_free (error);
1165 pango_layout_set_text (layout, text, -1);
1166 pango_layout_set_attributes (layout, list);
1167 pango_attr_list_unref (list);
1172 * pango_layout_get_unknown_glyphs_count:
1173 * @layout: a #PangoLayout
1175 * Counts the number unknown glyphs in @layout. That is, zero if
1176 * glyphs for all characters in the layout text were found, or more
1177 * than zero otherwise.
1179 * This function can be used to determine if there are any fonts
1180 * available to render all characters in a certain string, or when
1181 * used in combination with %PANGO_ATTR_FALLBACK, to check if a
1182 * certain font supports all the characters in the string.
1184 * Return value: The number of unknown glyphs in @layout.
1189 pango_layout_get_unknown_glyphs_count (PangoLayout *layout)
1191 PangoLayoutLine *line;
1192 PangoLayoutRun *run;
1197 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), 0);
1199 pango_layout_check_lines (layout);
1201 if (layout->unknown_glyphs_count >= 0)
1202 return layout->unknown_glyphs_count;
1204 lines_list = layout->lines;
1207 line = lines_list->data;
1208 runs_list = line->runs;
1212 run = runs_list->data;
1214 for (i = 0; i < run->glyphs->num_glyphs; i++)
1216 if (run->glyphs->glyphs[i].glyph & PANGO_GLYPH_UNKNOWN_FLAG)
1220 runs_list = runs_list->next;
1222 lines_list = lines_list->next;
1225 layout->unknown_glyphs_count = count;
1230 * pango_layout_context_changed:
1231 * @layout: a #PangoLayout
1233 * Forces recomputation of any state in the #PangoLayout that
1234 * might depend on the layout's context. This function should
1235 * be called if you make changes to the context subsequent
1236 * to creating the layout.
1239 pango_layout_context_changed (PangoLayout *layout)
1241 pango_layout_clear_lines (layout);
1242 layout->tab_width = -1;
1246 * pango_layout_get_log_attrs:
1247 * @layout: a #PangoLayout
1248 * @attrs: location to store a pointer to an array of logical attributes
1249 * This value must be freed with g_free().
1250 * @n_attrs: location to store the number of the attributes in the
1251 * array. (The stored value will be one more than the total number
1252 * of characters in the layout, since there need to be attributes
1253 * corresponding to both the position before the first character
1254 * and the position after the last character.)
1256 * Retrieves an array of logical attributes for each character in
1260 pango_layout_get_log_attrs (PangoLayout *layout,
1261 PangoLogAttr **attrs,
1264 g_return_if_fail (layout != NULL);
1266 pango_layout_check_lines (layout);
1270 *attrs = g_new (PangoLogAttr, layout->n_chars + 1);
1271 memcpy (*attrs, layout->log_attrs, sizeof(PangoLogAttr) * (layout->n_chars + 1));
1275 *n_attrs = layout->n_chars + 1;
1280 * pango_layout_get_line_count:
1281 * @layout: #PangoLayout
1283 * Retrieves the count of lines for the @layout.
1285 * Return value: the line count.
1288 pango_layout_get_line_count (PangoLayout *layout)
1290 g_return_val_if_fail (layout != NULL, 0);
1292 pango_layout_check_lines (layout);
1293 return layout->line_count;
1297 * pango_layout_get_lines:
1298 * @layout: a #PangoLayout
1300 * Returns the lines of the @layout as a list.
1302 * Use the faster pango_layout_get_lines_readonly() if you do not plan
1303 * to modify the contents of the lines (glyphs, glyph widths, etc.).
1305 * Return value: (element-type Pango.LayoutLine): (transfer none): a #GSList containing
1306 * the lines in the layout. This points to internal data of the #PangoLayout
1307 * and must be used with care. It will become invalid on any change to the layout's
1308 * text or properties.
1311 pango_layout_get_lines (PangoLayout *layout)
1313 pango_layout_check_lines (layout);
1317 GSList *tmp_list = layout->lines;
1320 PangoLayoutLine *line = tmp_list->data;
1321 tmp_list = tmp_list->next;
1323 pango_layout_line_leaked (line);
1327 return layout->lines;
1331 * pango_layout_get_lines_readonly:
1332 * @layout: a #PangoLayout
1334 * Returns the lines of the @layout as a list.
1336 * This is a faster alternative to pango_layout_get_lines(),
1337 * but the user is not expected
1338 * to modify the contents of the lines (glyphs, glyph widths, etc.).
1340 * Return value: (element-type Pango.LayoutLine): (transfer none): a #GSList containing
1341 * the lines in the layout. This points to internal data of the #PangoLayout and
1342 * must be used with care. It will become invalid on any change to the layout's
1343 * text or properties. No changes should be made to the lines.
1348 pango_layout_get_lines_readonly (PangoLayout *layout)
1350 pango_layout_check_lines (layout);
1352 return layout->lines;
1356 * pango_layout_get_line:
1357 * @layout: a #PangoLayout
1358 * @line: the index of a line, which must be between 0 and
1359 * <literal>pango_layout_get_line_count(layout) - 1</literal>, inclusive.
1361 * Retrieves a particular line from a #PangoLayout.
1363 * Use the faster pango_layout_get_line_readonly() if you do not plan
1364 * to modify the contents of the line (glyphs, glyph widths, etc.).
1366 * Return value: the requested #PangoLayoutLine, or %NULL if the
1367 * index is out of range. This layout line can
1368 * be ref'ed and retained, but will become invalid
1369 * if changes are made to the #PangoLayout.
1372 pango_layout_get_line (PangoLayout *layout,
1376 g_return_val_if_fail (layout != NULL, NULL);
1377 g_return_val_if_fail (line >= 0, NULL);
1382 pango_layout_check_lines (layout);
1384 list_item = g_slist_nth (layout->lines, line);
1388 PangoLayoutLine *line = list_item->data;
1390 pango_layout_line_leaked (line);
1398 * pango_layout_get_line_readonly:
1399 * @layout: a #PangoLayout
1400 * @line: the index of a line, which must be between 0 and
1401 * <literal>pango_layout_get_line_count(layout) - 1</literal>, inclusive.
1403 * Retrieves a particular line from a #PangoLayout.
1405 * This is a faster alternative to pango_layout_get_line(),
1406 * but the user is not expected
1407 * to modify the contents of the line (glyphs, glyph widths, etc.).
1409 * Return value: the requested #PangoLayoutLine, or %NULL if the
1410 * index is out of range. This layout line can
1411 * be ref'ed and retained, but will become invalid
1412 * if changes are made to the #PangoLayout.
1413 * No changes should be made to the line.
1418 pango_layout_get_line_readonly (PangoLayout *layout,
1422 g_return_val_if_fail (layout != NULL, NULL);
1423 g_return_val_if_fail (line >= 0, NULL);
1428 pango_layout_check_lines (layout);
1430 list_item = g_slist_nth (layout->lines, line);
1434 PangoLayoutLine *line = list_item->data;
1442 * pango_layout_line_index_to_x:
1443 * @line: a #PangoLayoutLine
1444 * @index_: byte offset of a grapheme within the layout
1445 * @trailing: an integer indicating the edge of the grapheme to retrieve
1446 * the position of. If > 0, the trailing edge of the grapheme,
1447 * if 0, the leading of the grapheme.
1448 * @x_pos: location to store the x_offset (in Pango unit)
1450 * Converts an index within a line to a X position.
1454 pango_layout_line_index_to_x (PangoLayoutLine *line,
1459 PangoLayout *layout = line->layout;
1460 GSList *run_list = line->runs;
1465 PangoLayoutRun *run = run_list->data;
1466 ItemProperties properties;
1468 pango_layout_get_item_properties (run->item, &properties);
1470 if (run->item->offset <= index && run->item->offset + run->item->length > index)
1472 int offset = g_utf8_pointer_to_offset (layout->text, layout->text + index);
1475 while (index < line->start_index + line->length &&
1476 offset + 1 < layout->n_chars &&
1477 !layout->log_attrs[offset + 1].is_cursor_position)
1480 index = g_utf8_next_char (layout->text + index) - layout->text;
1485 while (index > line->start_index &&
1486 !layout->log_attrs[offset].is_cursor_position)
1489 index = g_utf8_prev_char (layout->text + index) - layout->text;
1494 pango_glyph_string_index_to_x (run->glyphs,
1495 layout->text + run->item->offset,
1497 &run->item->analysis,
1498 index - run->item->offset, trailing, x_pos);
1505 width += pango_glyph_string_get_width (run->glyphs);
1507 run_list = run_list->next;
1514 static PangoLayoutLine *
1515 pango_layout_index_to_line (PangoLayout *layout,
1518 PangoLayoutLine **line_before,
1519 PangoLayoutLine **line_after)
1523 PangoLayoutLine *line = NULL;
1524 PangoLayoutLine *prev_line = NULL;
1527 line_list = tmp_list = layout->lines;
1530 PangoLayoutLine *tmp_line = tmp_list->data;
1532 if (tmp_line->start_index > index)
1533 break; /* index was in paragraph delimiters */
1537 line_list = tmp_list;
1540 if (line->start_index + line->length > index)
1543 tmp_list = tmp_list->next;
1550 *line_before = prev_line;
1553 *line_after = (line_list && line_list->next) ? line_list->next->data : NULL;
1558 static PangoLayoutLine *
1559 pango_layout_index_to_line_and_extents (PangoLayout *layout,
1561 PangoRectangle *line_rect)
1563 PangoLayoutIter *iter;
1564 PangoLayoutLine *line = NULL;
1566 iter = pango_layout_get_iter (layout);
1568 if (!ITER_IS_INVALID (iter))
1571 PangoLayoutLine *tmp_line = _pango_layout_iter_get_line (iter);
1573 if (tmp_line->start_index > index)
1574 break; /* index was in paragraph delimiters */
1578 pango_layout_iter_get_line_extents (iter, NULL, line_rect);
1580 if (line->start_index + line->length > index)
1583 if (!pango_layout_iter_next_line (iter))
1584 break; /* Use end of last line */
1587 pango_layout_iter_free (iter);
1593 * pango_layout_index_to_line_x:
1594 * @layout: a #PangoLayout
1595 * @index_: the byte index of a grapheme within the layout.
1596 * @trailing: an integer indicating the edge of the grapheme to retrieve the
1597 * position of. If 0, the trailing edge of the grapheme, if > 0,
1598 * the leading of the grapheme.
1599 * @line: location to store resulting line index. (which will
1600 * between 0 and pango_layout_get_line_count(layout) - 1)
1601 * @x_pos: location to store resulting position within line
1602 * (%PANGO_SCALE units per device unit)
1604 * Converts from byte @index_ within the @layout to line and X position.
1605 * (X position is measured from the left edge of the line)
1608 pango_layout_index_to_line_x (PangoLayout *layout,
1615 PangoLayoutLine *layout_line = NULL;
1617 g_return_if_fail (layout != NULL);
1618 g_return_if_fail (index >= 0);
1619 g_return_if_fail (index <= layout->length);
1621 pango_layout_check_lines (layout);
1623 layout_line = pango_layout_index_to_line (layout, index,
1624 &line_num, NULL, NULL);
1628 /* use end of line if index was in the paragraph delimiters */
1629 if (index > layout_line->start_index + layout_line->length)
1630 index = layout_line->start_index + layout_line->length;
1635 pango_layout_line_index_to_x (layout_line, index, trailing, x_pos);
1647 * pango_layout_move_cursor_visually:
1648 * @layout: a #PangoLayout.
1649 * @strong: whether the moving cursor is the strong cursor or the
1650 * weak cursor. The strong cursor is the cursor corresponding
1651 * to text insertion in the base direction for the layout.
1652 * @old_index: the byte index of the grapheme for the old index
1653 * @old_trailing: if 0, the cursor was at the trailing edge of the
1654 * grapheme indicated by @old_index, if > 0, the cursor
1655 * was at the leading edge.
1656 * @direction: direction to move cursor. A negative
1657 * value indicates motion to the left.
1658 * @new_index: location to store the new cursor byte index. A value of -1
1659 * indicates that the cursor has been moved off the beginning
1660 * of the layout. A value of %G_MAXINT indicates that
1661 * the cursor has been moved off the end of the layout.
1662 * @new_trailing: number of characters to move forward from the location returned
1663 * for @new_index to get the position where the cursor should
1664 * be displayed. This allows distinguishing the position at
1665 * the beginning of one line from the position at the end
1666 * of the preceding line. @new_index is always on the line
1667 * where the cursor should be displayed.
1669 * Computes a new cursor position from an old position and
1670 * a count of positions to move visually. If @direction is positive,
1671 * then the new strong cursor position will be one position
1672 * to the right of the old cursor position. If @direction is negative,
1673 * then the new strong cursor position will be one position
1674 * to the left of the old cursor position.
1676 * In the presence of bidirectional text, the correspondence
1677 * between logical and visual order will depend on the direction
1678 * of the current run, and there may be jumps when the cursor
1679 * is moved off of the end of a run.
1681 * Motion here is in cursor positions, not in characters, so a
1682 * single call to pango_layout_move_cursor_visually() may move the
1683 * cursor over multiple characters when multiple characters combine
1684 * to form a single grapheme.
1687 pango_layout_move_cursor_visually (PangoLayout *layout,
1695 PangoLayoutLine *line = NULL;
1696 PangoLayoutLine *prev_line;
1697 PangoLayoutLine *next_line;
1702 int vis_pos, vis_pos_old, log_pos;
1704 gboolean off_start = FALSE;
1705 gboolean off_end = FALSE;
1707 g_return_if_fail (layout != NULL);
1708 g_return_if_fail (old_index >= 0 && old_index <= layout->length);
1709 g_return_if_fail (old_index < layout->length || old_trailing == 0);
1710 g_return_if_fail (new_index != NULL);
1711 g_return_if_fail (new_trailing != NULL);
1713 direction = (direction >= 0 ? 1 : -1);
1715 pango_layout_check_lines (layout);
1717 /* Find the line the old cursor is on */
1718 line = pango_layout_index_to_line (layout, old_index,
1719 NULL, &prev_line, &next_line);
1721 start_offset = g_utf8_pointer_to_offset (layout->text, layout->text + line->start_index);
1723 while (old_trailing--)
1724 old_index = g_utf8_next_char (layout->text + old_index) - layout->text;
1726 log2vis_map = pango_layout_line_get_log2vis_map (line, strong);
1727 n_vis = pango_utf8_strlen (layout->text + line->start_index, line->length);
1729 /* Clamp old_index to fit on the line */
1730 if (old_index > (line->start_index + line->length))
1731 old_index = line->start_index + line->length;
1733 vis_pos = log2vis_map[old_index - line->start_index];
1735 g_free (log2vis_map);
1737 /* Handling movement between lines */
1738 if (vis_pos == 0 && direction < 0)
1740 if (line->resolved_dir == PANGO_DIRECTION_LTR)
1745 else if (vis_pos == n_vis && direction > 0)
1747 if (line->resolved_dir == PANGO_DIRECTION_LTR)
1753 if (off_start || off_end)
1755 /* If we move over a paragraph boundary, count that as
1756 * an extra position in the motion
1758 gboolean paragraph_boundary;
1769 paragraph_boundary = (line->start_index + line->length != old_index);
1775 *new_index = G_MAXINT;
1780 paragraph_boundary = (line->start_index != old_index);
1783 n_vis = pango_utf8_strlen (layout->text + line->start_index, line->length);
1784 start_offset = g_utf8_pointer_to_offset (layout->text, layout->text + line->start_index);
1786 if (vis_pos == 0 && direction < 0)
1789 if (paragraph_boundary)
1792 else /* (vis_pos == n_vis && direction > 0) */
1795 if (paragraph_boundary)
1800 vis2log_map = pango_layout_line_get_vis2log_map (line, strong);
1802 vis_pos_old = vis_pos + direction;
1803 log_pos = g_utf8_pointer_to_offset (layout->text + line->start_index,
1804 layout->text + line->start_index + vis2log_map[vis_pos_old]);
1807 vis_pos += direction;
1808 log_pos += g_utf8_pointer_to_offset (layout->text + line->start_index + vis2log_map[vis_pos_old],
1809 layout->text + line->start_index + vis2log_map[vis_pos]);
1810 vis_pos_old = vis_pos;
1812 while (vis_pos > 0 && vis_pos < n_vis &&
1813 !layout->log_attrs[start_offset + log_pos].is_cursor_position);
1815 *new_index = line->start_index + vis2log_map[vis_pos];
1816 g_free (vis2log_map);
1820 if (*new_index == line->start_index + line->length && line->length > 0)
1825 *new_index = g_utf8_prev_char (layout->text + *new_index) - layout->text;
1828 while (log_pos > 0 && !layout->log_attrs[start_offset + log_pos].is_cursor_position);
1833 * pango_layout_xy_to_index:
1834 * @layout: a #PangoLayout
1835 * @x: the X offset (in Pango units)
1836 * from the left edge of the layout.
1837 * @y: the Y offset (in Pango units)
1838 * from the top edge of the layout
1839 * @index_: location to store calculated byte index
1840 * @trailing: location to store a integer indicating where
1841 * in the grapheme the user clicked. It will either
1842 * be zero, or the number of characters in the
1843 * grapheme. 0 represents the trailing edge of the grapheme.
1845 * Converts from X and Y position within a layout to the byte
1846 * index to the character at that logical position. If the
1847 * Y position is not inside the layout, the closest position is chosen
1848 * (the position will be clamped inside the layout). If the
1849 * X position is not within the layout, then the start or the
1850 * end of the line is chosen as described for pango_layout_x_to_index().
1851 * If either the X or Y positions were not inside the layout, then the
1852 * function returns %FALSE; on an exact hit, it returns %TRUE.
1854 * Return value: %TRUE if the coordinates were inside text, %FALSE otherwise.
1857 pango_layout_xy_to_index (PangoLayout *layout,
1863 PangoLayoutIter *iter;
1864 PangoLayoutLine *prev_line = NULL;
1865 PangoLayoutLine *found = NULL;
1866 int found_line_x = 0;
1868 int prev_line_x = 0;
1869 gboolean retval = FALSE;
1870 gboolean outside = FALSE;
1872 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE);
1874 iter = pango_layout_get_iter (layout);
1878 PangoRectangle line_logical;
1879 int first_y, last_y;
1881 pango_layout_iter_get_line_extents (iter, NULL, &line_logical);
1882 pango_layout_iter_get_line_yrange (iter, &first_y, &last_y);
1886 if (prev_line && y < (prev_last + (first_y - prev_last) / 2))
1889 found_line_x = prev_line_x;
1893 if (prev_line == NULL)
1894 outside = TRUE; /* off the top */
1896 found = _pango_layout_iter_get_line (iter);
1897 found_line_x = x - line_logical.x;
1900 else if (y >= first_y &&
1903 found = _pango_layout_iter_get_line (iter);
1904 found_line_x = x - line_logical.x;
1907 prev_line = _pango_layout_iter_get_line (iter);
1909 prev_line_x = x - line_logical.x;
1914 while (pango_layout_iter_next_line (iter));
1916 pango_layout_iter_free (iter);
1920 /* Off the bottom of the layout */
1924 found_line_x = prev_line_x;
1927 retval = pango_layout_line_x_to_index (found,
1938 * pango_layout_index_to_pos:
1939 * @layout: a #PangoLayout
1940 * @index_: byte index within @layout
1941 * @pos: rectangle in which to store the position of the grapheme
1943 * Converts from an index within a #PangoLayout to the onscreen position
1944 * corresponding to the grapheme at that index, which is represented
1945 * as rectangle. Note that <literal>pos->x</literal> is always the leading
1946 * edge of the grapheme and <literal>pos->x + pos->width</literal> the trailing
1947 * edge of the grapheme. If the directionality of the grapheme is right-to-left,
1948 * then <literal>pos->width</literal> will be negative.
1951 pango_layout_index_to_pos (PangoLayout *layout,
1953 PangoRectangle *pos)
1955 PangoRectangle logical_rect;
1956 PangoLayoutIter *iter;
1957 PangoLayoutLine *layout_line = NULL;
1960 g_return_if_fail (layout != NULL);
1961 g_return_if_fail (index >= 0);
1962 g_return_if_fail (pos != NULL);
1964 iter = pango_layout_get_iter (layout);
1966 if (!ITER_IS_INVALID (iter))
1970 PangoLayoutLine *tmp_line = _pango_layout_iter_get_line (iter);
1972 if (tmp_line->start_index > index)
1974 /* index is in the paragraph delimiters, move to
1975 * end of previous line
1977 index = layout_line->start_index + layout_line->length;
1981 layout_line = tmp_line;
1983 pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
1985 if (layout_line->start_index + layout_line->length > index)
1988 if (!pango_layout_iter_next_line (iter))
1990 index = layout_line->start_index + layout_line->length;
1995 pos->y = logical_rect.y;
1996 pos->height = logical_rect.height;
1998 pango_layout_line_index_to_x (layout_line, index, 0, &x_pos);
1999 pos->x = logical_rect.x + x_pos;
2001 if (index < layout_line->start_index + layout_line->length)
2003 pango_layout_line_index_to_x (layout_line, index, 1, &x_pos);
2004 pos->width = (logical_rect.x + x_pos) - pos->x;
2010 pango_layout_iter_free (iter);
2014 pango_layout_line_get_range (PangoLayoutLine *line,
2020 p = line->layout->text + line->start_index;
2025 *end = p + line->length;
2029 pango_layout_line_get_vis2log_map (PangoLayoutLine *line,
2032 PangoLayout *layout = line->layout;
2033 PangoDirection prev_dir;
2034 PangoDirection cursor_dir;
2041 pango_layout_line_get_range (line, &start, &end);
2042 n_chars = pango_utf8_strlen (start, end - start);
2044 result = g_new (int, n_chars + 1);
2047 cursor_dir = line->resolved_dir;
2049 cursor_dir = (line->resolved_dir == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
2051 /* Handle the first visual position
2053 if (line->resolved_dir == cursor_dir)
2054 result[0] = line->resolved_dir == PANGO_DIRECTION_LTR ? 0 : end - start;
2056 prev_dir = line->resolved_dir;
2058 tmp_list = line->runs;
2061 PangoLayoutRun *run = tmp_list->data;
2062 int run_n_chars = run->item->num_chars;
2063 PangoDirection run_dir = (run->item->analysis.level % 2) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
2064 char *p = layout->text + run->item->offset;
2067 /* pos is the visual position at the start of the run */
2068 /* p is the logical byte index at the start of the run */
2070 if (run_dir == PANGO_DIRECTION_LTR)
2072 if ((cursor_dir == PANGO_DIRECTION_LTR) ||
2073 (prev_dir == run_dir))
2074 result[pos] = p - start;
2076 p = g_utf8_next_char (p);
2078 for (i = 1; i < run_n_chars; i++)
2080 result[pos + i] = p - start;
2081 p = g_utf8_next_char (p);
2084 if (cursor_dir == PANGO_DIRECTION_LTR)
2085 result[pos + run_n_chars] = p - start;
2089 if (cursor_dir == PANGO_DIRECTION_RTL)
2090 result[pos + run_n_chars] = p - start;
2092 p = g_utf8_next_char (p);
2094 for (i = 1; i < run_n_chars; i++)
2096 result[pos + run_n_chars - i] = p - start;
2097 p = g_utf8_next_char (p);
2100 if ((cursor_dir == PANGO_DIRECTION_RTL) ||
2101 (prev_dir == run_dir))
2102 result[pos] = p - start;
2107 tmp_list = tmp_list->next;
2110 /* And the last visual position
2112 if ((cursor_dir == line->resolved_dir) || (prev_dir == line->resolved_dir))
2113 result[pos] = line->resolved_dir == PANGO_DIRECTION_LTR ? end - start : 0;
2119 pango_layout_line_get_log2vis_map (PangoLayoutLine *line,
2128 pango_layout_line_get_range (line, &start, &end);
2129 n_chars = pango_utf8_strlen (start, end - start);
2130 result = g_new0 (int, end - start + 1);
2132 reverse_map = pango_layout_line_get_vis2log_map (line, strong);
2134 for (i=0; i <= n_chars; i++)
2135 result[reverse_map[i]] = i;
2137 g_free (reverse_map);
2142 static PangoDirection
2143 pango_layout_line_get_char_direction (PangoLayoutLine *layout_line,
2148 run_list = layout_line->runs;
2151 PangoLayoutRun *run = run_list->data;
2153 if (run->item->offset <= index && run->item->offset + run->item->length > index)
2154 return run->item->analysis.level % 2 ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
2156 run_list = run_list->next;
2159 g_assert_not_reached ();
2161 return PANGO_DIRECTION_LTR;
2165 * pango_layout_get_cursor_pos:
2166 * @layout: a #PangoLayout
2167 * @index_: the byte index of the cursor
2168 * @strong_pos: location to store the strong cursor position (may be %NULL)
2169 * @weak_pos: location to store the weak cursor position (may be %NULL)
2171 * Given an index within a layout, determines the positions that of the
2172 * strong and weak cursors if the insertion point is at that
2173 * index. The position of each cursor is stored as a zero-width
2174 * rectangle. The strong cursor location is the location where
2175 * characters of the directionality equal to the base direction of the
2176 * layout are inserted. The weak cursor location is the location
2177 * where characters of the directionality opposite to the base
2178 * direction of the layout are inserted.
2181 pango_layout_get_cursor_pos (PangoLayout *layout,
2183 PangoRectangle *strong_pos,
2184 PangoRectangle *weak_pos)
2186 PangoDirection dir1;
2187 PangoRectangle line_rect;
2188 PangoLayoutLine *layout_line = NULL; /* Quiet GCC */
2192 g_return_if_fail (layout != NULL);
2193 g_return_if_fail (index >= 0 && index <= layout->length);
2195 layout_line = pango_layout_index_to_line_and_extents (layout, index,
2198 g_assert (index >= layout_line->start_index);
2200 /* Examine the trailing edge of the character before the cursor */
2201 if (index == layout_line->start_index)
2203 dir1 = layout_line->resolved_dir;
2204 if (layout_line->resolved_dir == PANGO_DIRECTION_LTR)
2207 x1_trailing = line_rect.width;
2209 else if (index >= layout_line->start_index + layout_line->length)
2211 dir1 = layout_line->resolved_dir;
2212 if (layout_line->resolved_dir == PANGO_DIRECTION_LTR)
2213 x1_trailing = line_rect.width;
2219 gint prev_index = g_utf8_prev_char (layout->text + index) - layout->text;
2220 dir1 = pango_layout_line_get_char_direction (layout_line, prev_index);
2221 pango_layout_line_index_to_x (layout_line, prev_index, TRUE, &x1_trailing);
2224 /* Examine the leading edge of the character after the cursor */
2225 if (index >= layout_line->start_index + layout_line->length)
2227 if (layout_line->resolved_dir == PANGO_DIRECTION_LTR)
2228 x2 = line_rect.width;
2234 pango_layout_line_index_to_x (layout_line, index, FALSE, &x2);
2239 strong_pos->x = line_rect.x;
2241 if (dir1 == layout_line->resolved_dir)
2242 strong_pos->x += x1_trailing;
2244 strong_pos->x += x2;
2246 strong_pos->y = line_rect.y;
2247 strong_pos->width = 0;
2248 strong_pos->height = line_rect.height;
2253 weak_pos->x = line_rect.x;
2255 if (dir1 == layout_line->resolved_dir)
2258 weak_pos->x += x1_trailing;
2260 weak_pos->y = line_rect.y;
2261 weak_pos->width = 0;
2262 weak_pos->height = line_rect.height;
2267 direction_simple (PangoDirection d)
2271 case PANGO_DIRECTION_LTR :
2272 case PANGO_DIRECTION_WEAK_LTR :
2273 case PANGO_DIRECTION_TTB_RTL :
2275 case PANGO_DIRECTION_RTL :
2276 case PANGO_DIRECTION_WEAK_RTL :
2277 case PANGO_DIRECTION_TTB_LTR :
2279 case PANGO_DIRECTION_NEUTRAL :
2281 /* no default, compiler should complain if a new values is added */
2287 static PangoAlignment
2288 get_alignment (PangoLayout *layout,
2289 PangoLayoutLine *line)
2291 PangoAlignment alignment = layout->alignment;
2293 if (alignment != PANGO_ALIGN_CENTER && line->layout->auto_dir &&
2294 direction_simple (line->resolved_dir) ==
2295 -direction_simple (pango_context_get_base_dir (layout->context)))
2297 if (alignment == PANGO_ALIGN_LEFT)
2298 alignment = PANGO_ALIGN_RIGHT;
2299 else if (alignment == PANGO_ALIGN_RIGHT)
2300 alignment = PANGO_ALIGN_LEFT;
2307 get_x_offset (PangoLayout *layout,
2308 PangoLayoutLine *line,
2313 PangoAlignment alignment = get_alignment (layout, line);
2316 if (layout_width == 0)
2318 else if (alignment == PANGO_ALIGN_RIGHT)
2319 *x_offset = layout_width - line_width;
2320 else if (alignment == PANGO_ALIGN_CENTER) {
2321 *x_offset = (layout_width - line_width) / 2;
2323 if (((layout_width | line_width) & (PANGO_SCALE - 1)) == 0)
2325 *x_offset = PANGO_UNITS_ROUND (*x_offset);
2333 /* For center, we ignore indentation; I think I've seen word
2334 * processors that still do the indentation here as if it were
2335 * indented left/right, though we can't sensibly do that without
2336 * knowing whether left/right is the "normal" thing for this text
2339 if (alignment == PANGO_ALIGN_CENTER)
2342 if (line->is_paragraph_start)
2344 if (layout->indent > 0)
2346 if (alignment == PANGO_ALIGN_LEFT)
2347 *x_offset += layout->indent;
2349 *x_offset -= layout->indent;
2354 if (layout->indent < 0)
2356 if (alignment == PANGO_ALIGN_LEFT)
2357 *x_offset -= layout->indent;
2359 *x_offset += layout->indent;
2365 get_line_extents_layout_coords (PangoLayout *layout,
2366 PangoLayoutLine *line,
2370 PangoRectangle *line_ink_layout,
2371 PangoRectangle *line_logical_layout)
2374 /* Line extents in line coords (origin at line baseline) */
2375 PangoRectangle line_ink;
2376 PangoRectangle line_logical;
2378 pango_layout_line_get_extents (line, line_ink_layout ? &line_ink : NULL,
2381 get_x_offset (layout, line, layout_width, line_logical.width, &x_offset);
2383 /* Convert the line's extents into layout coordinates */
2384 if (line_ink_layout)
2386 *line_ink_layout = line_ink;
2387 line_ink_layout->x = line_ink.x + x_offset;
2388 line_ink_layout->y = y_offset - line_logical.y + line_ink.y;
2391 if (line_logical_layout)
2393 *line_logical_layout = line_logical;
2394 line_logical_layout->x = line_logical.x + x_offset;
2395 line_logical_layout->y = y_offset;
2399 *baseline = y_offset - line_logical.y;
2402 /* if non-NULL line_extents returns a list of line extents
2403 * in layout coordinates
2406 pango_layout_get_extents_internal (PangoLayout *layout,
2407 PangoRectangle *ink_rect,
2408 PangoRectangle *logical_rect,
2409 GSList **line_extents)
2414 gboolean need_width = FALSE;
2416 g_return_if_fail (layout != NULL);
2418 if (ink_rect && layout->ink_rect_cached)
2420 *ink_rect = layout->ink_rect;
2423 if (logical_rect && layout->logical_rect_cached)
2425 *logical_rect = layout->logical_rect;
2426 logical_rect = NULL;
2428 if (!ink_rect && !logical_rect && !line_extents)
2431 pango_layout_check_lines (layout);
2433 /* When we are not wrapping, we need the overall width of the layout to
2434 * figure out the x_offsets of each line. However, we only need the
2435 * x_offsets if we are computing the ink_rect or individual line extents.
2437 width = layout->width;
2439 if (layout->auto_dir)
2441 /* If one of the lines of the layout is not left aligned, then we need
2442 * the width of the layout to calculate line x-offsets; this requires
2443 * looping through the lines for layout->auto_dir.
2445 line_list = layout->lines;
2446 while (line_list && !need_width)
2448 PangoLayoutLine *line = line_list->data;
2450 if (get_alignment (layout, line) != PANGO_ALIGN_LEFT)
2453 line_list = line_list->next;
2456 else if (layout->alignment != PANGO_ALIGN_LEFT)
2459 if (width == -1 && need_width && (ink_rect || line_extents))
2461 PangoRectangle overall_logical;
2463 pango_layout_get_extents_internal (layout, NULL, &overall_logical, NULL);
2464 width = overall_logical.width;
2469 logical_rect->x = 0;
2470 logical_rect->y = 0;
2471 logical_rect->width = 0;
2472 logical_rect->height = 0;
2475 line_list = layout->lines;
2478 PangoLayoutLine *line = line_list->data;
2479 /* Line extents in layout coords (origin at 0,0 of the layout) */
2480 PangoRectangle line_ink_layout;
2481 PangoRectangle line_logical_layout;
2485 /* This block gets the line extents in layout coords */
2489 get_line_extents_layout_coords (layout, line,
2492 ink_rect ? &line_ink_layout : NULL,
2493 &line_logical_layout);
2497 Extents *ext = g_slice_new (Extents);
2498 ext->baseline = baseline;
2499 ext->ink_rect = line_ink_layout;
2500 ext->logical_rect = line_logical_layout;
2501 *line_extents = g_slist_prepend (*line_extents, ext);
2507 /* Compute the union of the current ink_rect with
2511 if (line_list == layout->lines)
2513 *ink_rect = line_ink_layout;
2517 new_pos = MIN (ink_rect->x, line_ink_layout.x);
2519 MAX (ink_rect->x + ink_rect->width,
2520 line_ink_layout.x + line_ink_layout.width) - new_pos;
2521 ink_rect->x = new_pos;
2523 new_pos = MIN (ink_rect->y, line_ink_layout.y);
2525 MAX (ink_rect->y + ink_rect->height,
2526 line_ink_layout.y + line_ink_layout.height) - new_pos;
2527 ink_rect->y = new_pos;
2533 if (layout->width == -1)
2535 /* When no width is set on layout, we can just compute the max of the
2536 * line lengths to get the horizontal extents ... logical_rect.x = 0.
2538 logical_rect->width = MAX (logical_rect->width, line_logical_layout.width);
2542 /* When a width is set, we have to compute the union of the horizontal
2543 * extents of all the lines.
2545 if (line_list == layout->lines)
2547 logical_rect->x = line_logical_layout.x;
2548 logical_rect->width = line_logical_layout.width;
2552 new_pos = MIN (logical_rect->x, line_logical_layout.x);
2553 logical_rect->width =
2554 MAX (logical_rect->x + logical_rect->width,
2555 line_logical_layout.x + line_logical_layout.width) - new_pos;
2556 logical_rect->x = new_pos;
2561 logical_rect->height += line_logical_layout.height;
2563 /* No space after the last line, of course. */
2564 if (line_list->next != NULL)
2565 logical_rect->height += layout->spacing;
2568 y_offset += line_logical_layout.height + layout->spacing;
2569 line_list = line_list->next;
2574 layout->ink_rect = *ink_rect;
2575 layout->ink_rect_cached = TRUE;
2579 layout->logical_rect = *logical_rect;
2580 layout->logical_rect_cached = TRUE;
2583 *line_extents = g_slist_reverse (*line_extents);
2587 * pango_layout_get_extents:
2588 * @layout: a #PangoLayout
2589 * @ink_rect: rectangle used to store the extents of the layout as drawn
2590 * or %NULL to indicate that the result is not needed.
2591 * @logical_rect: rectangle used to store the logical extents of the layout
2592 or %NULL to indicate that the result is not needed.
2594 * Computes the logical and ink extents of @layout. Logical extents
2595 * are usually what you want for positioning things. Note that both extents
2596 * may have non-zero x and y. You may want to use those to offset where you
2597 * render the layout. Not doing that is a very typical bug that shows up as
2598 * right-to-left layouts not being correctly positioned in a layout with
2601 * The extents are given in layout coordinates and in Pango units; layout
2602 * coordinates begin at the top left corner of the layout.
2605 pango_layout_get_extents (PangoLayout *layout,
2606 PangoRectangle *ink_rect,
2607 PangoRectangle *logical_rect)
2609 g_return_if_fail (layout != NULL);
2611 pango_layout_get_extents_internal (layout, ink_rect, logical_rect, NULL);
2615 * pango_layout_get_pixel_extents:
2616 * @layout: a #PangoLayout
2617 * @ink_rect: rectangle used to store the extents of the layout as drawn
2618 * or %NULL to indicate that the result is not needed.
2619 * @logical_rect: rectangle used to store the logical extents of the
2620 * layout or %NULL to indicate that the result is not needed.
2622 * Computes the logical and ink extents of @layout in device units.
2623 * This function just calls pango_layout_get_extents() followed by
2624 * two pango_extents_to_pixels() calls, rounding @ink_rect and @logical_rect
2625 * such that the rounded rectangles fully contain the unrounded one (that is,
2626 * passes them as first argument to pango_extents_to_pixels()).
2629 pango_layout_get_pixel_extents (PangoLayout *layout,
2630 PangoRectangle *ink_rect,
2631 PangoRectangle *logical_rect)
2633 g_return_if_fail (PANGO_IS_LAYOUT (layout));
2635 pango_layout_get_extents (layout, ink_rect, logical_rect);
2636 pango_extents_to_pixels (ink_rect, NULL);
2637 pango_extents_to_pixels (logical_rect, NULL);
2641 * pango_layout_get_size:
2642 * @layout: a #PangoLayout
2643 * @width: location to store the logical width, or %NULL
2644 * @height: location to store the logical height, or %NULL
2646 * Determines the logical width and height of a #PangoLayout
2647 * in Pango units (device units scaled by %PANGO_SCALE). This
2648 * is simply a convenience function around pango_layout_get_extents().
2651 pango_layout_get_size (PangoLayout *layout,
2655 PangoRectangle logical_rect;
2657 pango_layout_get_extents (layout, NULL, &logical_rect);
2660 *width = logical_rect.width;
2662 *height = logical_rect.height;
2666 * pango_layout_get_pixel_size:
2667 * @layout: a #PangoLayout
2668 * @width: location to store the logical width, or %NULL
2669 * @height: location to store the logical height, or %NULL
2671 * Determines the logical width and height of a #PangoLayout
2672 * in device units. (pango_layout_get_size() returns the width
2673 * and height scaled by %PANGO_SCALE.) This
2674 * is simply a convenience function around
2675 * pango_layout_get_pixel_extents().
2678 pango_layout_get_pixel_size (PangoLayout *layout,
2682 PangoRectangle logical_rect;
2684 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2687 *width = logical_rect.width;
2689 *height = logical_rect.height;
2693 * pango_layout_get_baseline:
2694 * @layout: a #PangoLayout
2696 * Gets the Y position of baseline of the first line in @layout.
2698 * Return value: baseline of first line, from top of @layout.
2703 pango_layout_get_baseline (PangoLayout *layout)
2707 /* XXX this is so inefficient */
2708 PangoLayoutIter *iter = pango_layout_get_iter (layout);
2709 baseline = pango_layout_iter_get_baseline (iter);
2710 pango_layout_iter_free (iter);
2716 pango_layout_clear_lines (PangoLayout *layout)
2720 GSList *tmp_list = layout->lines;
2723 PangoLayoutLine *line = tmp_list->data;
2724 tmp_list = tmp_list->next;
2726 line->layout = NULL;
2727 pango_layout_line_unref (line);
2730 g_slist_free (layout->lines);
2731 layout->lines = NULL;
2732 layout->line_count = 0;
2734 /* This could be handled separately, since we don't need to
2735 * recompute log_attrs on a width change, but this is easiest
2737 g_free (layout->log_attrs);
2738 layout->log_attrs = NULL;
2741 layout->unknown_glyphs_count = -1;
2742 layout->logical_rect_cached = FALSE;
2743 layout->ink_rect_cached = FALSE;
2744 layout->is_ellipsized = FALSE;
2745 layout->is_wrapped = FALSE;
2749 pango_layout_line_leaked (PangoLayoutLine *line)
2751 PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
2753 private->cache_status = LEAKED;
2757 line->layout->logical_rect_cached = FALSE;
2758 line->layout->ink_rect_cached = FALSE;
2767 static void shape_tab (PangoLayoutLine *line,
2768 PangoGlyphString *glyphs);
2771 free_run (PangoLayoutRun *run, gpointer data)
2773 gboolean free_item = data != NULL;
2775 pango_item_free (run->item);
2777 pango_glyph_string_free (run->glyphs);
2778 g_slice_free (PangoLayoutRun, run);
2782 extents_free (Extents *ext, gpointer data G_GNUC_UNUSED)
2784 g_slice_free (Extents, ext);
2788 uninsert_run (PangoLayoutLine *line)
2790 PangoLayoutRun *run;
2793 GSList *tmp_node = line->runs;
2795 run = tmp_node->data;
2798 line->runs = tmp_node->next;
2799 line->length -= item->length;
2801 g_slist_free_1 (tmp_node);
2802 free_run (run, (gpointer)FALSE);
2808 ensure_tab_width (PangoLayout *layout)
2810 if (layout->tab_width == -1)
2812 /* Find out how wide 8 spaces are in the context's default
2813 * font. Utter performance killer. :-(
2815 PangoGlyphString *glyphs = pango_glyph_string_new ();
2818 PangoAttribute *attr;
2819 PangoAttrList *layout_attrs;
2820 PangoAttrList *tmp_attrs;
2821 PangoAttrIterator *iter;
2822 PangoFontDescription *font_desc = pango_font_description_copy_static (pango_context_get_font_description (layout->context));
2823 PangoLanguage *language;
2825 layout_attrs = pango_layout_get_effective_attributes (layout);
2826 iter = pango_attr_list_get_iterator (layout_attrs);
2827 pango_attr_iterator_get_font (iter, font_desc, &language, NULL);
2829 tmp_attrs = pango_attr_list_new ();
2831 attr = pango_attr_font_desc_new (font_desc);
2832 pango_font_description_free (font_desc);
2833 pango_attr_list_insert_before (tmp_attrs, attr);
2837 attr = pango_attr_language_new (language);
2838 pango_attr_list_insert_before (tmp_attrs, attr);
2841 items = pango_itemize (layout->context, " ", 0, 1, tmp_attrs, NULL);
2843 pango_attr_iterator_destroy (iter);
2844 if (layout_attrs != layout->attrs)
2846 pango_attr_list_unref (layout_attrs);
2847 layout_attrs = NULL;
2849 pango_attr_list_unref (tmp_attrs);
2852 pango_shape (" ", 8, &item->analysis, glyphs);
2854 pango_item_free (item);
2855 g_list_free (items);
2857 layout->tab_width = pango_glyph_string_get_width (glyphs);
2859 pango_glyph_string_free (glyphs);
2861 /* We need to make sure the tab_width is > 0 so finding tab positions
2862 * terminates. This check should be necessary only under extreme
2863 * problems with the font.
2865 if (layout->tab_width <= 0)
2866 layout->tab_width = 50 * PANGO_SCALE; /* pretty much arbitrary */
2870 /* For now we only need the tab position, we assume
2871 * all tabs are left-aligned.
2874 get_tab_pos (PangoLayout *layout, int index, gboolean *is_default)
2881 n_tabs = pango_tab_array_get_size (layout->tabs);
2882 in_pixels = pango_tab_array_get_positions_in_pixels (layout->tabs);
2884 *is_default = FALSE;
2898 pango_tab_array_get_tab (layout->tabs, index, NULL, &pos);
2901 return pos * PANGO_SCALE;
2908 /* Extrapolate tab position, repeating the last tab gap to
2912 int next_to_last_pos = 0;
2915 pango_tab_array_get_tab (layout->tabs, n_tabs - 1, NULL, &last_pos);
2918 pango_tab_array_get_tab (layout->tabs, n_tabs - 2, NULL, &next_to_last_pos);
2920 next_to_last_pos = 0;
2924 next_to_last_pos *= PANGO_SCALE;
2925 last_pos *= PANGO_SCALE;
2928 if (last_pos > next_to_last_pos)
2930 tab_width = last_pos - next_to_last_pos;
2934 tab_width = layout->tab_width;
2937 return last_pos + tab_width * (index - n_tabs + 1);
2941 /* No tab array set, so use default tab width
2943 return layout->tab_width * index;
2948 line_width (PangoLayoutLine *line)
2954 /* Compute the width of the line currently - inefficient, but easier
2955 * than keeping the current width of the line up to date everywhere
2957 for (l = line->runs; l; l = l->next)
2959 PangoLayoutRun *run = l->data;
2961 for (i=0; i < run->glyphs->num_glyphs; i++)
2962 width += run->glyphs->glyphs[i].geometry.width;
2969 shape_tab (PangoLayoutLine *line,
2970 PangoGlyphString *glyphs)
2974 int current_width = line_width (line);
2976 pango_glyph_string_set_size (glyphs, 1);
2978 glyphs->glyphs[0].glyph = PANGO_GLYPH_EMPTY;
2979 glyphs->glyphs[0].geometry.x_offset = 0;
2980 glyphs->glyphs[0].geometry.y_offset = 0;
2981 glyphs->glyphs[0].attr.is_cluster_start = 1;
2983 glyphs->log_clusters[0] = 0;
2985 ensure_tab_width (line->layout);
2986 space_width = line->layout->tab_width / 8;
2990 gboolean is_default;
2991 int tab_pos = get_tab_pos (line->layout, i, &is_default);
2992 /* Make sure there is at least a space-width of space between
2993 * tab-aligned text and the text before it. However, only do
2994 * this if no tab array is set on the layout, ie. using default
2995 * tab positions. If use has set tab positions, respect it to
2998 if (tab_pos >= current_width + (is_default ? space_width : 1))
3000 glyphs->glyphs[0].geometry.width = tab_pos - current_width;
3006 static inline gboolean
3007 can_break_at (PangoLayout *layout,
3009 gboolean always_wrap_char)
3012 /* We probably should have a mode where we treat all white-space as
3013 * of fungible width - appropriate for typography but not for
3016 wrap = layout->wrap;
3018 if (wrap == PANGO_WRAP_WORD_CHAR)
3019 wrap = always_wrap_char ? PANGO_WRAP_CHAR : PANGO_WRAP_WORD;
3021 if (offset == layout->n_chars)
3023 else if (wrap == PANGO_WRAP_WORD)
3024 return layout->log_attrs[offset].is_line_break;
3025 else if (wrap == PANGO_WRAP_CHAR)
3026 return layout->log_attrs[offset].is_char_break;
3029 g_warning (G_STRLOC": broken PangoLayout");
3034 static inline gboolean
3035 can_break_in (PangoLayout *layout,
3038 gboolean allow_break_at_start)
3042 for (i = allow_break_at_start ? 0 : 1; i < num_chars; i++)
3043 if (can_break_at (layout, start_offset + i, FALSE))
3050 distribute_letter_spacing (int letter_spacing,
3054 *space_left = letter_spacing / 2;
3056 if ((letter_spacing & (PANGO_SCALE - 1)) == 0)
3058 *space_left = PANGO_UNITS_ROUND (*space_left);
3060 *space_right = letter_spacing - *space_left;
3069 BREAK_LINE_SEPARATOR
3072 struct _ParaBreakState
3074 /* maintained per layout */
3075 int line_height; /* Estimate of height of current line; < 0 is no estimate */
3076 int remaining_height; /* Remaining height of the layout; only defined if layout->height >= 0 */
3078 /* maintained per paragraph */
3079 PangoAttrList *attrs; /* Attributes being used for itemization */
3080 GList *items; /* This paragraph turned into items */
3081 PangoDirection base_dir; /* Current resolved base direction */
3082 gboolean line_of_par; /* Line of the paragraph, starting at 1 for first line */
3084 PangoGlyphString *glyphs; /* Glyphs for the first item in state->items */
3085 int start_offset; /* Character offset of first item in state->items in layout->text */
3086 ItemProperties properties; /* Properties for the first item in state->items */
3087 int *log_widths; /* Logical widths for first item in state->items.. */
3088 int log_widths_offset; /* Offset into log_widths to the point corresponding
3089 * to the remaining portion of the first item */
3091 int line_start_index; /* Start index (byte offset) of line in layout->text */
3092 int line_start_offset; /* Character offset of line in layout->text */
3094 /* maintained per line */
3095 int line_width; /* Goal width of line currently processing; < 0 is infinite */
3096 int remaining_width; /* Amount of space remaining on line; < 0 is infinite */
3100 should_ellipsize_current_line (PangoLayout *layout,
3101 ParaBreakState *state);
3103 static PangoGlyphString *
3104 shape_run (PangoLayoutLine *line,
3105 ParaBreakState *state,
3108 PangoLayout *layout = line->layout;
3109 PangoGlyphString *glyphs = pango_glyph_string_new ();
3111 if (layout->text[item->offset] == '\t')
3112 shape_tab (line, glyphs);
3115 if (state->properties.shape_set)
3116 _pango_shape_shape (layout->text + item->offset, item->num_chars,
3117 state->properties.shape_ink_rect, state->properties.shape_logical_rect,
3120 pango_shape (layout->text + item->offset, item->length, &item->analysis, glyphs);
3122 if (state->properties.letter_spacing)
3124 PangoGlyphItem glyph_item;
3125 int space_left, space_right;
3127 glyph_item.item = item;
3128 glyph_item.glyphs = glyphs;
3130 pango_glyph_item_letter_space (&glyph_item,
3132 layout->log_attrs + state->start_offset,
3133 state->properties.letter_spacing);
3135 distribute_letter_spacing (state->properties.letter_spacing, &space_left, &space_right);
3137 glyphs->glyphs[0].geometry.width += space_left;
3138 glyphs->glyphs[0].geometry.x_offset += space_left;
3139 glyphs->glyphs[glyphs->num_glyphs - 1].geometry.width += space_right;
3147 insert_run (PangoLayoutLine *line,
3148 ParaBreakState *state,
3149 PangoItem *run_item,
3152 PangoLayoutRun *run = g_slice_new (PangoLayoutRun);
3154 run->item = run_item;
3156 if (last_run && state->log_widths_offset == 0)
3157 run->glyphs = state->glyphs;
3159 run->glyphs = shape_run (line, state, run_item);
3163 if (state->log_widths_offset > 0)
3164 pango_glyph_string_free (state->glyphs);
3165 state->glyphs = NULL;
3166 g_free (state->log_widths);
3167 state->log_widths = NULL;
3170 line->runs = g_slist_prepend (line->runs, run);
3171 line->length += run_item->length;
3175 # define DEBUG debug
3177 debug (const char *where, PangoLayoutLine *line, ParaBreakState *state)
3179 int line_width = pango_layout_line_get_width (line);
3181 g_debug ("rem %d + line %d = %d %s",
3182 state->remaining_width,
3184 state->remaining_width + line_width,
3188 # define DEBUG(where, line, state) do { } while (0)
3191 /* Tries to insert as much as possible of the item at the head of
3192 * state->items onto @line. Five results are possible:
3194 * %BREAK_NONE_FIT: Couldn't fit anything.
3195 * %BREAK_SOME_FIT: The item was broken in the middle.
3196 * %BREAK_ALL_FIT: Everything fit.
3197 * %BREAK_EMPTY_FIT: Nothing fit, but that was ok, as we can break at the first char.
3198 * %BREAK_LINE_SEPARATOR: Item begins with a line separator.
3200 * If @force_fit is %TRUE, then %BREAK_NONE_FIT will never
3201 * be returned, a run will be added even if inserting the minimum amount
3202 * will cause the line to overflow. This is used at the start of a line
3203 * and until we've found at least some place to break.
3205 * If @no_break_at_end is %TRUE, then %BREAK_ALL_FIT will never be
3206 * returned even everything fits; the run will be broken earlier,
3207 * or %BREAK_NONE_FIT returned. This is used when the end of the
3208 * run is not a break position.
3211 process_item (PangoLayout *layout,
3212 PangoLayoutLine *line,
3213 ParaBreakState *state,
3215 gboolean no_break_at_end)
3217 PangoItem *item = state->items->data;
3218 gboolean shape_set = FALSE;
3222 gboolean processing_new_item = FALSE;
3224 /* Only one character has type G_UNICODE_LINE_SEPARATOR in Unicode 5.0;
3225 * update this if that changes. */
3226 #define LINE_SEPARATOR 0x2028
3230 pango_layout_get_item_properties (item, &state->properties);
3231 state->glyphs = shape_run (line, state, item);
3233 state->log_widths = NULL;
3234 state->log_widths_offset = 0;
3236 processing_new_item = TRUE;
3239 if (!layout->single_paragraph &&
3240 g_utf8_get_char (layout->text + item->offset) == LINE_SEPARATOR &&
3241 !should_ellipsize_current_line (layout, state))
3243 insert_run (line, state, item, TRUE);
3244 state->log_widths_offset += item->num_chars;
3245 return BREAK_LINE_SEPARATOR;
3248 if (state->remaining_width < 0 && !no_break_at_end) /* Wrapping off */
3250 insert_run (line, state, item, TRUE);
3252 return BREAK_ALL_FIT;
3256 if (processing_new_item)
3258 width = pango_glyph_string_get_width (state->glyphs);
3262 for (i = 0; i < item->num_chars; i++)
3263 width += state->log_widths[state->log_widths_offset + i];
3266 if ((width <= state->remaining_width || (item->num_chars == 1 && !line->runs)) &&
3269 state->remaining_width -= width;
3270 state->remaining_width = MAX (state->remaining_width, 0);
3271 insert_run (line, state, item, TRUE);
3273 return BREAK_ALL_FIT;
3277 int num_chars = item->num_chars;
3278 int break_num_chars = num_chars;
3279 int break_width = width;
3280 int orig_width = width;
3281 gboolean retrying_with_char_breaks = FALSE;
3283 if (processing_new_item)
3285 PangoGlyphItem glyph_item = {item, state->glyphs};
3286 state->log_widths = g_new (int, item->num_chars);
3287 pango_glyph_item_get_logical_widths (&glyph_item, layout->text, state->log_widths);
3292 /* See how much of the item we can stuff in the line
3295 for (num_chars = 0; num_chars < item->num_chars; num_chars++)
3297 if (width > state->remaining_width && break_num_chars < item->num_chars)
3300 /* If there are no previous runs we have to take care to grab at least one char. */
3301 if (can_break_at (layout, state->start_offset + num_chars, retrying_with_char_breaks) &&
3302 (num_chars > 0 || line->runs))
3304 break_num_chars = num_chars;
3305 break_width = width;
3308 width += state->log_widths[state->log_widths_offset + num_chars];
3311 if (layout->wrap == PANGO_WRAP_WORD_CHAR && force_fit && break_width > state->remaining_width && !retrying_with_char_breaks)
3313 retrying_with_char_breaks = TRUE;
3314 num_chars = item->num_chars;
3316 break_num_chars = num_chars;
3317 break_width = width;
3321 if (force_fit || break_width <= state->remaining_width) /* Successfully broke the item */
3323 if (state->remaining_width >= 0)
3325 state->remaining_width -= break_width;
3326 state->remaining_width = MAX (state->remaining_width, 0);
3329 if (break_num_chars == item->num_chars)
3331 insert_run (line, state, item, TRUE);
3333 return BREAK_ALL_FIT;
3335 else if (break_num_chars == 0)
3337 return BREAK_EMPTY_FIT;
3341 PangoItem *new_item;
3343 length = g_utf8_offset_to_pointer (layout->text + item->offset, break_num_chars) - (layout->text + item->offset);
3345 new_item = pango_item_split (item, length, break_num_chars);
3347 /* reshaping may slightly change the item width. update
3348 * remaining_width if we are justifying */
3350 state->remaining_width += break_width;
3352 insert_run (line, state, new_item, FALSE);
3354 if (layout->justify)
3355 break_width = pango_glyph_string_get_width (((PangoGlyphItem *)(line->runs->data))->glyphs);
3357 state->remaining_width -= break_width;
3359 state->log_widths_offset += break_num_chars;
3361 /* Shaped items should never be broken */
3362 g_assert (!shape_set);
3364 return BREAK_SOME_FIT;
3369 pango_glyph_string_free (state->glyphs);
3370 state->glyphs = NULL;
3371 g_free (state->log_widths);
3372 state->log_widths = NULL;
3374 return BREAK_NONE_FIT;
3379 /* The resolved direction for the line is always one
3380 * of LTR/RTL; not a week or neutral directions
3383 line_set_resolved_dir (PangoLayoutLine *line,
3384 PangoDirection direction)
3389 case PANGO_DIRECTION_LTR:
3390 case PANGO_DIRECTION_TTB_RTL:
3391 case PANGO_DIRECTION_WEAK_LTR:
3392 case PANGO_DIRECTION_NEUTRAL:
3393 line->resolved_dir = PANGO_DIRECTION_LTR;
3395 case PANGO_DIRECTION_RTL:
3396 case PANGO_DIRECTION_WEAK_RTL:
3397 case PANGO_DIRECTION_TTB_LTR:
3398 line->resolved_dir = PANGO_DIRECTION_RTL;
3402 /* The direction vs. gravity dance:
3403 * - If gravity is SOUTH, leave direction untouched.
3404 * - If gravity is NORTH, switch direction.
3405 * - If gravity is EAST, set to LTR, as
3406 * it's a clockwise-rotated layout, so the rotated
3407 * top is unrotated left.
3408 * - If gravity is WEST, set to RTL, as
3409 * it's a counter-clockwise-rotated layout, so the rotated
3410 * top is unrotated right.
3412 * A similar dance is performed in pango-context.c:
3413 * itemize_state_add_character(). Keep in synch.
3415 switch (pango_context_get_gravity (line->layout->context))
3418 case PANGO_GRAVITY_AUTO:
3419 case PANGO_GRAVITY_SOUTH:
3421 case PANGO_GRAVITY_NORTH:
3422 line->resolved_dir = PANGO_DIRECTION_LTR
3423 + PANGO_DIRECTION_RTL
3424 - line->resolved_dir;
3426 case PANGO_GRAVITY_EAST:
3427 /* This is in fact why deprecated TTB_RTL is LTR */
3428 line->resolved_dir = PANGO_DIRECTION_LTR;
3430 case PANGO_GRAVITY_WEST:
3431 /* This is in fact why deprecated TTB_LTR is RTL */
3432 line->resolved_dir = PANGO_DIRECTION_RTL;
3438 should_ellipsize_current_line (PangoLayout *layout,
3439 ParaBreakState *state)
3441 if (G_LIKELY (layout->ellipsize == PANGO_ELLIPSIZE_NONE || layout->width < 0))
3445 if (layout->height >= 0)
3447 /* state->remaining_height is height of layout left */
3449 /* if we can't stuff two more lines at the current guess of line height,
3450 * the line we are going to produce is going to be the last line */
3451 return state->line_height * 2 > state->remaining_height;
3455 /* -layout->height is number of lines per paragraph to show */
3456 return state->line_of_par == - layout->height;
3461 add_line (PangoLayoutLine *line,
3462 ParaBreakState *state)
3464 PangoLayout *layout = line->layout;
3466 /* we prepend, then reverse the list later */
3467 layout->lines = g_slist_prepend (layout->lines, line);
3468 layout->line_count++;
3470 if (layout->height >= 0)
3472 PangoRectangle logical_rect;
3473 pango_layout_line_get_extents (line, NULL, &logical_rect);
3474 state->remaining_height -= logical_rect.height;
3475 state->line_height = logical_rect.height;
3480 process_line (PangoLayout *layout,
3481 ParaBreakState *state)
3483 PangoLayoutLine *line;
3485 gboolean have_break = FALSE; /* If we've seen a possible break yet */
3486 int break_remaining_width = 0; /* Remaining width before adding run with break */
3487 int break_start_offset = 0; /* Start offset before adding run with break */
3488 GSList *break_link = NULL; /* Link holding run before break */
3489 gboolean wrapped = FALSE; /* If we had to wrap the line */
3491 line = pango_layout_line_new (layout);
3492 line->start_index = state->line_start_index;
3493 line->is_paragraph_start = state->line_of_par == 1;
3494 line_set_resolved_dir (line, state->base_dir);
3496 state->line_width = layout->width;
3497 if (state->line_width >= 0 && layout->alignment != PANGO_ALIGN_CENTER)
3499 if (line->is_paragraph_start && layout->indent >= 0)
3500 state->line_width -= layout->indent;
3501 else if (!line->is_paragraph_start && layout->indent < 0)
3502 state->line_width += layout->indent;
3504 if (state->line_width < 0)
3505 state->line_width = 0;
3508 if (G_UNLIKELY (should_ellipsize_current_line (layout, state)))
3509 state->remaining_width = -1;
3511 state->remaining_width = state->line_width;
3512 DEBUG ("starting to fill line", line, state);
3514 while (state->items)
3516 PangoItem *item = state->items->data;
3519 int old_remaining_width;
3520 gboolean first_item_in_line;
3522 old_num_chars = item->num_chars;
3523 old_remaining_width = state->remaining_width;
3524 first_item_in_line = line->runs != NULL;
3526 result = process_item (layout, line, state, !have_break, FALSE);
3531 if (can_break_in (layout, state->start_offset, old_num_chars, first_item_in_line))
3534 break_remaining_width = old_remaining_width;
3535 break_start_offset = state->start_offset;
3536 break_link = line->runs->next;
3539 state->items = g_list_delete_link (state->items, state->items);
3540 state->start_offset += old_num_chars;
3544 case BREAK_EMPTY_FIT:
3548 case BREAK_SOME_FIT:
3549 state->start_offset += old_num_chars - item->num_chars;
3553 case BREAK_NONE_FIT:
3554 /* Back up over unused runs to run where there is a break */
3555 while (line->runs && line->runs != break_link)
3556 state->items = g_list_prepend (state->items, uninsert_run (line));
3558 state->start_offset = break_start_offset;
3559 state->remaining_width = break_remaining_width;
3561 /* Reshape run to break */
3562 item = state->items->data;
3564 old_num_chars = item->num_chars;
3565 result = process_item (layout, line, state, TRUE, TRUE);
3566 g_assert (result == BREAK_SOME_FIT || result == BREAK_EMPTY_FIT);
3568 state->start_offset += old_num_chars - item->num_chars;
3573 case BREAK_LINE_SEPARATOR:
3574 state->items = g_list_delete_link (state->items, state->items);
3575 state->start_offset += old_num_chars;
3576 /* A line-separate is just a forced break. Set wrapped, so we do
3584 pango_layout_line_postprocess (line, state, wrapped);
3585 add_line (line, state);
3586 state->line_of_par++;
3587 state->line_start_index += line->length;
3588 state->line_start_offset = state->start_offset;
3592 get_items_log_attrs (const char *text,
3594 PangoLogAttr *log_attrs,
3595 int para_delimiter_len)
3602 PangoItem tmp_item = *(PangoItem *)items->data;
3604 /* Accumulate all the consecutive items that match in language
3605 * characteristics, ignoring font, style tags, etc.
3609 PangoItem *next_item = items->next->data;
3611 /* FIXME: Handle language tags */
3612 if (next_item->analysis.lang_engine != tmp_item.analysis.lang_engine)
3616 tmp_item.length += next_item->length;
3617 tmp_item.num_chars += next_item->num_chars;
3620 items = items->next;
3623 /* Break the paragraph delimiters with the last item */
3624 if (items->next == NULL)
3626 tmp_item.num_chars += pango_utf8_strlen (text + index + tmp_item.length, para_delimiter_len);
3627 tmp_item.length += para_delimiter_len;
3630 /* XXX This is wrong. we should call pango_default_break on the entire
3631 * layout text and then tailor_break on each lang_engine change, like
3632 * pango_get_log_attrs does.
3634 pango_break (text + index, tmp_item.length, &tmp_item.analysis,
3635 log_attrs + offset, tmp_item.num_chars + 1);
3637 offset += tmp_item.num_chars;
3638 index += tmp_item.length;
3640 items = items->next;
3644 static PangoAttrList *
3645 pango_layout_get_effective_attributes (PangoLayout *layout)
3647 PangoAttrList *attrs;
3650 attrs = pango_attr_list_copy (layout->attrs);
3652 attrs = pango_attr_list_new ();
3654 if (layout->font_desc)
3656 PangoAttribute *attr = pango_attr_font_desc_new (layout->font_desc);
3657 pango_attr_list_insert_before (attrs, attr);
3664 no_shape_filter_func (PangoAttribute *attribute,
3665 gpointer data G_GNUC_UNUSED)
3667 static const PangoAttrType no_shape_types[] = {
3668 PANGO_ATTR_FOREGROUND,
3669 PANGO_ATTR_BACKGROUND,
3670 PANGO_ATTR_UNDERLINE,
3671 PANGO_ATTR_STRIKETHROUGH,
3677 for (i = 0; i < (int)G_N_ELEMENTS (no_shape_types); i++)
3678 if (attribute->klass->type == no_shape_types[i])
3684 static PangoAttrList *
3685 filter_no_shape_attributes (PangoAttrList *attrs)
3687 return pango_attr_list_filter (attrs,
3688 no_shape_filter_func,
3693 apply_no_shape_attributes (PangoLayout *layout,
3694 PangoAttrList *no_shape_attrs)
3698 for (line_list = layout->lines; line_list; line_list = line_list->next)
3700 PangoLayoutLine *line = line_list->data;
3701 GSList *old_runs = g_slist_reverse (line->runs);
3705 for (run_list = old_runs; run_list; run_list = run_list->next)
3707 PangoGlyphItem *glyph_item = run_list->data;
3710 new_runs = pango_glyph_item_apply_attrs (glyph_item,
3714 line->runs = g_slist_concat (new_runs, line->runs);
3717 g_slist_free (old_runs);
3722 pango_layout_check_lines (PangoLayout *layout)
3725 gboolean done = FALSE;
3727 PangoAttrList *attrs;
3728 PangoAttrList *no_shape_attrs;
3729 PangoAttrIterator *iter;
3730 PangoDirection prev_base_dir = PANGO_DIRECTION_NEUTRAL, base_dir = PANGO_DIRECTION_NEUTRAL;
3731 ParaBreakState state;
3733 if (G_LIKELY (layout->lines))
3736 g_assert (!layout->log_attrs);
3738 /* For simplicity, we make sure at this point that layout->text
3739 * is non-NULL even if it is zero length
3741 if (G_UNLIKELY (!layout->text))
3742 pango_layout_set_text (layout, NULL, 0);
3744 attrs = pango_layout_get_effective_attributes (layout);
3745 no_shape_attrs = filter_no_shape_attributes (attrs);
3746 iter = pango_attr_list_get_iterator (attrs);
3748 layout->log_attrs = g_new (PangoLogAttr, layout->n_chars + 1);
3751 start = layout->text;
3753 /* Find the first strong direction of the text */
3754 if (layout->auto_dir)
3756 prev_base_dir = pango_find_base_dir (layout->text, layout->length);
3757 if (prev_base_dir == PANGO_DIRECTION_NEUTRAL)
3758 prev_base_dir = pango_context_get_base_dir (layout->context);
3761 base_dir = pango_context_get_base_dir (layout->context);
3763 /* these are only used if layout->height >= 0 */
3764 state.remaining_height = layout->height;
3765 state.line_height = -1;
3766 if (layout->height >= 0)
3768 PangoRectangle logical;
3769 pango_layout_get_empty_extents_at_index (layout, 0, &logical);
3770 state.line_height = logical.height;
3777 int delimiter_index, next_para_index;
3779 if (layout->single_paragraph)
3781 delimiter_index = layout->length;
3782 next_para_index = layout->length;
3786 pango_find_paragraph_boundary (start,
3787 (layout->text + layout->length) - start,
3792 g_assert (next_para_index >= delimiter_index);
3794 if (layout->auto_dir)
3796 base_dir = pango_find_base_dir (start, delimiter_index);
3798 /* Propagate the base direction for neutral paragraphs */
3799 if (base_dir == PANGO_DIRECTION_NEUTRAL)
3800 base_dir = prev_base_dir;
3802 prev_base_dir = base_dir;
3805 end = start + delimiter_index;
3807 delim_len = next_para_index - delimiter_index;
3809 if (end == (layout->text + layout->length))
3812 g_assert (end <= (layout->text + layout->length));
3813 g_assert (start <= (layout->text + layout->length));
3814 g_assert (delim_len < 4); /* PS is 3 bytes */
3815 g_assert (delim_len >= 0);
3817 state.attrs = attrs;
3818 state.items = pango_itemize_with_base_dir (layout->context,
3821 start - layout->text,
3826 get_items_log_attrs (start, state.items,
3827 layout->log_attrs + start_offset,
3830 state.base_dir = base_dir;
3831 state.line_of_par = 1;
3832 state.start_offset = start_offset;
3833 state.line_start_offset = start_offset;
3834 state.line_start_index = start - layout->text;
3836 state.glyphs = NULL;
3837 state.log_widths = NULL;
3839 /* for deterministic bug hunting's sake set everything! */
3840 state.line_width = -1;
3841 state.remaining_width = -1;
3842 state.log_widths_offset = 0;
3847 process_line (layout, &state);
3851 PangoLayoutLine *empty_line;
3853 empty_line = pango_layout_line_new (layout);
3854 empty_line->start_index = state.line_start_index;
3855 empty_line->is_paragraph_start = TRUE;
3856 line_set_resolved_dir (empty_line, base_dir);
3858 add_line (empty_line, &state);
3861 if (layout->height >= 0 && state.remaining_height < state.line_height)
3865 start_offset += pango_utf8_strlen (start, (end - start) + delim_len);
3867 start = end + delim_len;
3871 pango_attr_iterator_destroy (iter);
3872 pango_attr_list_unref (attrs);
3876 apply_no_shape_attributes (layout, no_shape_attrs);
3877 pango_attr_list_unref (no_shape_attrs);
3880 layout->lines = g_slist_reverse (layout->lines);
3884 * pango_layout_line_ref:
3885 * @line: a #PangoLayoutLine, may be %NULL
3887 * Increase the reference count of a #PangoLayoutLine by one.
3889 * Return value: the line passed in.
3894 pango_layout_line_ref (PangoLayoutLine *line)
3896 PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
3901 g_atomic_int_inc ((int *) &private->ref_count);
3907 * pango_layout_line_unref:
3908 * @line: a #PangoLayoutLine
3910 * Decrease the reference count of a #PangoLayoutLine by one.
3911 * If the result is zero, the line and all associated memory
3915 pango_layout_line_unref (PangoLayoutLine *line)
3917 PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
3922 g_return_if_fail (private->ref_count > 0);
3924 if (g_atomic_int_dec_and_test ((int *) &private->ref_count))
3926 g_slist_foreach (line->runs, (GFunc)free_run, GINT_TO_POINTER (1));
3927 g_slist_free (line->runs);
3928 g_slice_free (PangoLayoutLinePrivate, private);
3933 pango_layout_line_get_type (void)
3935 static GType our_type = 0;
3937 if (G_UNLIKELY (our_type == 0))
3938 our_type = g_boxed_type_register_static (I_("PangoLayoutLine"),
3939 (GBoxedCopyFunc) pango_layout_line_ref,
3940 (GBoxedFreeFunc) pango_layout_line_unref);
3945 * pango_layout_line_x_to_index:
3946 * @line: a #PangoLayoutLine
3947 * @x_pos: the X offset (in Pango units)
3948 * from the left edge of the line.
3949 * @index_: location to store calculated byte index for
3950 * the grapheme in which the user clicked.
3951 * @trailing: location to store an integer indicating where
3952 * in the grapheme the user clicked. It will either
3953 * be zero, or the number of characters in the
3954 * grapheme. 0 represents the leading edge of the grapheme.
3956 * Converts from x offset to the byte index of the corresponding
3957 * character within the text of the layout. If @x_pos is outside the line,
3958 * @index_ and @trailing will point to the very first or very last position
3959 * in the line. This determination is based on the resolved direction
3960 * of the paragraph; for example, if the resolved direction is
3961 * right-to-left, then an X position to the right of the line (after it)
3962 * results in 0 being stored in @index_ and @trailing. An X position to the
3963 * left of the line results in @index_ pointing to the (logical) last
3964 * grapheme in the line and @trailing being set to the number of characters
3965 * in that grapheme. The reverse is true for a left-to-right line.
3967 * Return value: %FALSE if @x_pos was outside the line, %TRUE if inside
3970 pango_layout_line_x_to_index (PangoLayoutLine *line,
3977 gint first_index = 0; /* line->start_index */
3979 gint last_index; /* start of last grapheme in line */
3981 gint end_index; /* end iterator for line */
3982 gint end_offset; /* end iterator for line */
3983 PangoLayout *layout;
3985 gboolean suppress_last_trailing;
3987 g_return_val_if_fail (line != NULL, FALSE);
3988 g_return_val_if_fail (LINE_IS_VALID (line), FALSE);
3990 if (!LINE_IS_VALID (line))
3993 layout = line->layout;
3995 /* Find the last index in the line
3997 first_index = line->start_index;
3999 if (line->length == 0)
4002 *index = first_index;
4009 g_assert (line->length > 0);
4011 first_offset = g_utf8_pointer_to_offset (layout->text, layout->text + line->start_index);
4013 end_index = first_index + line->length;
4014 end_offset = first_offset + g_utf8_pointer_to_offset (layout->text + first_index, layout->text + end_index);
4016 last_index = end_index;
4017 last_offset = end_offset;
4021 last_index = g_utf8_prev_char (layout->text + last_index) - layout->text;
4025 while (last_offset > first_offset && !layout->log_attrs[last_offset].is_cursor_position);
4027 /* This is a HACK. If a program only keeps track of cursor (etc)
4028 * indices and not the trailing flag, then the trailing index of the
4029 * last character on a wrapped line is identical to the leading
4030 * index of the next line. So, we fake it and set the trailing flag
4033 * That is, if the text is "now is the time", and is broken between
4036 * Then when the cursor is actually at:
4040 * we lie and say it is at:
4045 * So the cursor won't appear on the next line before 'the'.
4047 * Actually, any program keeping cursor
4048 * positions with wrapped lines should distinguish leading and
4051 tmp_list = layout->lines;
4052 while (tmp_list->data != line)
4053 tmp_list = tmp_list->next;
4055 if (tmp_list->next &&
4056 line->start_index + line->length == ((PangoLayoutLine *)tmp_list->next->data)->start_index)
4057 suppress_last_trailing = TRUE;
4059 suppress_last_trailing = FALSE;
4063 /* pick the leftmost char */
4065 *index = (line->resolved_dir == PANGO_DIRECTION_LTR) ? first_index : last_index;
4066 /* and its leftmost edge */
4068 *trailing = (line->resolved_dir == PANGO_DIRECTION_LTR || suppress_last_trailing) ? 0 : last_trailing;
4073 tmp_list = line->runs;
4076 PangoLayoutRun *run = tmp_list->data;
4077 ItemProperties properties;
4080 pango_layout_get_item_properties (run->item, &properties);
4082 logical_width = pango_glyph_string_get_width (run->glyphs);
4084 if (x_pos >= start_pos && x_pos < start_pos + logical_width)
4087 gboolean char_trailing;
4088 int grapheme_start_index;
4089 int grapheme_start_offset;
4090 int grapheme_end_offset;
4094 pango_glyph_string_x_to_index (run->glyphs,
4095 layout->text + run->item->offset, run->item->length,
4096 &run->item->analysis,
4098 &pos, &char_trailing);
4100 char_index = run->item->offset + pos;
4102 /* Convert from characters to graphemes */
4104 offset = g_utf8_pointer_to_offset (layout->text, layout->text + char_index);
4106 grapheme_start_offset = offset;
4107 grapheme_start_index = char_index;
4108 while (grapheme_start_offset > first_offset &&
4109 !layout->log_attrs[grapheme_start_offset].is_cursor_position)
4111 grapheme_start_index = g_utf8_prev_char (layout->text + grapheme_start_index) - layout->text;
4112 grapheme_start_offset--;
4115 grapheme_end_offset = offset;
4118 grapheme_end_offset++;
4120 while (grapheme_end_offset < end_offset &&
4121 !layout->log_attrs[grapheme_end_offset].is_cursor_position);
4124 *index = grapheme_start_index;
4128 if ((grapheme_end_offset == end_offset && suppress_last_trailing) ||
4129 offset + char_trailing <= (grapheme_start_offset + grapheme_end_offset) / 2)
4132 *trailing = grapheme_end_offset - grapheme_start_offset;
4138 start_pos += logical_width;
4139 tmp_list = tmp_list->next;
4142 /* pick the rightmost char */
4144 *index = (line->resolved_dir == PANGO_DIRECTION_LTR) ? last_index : first_index;
4146 /* and its rightmost edge */
4148 *trailing = (line->resolved_dir == PANGO_DIRECTION_LTR && !suppress_last_trailing) ? last_trailing : 0;
4154 pango_layout_line_get_width (PangoLayoutLine *line)
4157 GSList *tmp_list = line->runs;
4161 PangoLayoutRun *run = tmp_list->data;
4163 width += pango_glyph_string_get_width (run->glyphs);
4165 tmp_list = tmp_list->next;
4172 * pango_layout_line_get_x_ranges:
4173 * @line: a #PangoLayoutLine
4174 * @start_index: Start byte index of the logical range. If this value
4175 * is less than the start index for the line, then
4176 * the first range will extend all the way to the leading
4177 * edge of the layout. Otherwise it will start at the
4178 * leading edge of the first character.
4179 * @end_index: Ending byte index of the logical range. If this value
4180 * is greater than the end index for the line, then
4181 * the last range will extend all the way to the trailing
4182 * edge of the layout. Otherwise, it will end at the
4183 * trailing edge of the last character.
4184 * @ranges: (out): (array length=n_ranges): (transfer=full):
4185 * location to store a pointer to an array of ranges.
4186 * The array will be of length <literal>2*n_ranges</literal>,
4187 * with each range starting at <literal>(*ranges)[2*n]</literal>
4188 * and of width <literal>(*ranges)[2*n + 1] - (*ranges)[2*n]</literal>.
4189 * This array must be freed with g_free(). The coordinates are relative
4190 * to the layout and are in Pango units.
4191 * @n_ranges: The number of ranges stored in @ranges.
4193 * Gets a list of visual ranges corresponding to a given logical range.
4194 * This list is not necessarily minimal - there may be consecutive
4195 * ranges which are adjacent. The ranges will be sorted from left to
4196 * right. The ranges are with respect to the left edge of the entire
4197 * layout, not with respect to the line.
4200 pango_layout_line_get_x_ranges (PangoLayoutLine *line,
4206 gint line_start_index = 0;
4208 int range_count = 0;
4209 int accumulated_width = 0;
4211 int width, line_width;
4212 PangoAlignment alignment;
4214 g_return_if_fail (line != NULL);
4215 g_return_if_fail (line->layout != NULL);
4216 g_return_if_fail (start_index <= end_index);
4218 alignment = get_alignment (line->layout, line);
4220 width = line->layout->width;
4221 if (width == -1 && alignment != PANGO_ALIGN_LEFT)
4223 PangoRectangle logical_rect;
4224 pango_layout_get_extents (line->layout, NULL, &logical_rect);
4225 width = logical_rect.width;
4228 /* FIXME: The computations here could be optimized, by moving the
4229 * computations of the x_offset after we go through and figure
4230 * out where each range is.
4234 PangoRectangle logical_rect;
4235 pango_layout_line_get_extents (line, NULL, &logical_rect);
4236 line_width = logical_rect.width;
4239 get_x_offset (line->layout, line, width, line_width, &x_offset);
4241 line_start_index = line->start_index;
4243 /* Allocate the maximum possible size */
4245 *ranges = g_new (int, 2 * (2 + g_slist_length (line->runs)));
4248 ((line->resolved_dir == PANGO_DIRECTION_LTR && start_index < line_start_index) ||
4249 (line->resolved_dir == PANGO_DIRECTION_RTL && end_index > line_start_index + line->length)))
4253 (*ranges)[2*range_count] = 0;
4254 (*ranges)[2*range_count + 1] = x_offset;
4260 tmp_list = line->runs;
4263 PangoLayoutRun *run = (PangoLayoutRun *)tmp_list->data;
4265 if ((start_index < run->item->offset + run->item->length &&
4266 end_index > run->item->offset))
4270 int run_start_index = MAX (start_index, run->item->offset);
4271 int run_end_index = MIN (end_index, run->item->offset + run->item->length);
4272 int run_start_x, run_end_x;
4274 g_assert (run_end_index > 0);
4276 /* Back the end_index off one since we want to find the trailing edge of the preceding character */
4278 run_end_index = g_utf8_prev_char (line->layout->text + run_end_index) - line->layout->text;
4280 pango_glyph_string_index_to_x (run->glyphs,
4281 line->layout->text + run->item->offset,
4283 &run->item->analysis,
4284 run_start_index - run->item->offset, FALSE,
4286 pango_glyph_string_index_to_x (run->glyphs,
4287 line->layout->text + run->item->offset,
4289 &run->item->analysis,
4290 run_end_index - run->item->offset, TRUE,
4293 (*ranges)[2*range_count] = x_offset + accumulated_width + MIN (run_start_x, run_end_x);
4294 (*ranges)[2*range_count + 1] = x_offset + accumulated_width + MAX (run_start_x, run_end_x);
4301 accumulated_width += pango_glyph_string_get_width (run->glyphs);
4303 tmp_list = tmp_list->next;
4306 if (x_offset + line_width < line->layout->width &&
4307 ((line->resolved_dir == PANGO_DIRECTION_LTR && end_index > line_start_index + line->length) ||
4308 (line->resolved_dir == PANGO_DIRECTION_RTL && start_index < line_start_index)))
4312 (*ranges)[2*range_count] = x_offset + line_width;
4313 (*ranges)[2*range_count + 1] = line->layout->width;
4320 *n_ranges = range_count;
4324 pango_layout_get_empty_extents_at_index (PangoLayout *layout,
4326 PangoRectangle *logical_rect)
4331 PangoFontDescription *font_desc = NULL;
4332 gboolean free_font_desc = FALSE;
4334 font_desc = pango_context_get_font_description (layout->context);
4336 if (layout->font_desc)
4338 font_desc = pango_font_description_copy_static (font_desc);
4339 pango_font_description_merge (font_desc, layout->font_desc, TRUE);
4340 free_font_desc = TRUE;
4343 /* Find the font description for this line
4347 PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->attrs);
4352 pango_attr_iterator_range (iter, &start, &end);
4354 if (start <= index && index < end)
4356 PangoFontDescription *base_font_desc;
4358 if (layout->font_desc)
4359 base_font_desc = layout->font_desc;
4361 base_font_desc = pango_context_get_font_description (layout->context);
4363 if (!free_font_desc)
4365 font_desc = pango_font_description_copy_static (font_desc);
4366 free_font_desc = TRUE;
4369 pango_attr_iterator_get_font (iter,
4378 while (pango_attr_iterator_next (iter));
4380 pango_attr_iterator_destroy (iter);
4383 font = pango_context_load_font (layout->context, font_desc);
4386 PangoFontMetrics *metrics;
4388 metrics = pango_font_get_metrics (font,
4389 pango_context_get_language (layout->context));
4393 logical_rect->y = - pango_font_metrics_get_ascent (metrics);
4394 logical_rect->height = - logical_rect->y + pango_font_metrics_get_descent (metrics);
4396 pango_font_metrics_unref (metrics);
4400 logical_rect->y = 0;
4401 logical_rect->height = 0;
4403 g_object_unref (font);
4407 logical_rect->y = 0;
4408 logical_rect->height = 0;
4412 pango_font_description_free (font_desc);
4414 logical_rect->x = 0;
4415 logical_rect->width = 0;
4420 pango_layout_line_get_empty_extents (PangoLayoutLine *line,
4421 PangoRectangle *logical_rect)
4423 pango_layout_get_empty_extents_at_index (line->layout, line->start_index, logical_rect);
4427 pango_layout_run_get_extents (PangoLayoutRun *run,
4428 PangoRectangle *run_ink,
4429 PangoRectangle *run_logical)
4431 PangoRectangle logical;
4432 ItemProperties properties;
4434 if (G_UNLIKELY (!run_ink && !run_logical))
4437 pango_layout_get_item_properties (run->item, &properties);
4439 if (!run_logical && (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE))
4440 run_logical = &logical;
4442 if (!run_logical && (properties.uline != PANGO_UNDERLINE_NONE || properties.strikethrough))
4443 run_logical = &logical;
4445 if (properties.shape_set)
4446 _pango_shape_get_extents (run->item->num_chars,
4447 properties.shape_ink_rect,
4448 properties.shape_logical_rect,
4449 run_ink, run_logical);
4451 pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
4452 run_ink, run_logical);
4454 if (run_ink && (properties.uline != PANGO_UNDERLINE_NONE || properties.strikethrough))
4456 PangoFontMetrics *metrics = pango_font_get_metrics (run->item->analysis.font,
4457 run->item->analysis.language);
4458 int underline_thickness = pango_font_metrics_get_underline_thickness (metrics);
4459 int underline_position = pango_font_metrics_get_underline_position (metrics);
4460 int strikethrough_thickness = pango_font_metrics_get_strikethrough_thickness (metrics);
4461 int strikethrough_position = pango_font_metrics_get_strikethrough_position (metrics);
4465 /* the underline/strikethrough takes x,width of logical_rect. reflect
4466 * that into ink_rect.
4468 new_pos = MIN (run_ink->x, run_logical->x);
4469 run_ink->width = MAX (run_ink->x + run_ink->width, run_logical->x + run_logical->width) - new_pos;
4470 run_ink->x = new_pos;
4472 /* We should better handle the case of height==0 in the following cases.
4473 * If run_ink->height == 0, we should adjust run_ink->y appropriately.
4476 if (properties.strikethrough)
4478 if (run_ink->height == 0)
4480 run_ink->height = strikethrough_thickness;
4481 run_ink->y = -strikethrough_position;
4485 switch (properties.uline)
4487 case PANGO_UNDERLINE_ERROR:
4488 run_ink->height = MAX (run_ink->height,
4489 3 * underline_thickness - underline_position - run_ink->y);
4491 case PANGO_UNDERLINE_SINGLE:
4492 run_ink->height = MAX (run_ink->height,
4493 underline_thickness - underline_position - run_ink->y);
4495 case PANGO_UNDERLINE_DOUBLE:
4496 run_ink->height = MAX (run_ink->height,
4497 3 * underline_thickness - underline_position - run_ink->y);
4499 case PANGO_UNDERLINE_LOW:
4500 run_ink->height += 2 * underline_thickness;
4502 case PANGO_UNDERLINE_NONE:
4505 g_critical ("unknown underline mode");
4509 pango_font_metrics_unref (metrics);
4512 if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE)
4514 gboolean is_hinted = (run_logical->y & run_logical->height & (PANGO_SCALE - 1)) == 0;
4515 int adjustment = run_logical->y + run_logical->height / 2;
4518 adjustment = PANGO_UNITS_ROUND (adjustment);
4520 properties.rise += adjustment;
4523 if (properties.rise != 0)
4526 run_ink->y -= properties.rise;
4529 run_logical->y -= properties.rise;
4534 * pango_layout_line_get_extents:
4535 * @line: a #PangoLayoutLine
4536 * @ink_rect: rectangle used to store the extents of the glyph string
4537 * as drawn, or %NULL
4538 * @logical_rect: rectangle used to store the logical extents of the glyph
4541 * Computes the logical and ink extents of a layout line. See
4542 * pango_font_get_glyph_extents() for details about the interpretation
4543 * of the rectangles.
4546 pango_layout_line_get_extents (PangoLayoutLine *line,
4547 PangoRectangle *ink_rect,
4548 PangoRectangle *logical_rect)
4550 PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
4553 gboolean caching = FALSE;
4555 g_return_if_fail (LINE_IS_VALID (line));
4557 if (!LINE_IS_VALID (line))
4560 if (G_UNLIKELY (!ink_rect && !logical_rect))
4563 switch (private->cache_status)
4568 *ink_rect = private->ink_rect;
4570 *logical_rect = private->logical_rect;
4577 ink_rect = &private->ink_rect;
4579 logical_rect = &private->logical_rect;
4592 ink_rect->width = 0;
4593 ink_rect->height = 0;
4598 logical_rect->x = 0;
4599 logical_rect->y = 0;
4600 logical_rect->width = 0;
4601 logical_rect->height = 0;
4604 tmp_list = line->runs;
4607 PangoLayoutRun *run = tmp_list->data;
4609 PangoRectangle run_ink;
4610 PangoRectangle run_logical;
4612 pango_layout_run_get_extents (run,
4613 ink_rect ? &run_ink : NULL,
4618 if (ink_rect->width == 0 || ink_rect->height == 0)
4620 *ink_rect = run_ink;
4621 ink_rect->x += x_pos;
4623 else if (run_ink.width != 0 && run_ink.height != 0)
4625 new_pos = MIN (ink_rect->x, x_pos + run_ink.x);
4626 ink_rect->width = MAX (ink_rect->x + ink_rect->width,
4627 x_pos + run_ink.x + run_ink.width) - new_pos;
4628 ink_rect->x = new_pos;
4630 new_pos = MIN (ink_rect->y, run_ink.y);
4631 ink_rect->height = MAX (ink_rect->y + ink_rect->height,
4632 run_ink.y + run_ink.height) - new_pos;
4633 ink_rect->y = new_pos;
4639 new_pos = MIN (logical_rect->x, x_pos + run_logical.x);
4640 logical_rect->width = MAX (logical_rect->x + logical_rect->width,
4641 x_pos + run_logical.x + run_logical.width) - new_pos;
4642 logical_rect->x = new_pos;
4644 new_pos = MIN (logical_rect->y, run_logical.y);
4645 logical_rect->height = MAX (logical_rect->y + logical_rect->height,
4646 run_logical.y + run_logical.height) - new_pos;
4647 logical_rect->y = new_pos;
4650 x_pos += run_logical.width;
4651 tmp_list = tmp_list->next;
4654 if (logical_rect && !line->runs)
4655 pango_layout_line_get_empty_extents (line, logical_rect);
4659 if (&private->ink_rect != ink_rect)
4660 private->ink_rect = *ink_rect;
4661 if (&private->logical_rect != logical_rect)
4662 private->logical_rect = *logical_rect;
4663 private->cache_status = CACHED;
4667 static PangoLayoutLine *
4668 pango_layout_line_new (PangoLayout *layout)
4670 PangoLayoutLinePrivate *private = g_slice_new (PangoLayoutLinePrivate);
4672 private->ref_count = 1;
4673 private->line.layout = layout;
4674 private->line.runs = NULL;
4675 private->line.length = 0;
4676 private->cache_status = NOT_CACHED;
4678 /* Note that we leave start_index, resolved_dir, and is_paragraph_start
4681 return (PangoLayoutLine *) private;
4685 * pango_layout_line_get_pixel_extents:
4686 * @layout_line: a #PangoLayoutLine
4687 * @ink_rect: rectangle used to store the extents of the glyph string
4688 * as drawn, or %NULL
4689 * @logical_rect: rectangle used to store the logical extents of the glyph
4692 * Computes the logical and ink extents of @layout_line in device units.
4693 * This function just calls pango_layout_line_get_extents() followed by
4694 * two pango_extents_to_pixels() calls, rounding @ink_rect and @logical_rect
4695 * such that the rounded rectangles fully contain the unrounded one (that is,
4696 * passes them as first argument to pango_extents_to_pixels()).
4699 pango_layout_line_get_pixel_extents (PangoLayoutLine *layout_line,
4700 PangoRectangle *ink_rect,
4701 PangoRectangle *logical_rect)
4703 g_return_if_fail (LINE_IS_VALID (layout_line));
4705 pango_layout_line_get_extents (layout_line, ink_rect, logical_rect);
4706 pango_extents_to_pixels (ink_rect, NULL);
4707 pango_extents_to_pixels (logical_rect, NULL);
4711 * NB: This implement the exact same algorithm as
4712 * reorder-items.c:pango_reorder_items().
4716 reorder_runs_recurse (GSList *items, int n_items)
4718 GSList *tmp_list, *level_start_node;
4719 int i, level_start_i;
4720 int min_level = G_MAXINT;
4721 GSList *result = NULL;
4727 for (i=0; i<n_items; i++)
4729 PangoLayoutRun *run = tmp_list->data;
4731 min_level = MIN (min_level, run->item->analysis.level);
4733 tmp_list = tmp_list->next;
4737 level_start_node = items;
4739 for (i=0; i<n_items; i++)
4741 PangoLayoutRun *run = tmp_list->data;
4743 if (run->item->analysis.level == min_level)
4747 if (i > level_start_i)
4748 result = g_slist_concat (reorder_runs_recurse (level_start_node, i - level_start_i), result);
4749 result = g_slist_prepend (result, run);
4753 if (i > level_start_i)
4754 result = g_slist_concat (result, reorder_runs_recurse (level_start_node, i - level_start_i));
4755 result = g_slist_append (result, run);
4758 level_start_i = i + 1;
4759 level_start_node = tmp_list->next;
4762 tmp_list = tmp_list->next;
4767 if (i > level_start_i)
4768 result = g_slist_concat (reorder_runs_recurse (level_start_node, i - level_start_i), result);
4772 if (i > level_start_i)
4773 result = g_slist_concat (result, reorder_runs_recurse (level_start_node, i - level_start_i));
4780 pango_layout_line_reorder (PangoLayoutLine *line)
4782 GSList *logical_runs = line->runs;
4783 line->runs = reorder_runs_recurse (logical_runs, g_slist_length (logical_runs));
4784 g_slist_free (logical_runs);
4788 get_item_letter_spacing (PangoItem *item)
4790 ItemProperties properties;
4792 pango_layout_get_item_properties (item, &properties);
4794 return properties.letter_spacing;
4798 pad_glyphstring_right (PangoGlyphString *glyphs,
4799 ParaBreakState *state,
4802 int glyph = glyphs->num_glyphs - 1;
4804 while (glyph >= 0 && glyphs->glyphs[glyph].geometry.width == 0)
4810 state->remaining_width -= adjustment;
4811 glyphs->glyphs[glyph].geometry.width += adjustment;
4812 if (glyphs->glyphs[glyph].geometry.width < 0)
4814 state->remaining_width += glyphs->glyphs[glyph].geometry.width;
4815 glyphs->glyphs[glyph].geometry.width = 0;
4820 pad_glyphstring_left (PangoGlyphString *glyphs,
4821 ParaBreakState *state,
4826 while (glyph < glyphs->num_glyphs && glyphs->glyphs[glyph].geometry.width == 0)
4829 if (glyph == glyphs->num_glyphs)
4832 state->remaining_width -= adjustment;
4833 glyphs->glyphs[glyph].geometry.width += adjustment;
4834 glyphs->glyphs[glyph].geometry.x_offset += adjustment;
4838 is_tab_run (PangoLayout *layout,
4839 PangoLayoutRun *run)
4841 return (layout->text[run->item->offset] == '\t');
4845 zero_line_final_space (PangoLayoutLine *line,
4846 ParaBreakState *state,
4847 PangoLayoutRun *run)
4849 PangoLayout *layout = line->layout;
4850 PangoItem *item = run->item;
4851 PangoGlyphString *glyphs = run->glyphs;
4852 int glyph = item->analysis.level % 2 ? 0 : glyphs->num_glyphs - 1;
4855 /* if the final char of line forms a cluster, and it's
4856 * a whitespace char, zero its glyph's width as it's been wrapped
4859 if (glyphs->num_glyphs < 1 || state->start_offset == 0 ||
4860 !layout->log_attrs[state->start_offset - 1].is_white)
4863 p = g_utf8_prev_char (layout->text + item->offset + item->length);
4864 if (p != layout->text + item->offset + glyphs->log_clusters[glyph])
4867 state->remaining_width += glyphs->glyphs[glyph].geometry.width;
4868 glyphs->glyphs[glyph].geometry.width = 0;
4871 /* When doing shaping, we add the letter spacing value for a
4872 * run after every grapheme in the run. This produces ugly
4873 * asymmetrical results, so what this routine is redistributes
4874 * that space to the beginning and the end of the run.
4876 * We also trim the letter spacing from runs adjacent to
4877 * tabs and from the outside runs of the lines so that things
4878 * line up properly. The line breaking and tab positioning
4879 * were computed without this trimming so they are no longer
4880 * exactly correct, but this won't be very noticeable in most
4884 adjust_line_letter_spacing (PangoLayoutLine *line,
4885 ParaBreakState *state)
4887 PangoLayout *layout = line->layout;
4889 PangoLayoutRun *last_run;
4893 /* If we have tab stops and the resolved direction of the
4894 * line is RTL, then we need to walk through the line
4895 * in reverse direction to figure out the corrections for
4899 if (line->resolved_dir == PANGO_DIRECTION_RTL)
4901 for (l = line->runs; l; l = l->next)
4902 if (is_tab_run (layout, l->data))
4904 line->runs = g_slist_reverse (line->runs);
4910 /* Walk over the runs in the line, redistributing letter
4911 * spacing from the end of the run to the start of the
4912 * run and trimming letter spacing from the ends of the
4913 * runs adjacent to the ends of the line or tab stops.
4915 * We accumulate a correction factor from this trimming
4916 * which we add onto the next tab stop space to keep the
4917 * things properly aligned.
4922 for (l = line->runs; l; l = l->next)
4924 PangoLayoutRun *run = l->data;
4925 PangoLayoutRun *next_run = l->next ? l->next->data : NULL;
4927 if (is_tab_run (layout, run))
4929 pad_glyphstring_right (run->glyphs, state, tab_adjustment);
4934 PangoLayoutRun *visual_next_run = reversed ? last_run : next_run;
4935 PangoLayoutRun *visual_last_run = reversed ? next_run : last_run;
4936 int run_spacing = get_item_letter_spacing (run->item);
4937 int space_left, space_right;
4939 distribute_letter_spacing (run_spacing, &space_left, &space_right);
4941 if (run->glyphs->glyphs[0].geometry.width == 0)
4943 /* we've zeroed this space glyph at the end of line, now remove
4944 * the letter spacing added to its adjacent glyph */
4945 pad_glyphstring_left (run->glyphs, state, - space_left);
4947 else if (!visual_last_run || is_tab_run (layout, visual_last_run))
4949 pad_glyphstring_left (run->glyphs, state, - space_left);
4950 tab_adjustment += space_left;
4953 if (run->glyphs->glyphs[run->glyphs->num_glyphs - 1].geometry.width == 0)
4955 /* we've zeroed this space glyph at the end of line, now remove
4956 * the letter spacing added to its adjacent glyph */
4957 pad_glyphstring_right (run->glyphs, state, - space_right);
4959 else if (!visual_next_run || is_tab_run (layout, visual_next_run))
4961 pad_glyphstring_right (run->glyphs, state, - space_right);
4962 tab_adjustment += space_right;
4970 line->runs = g_slist_reverse (line->runs);
4974 justify_clusters (PangoLayoutLine *line,
4975 ParaBreakState *state)
4977 int total_remaining_width, total_gaps = 0;
4978 int added_so_far, gaps_so_far;
4986 total_remaining_width = state->remaining_width;
4987 if (total_remaining_width <= 0)
4990 /* hint to full pixel if total remaining width was so */
4991 is_hinted = (total_remaining_width & (PANGO_SCALE - 1)) == 0;
4993 for (mode = MEASURE; mode <= ADJUST; mode++)
4998 for (run_iter = line->runs; run_iter; run_iter = run_iter->next)
5000 PangoLayoutRun *run = run_iter->data;
5001 PangoGlyphString *glyphs = run->glyphs;
5002 gboolean is_first_gap = TRUE;
5006 for (i = 0; i < glyphs->num_glyphs; i++)
5008 if (!glyphs->glyphs[i].attr.is_cluster_start)
5011 /* also don't expand zero-width spaces at the end of runs */
5012 if (glyphs->glyphs[i].geometry.width == 0)
5014 if (i == glyphs->num_glyphs -1)
5017 if (i == 0 && glyphs->num_glyphs > 1 && glyphs->glyphs[i+1].attr.is_cluster_start)
5023 is_first_gap = FALSE;
5031 int adjustment, space_left, space_right;
5033 adjustment = (gaps_so_far * total_remaining_width) / total_gaps - added_so_far;
5035 adjustment = PANGO_UNITS_ROUND (adjustment);
5036 /* distribute to before/after */
5037 distribute_letter_spacing (adjustment, &space_left, &space_right);
5039 glyphs->glyphs[i-1].geometry.width += space_left ;
5040 glyphs->glyphs[i ].geometry.width += space_right;
5041 glyphs->glyphs[i ].geometry.x_offset += space_right;
5043 added_so_far += adjustment;
5048 if (mode == MEASURE)
5050 total_gaps = gaps_so_far;
5052 if (total_gaps == 0)
5054 /* a single cluster, can't really justify it */
5060 state->remaining_width -= added_so_far;
5064 justify_words (PangoLayoutLine *line,
5065 ParaBreakState *state)
5067 const gchar *text = line->layout->text;
5068 const PangoLogAttr *log_attrs = line->layout->log_attrs;
5071 int total_remaining_width, total_space_width = 0;
5072 int added_so_far, spaces_so_far;
5080 total_remaining_width = state->remaining_width;
5081 if (total_remaining_width <= 0)
5084 /* hint to full pixel if total remaining width was so */
5085 is_hinted = (total_remaining_width & (PANGO_SCALE - 1)) == 0;
5087 for (mode = MEASURE; mode <= ADJUST; mode++)
5092 offset = state->line_start_offset;
5093 for (run_iter = line->runs; run_iter; run_iter = run_iter->next)
5095 PangoLayoutRun *run = run_iter->data;
5096 PangoGlyphString *glyphs = run->glyphs;
5097 PangoGlyphItemIter cluster_iter;
5098 gboolean have_cluster;
5100 for (have_cluster = pango_glyph_item_iter_init_start (&cluster_iter, run, text);
5102 have_cluster = pango_glyph_item_iter_next_cluster (&cluster_iter))
5107 if (!log_attrs[offset + cluster_iter.start_char].is_expandable_space)
5110 dir = (cluster_iter.start_glyph < cluster_iter.end_glyph) ? 1 : -1;
5111 for (i = cluster_iter.start_glyph; i != cluster_iter.end_glyph; i += dir)
5113 int glyph_width = glyphs->glyphs[i].geometry.width;
5115 if (glyph_width == 0)
5118 spaces_so_far += glyph_width;
5124 adjustment = ((guint64) spaces_so_far * total_remaining_width) / total_space_width - added_so_far;
5126 adjustment = PANGO_UNITS_ROUND (adjustment);
5128 glyphs->glyphs[i].geometry.width += adjustment;
5129 added_so_far += adjustment;
5134 offset += glyphs->num_glyphs;
5137 if (mode == MEASURE)
5139 total_space_width = spaces_so_far;
5141 if (total_space_width == 0)
5143 justify_clusters (line, state);
5149 state->remaining_width -= added_so_far;
5153 pango_layout_line_postprocess (PangoLayoutLine *line,
5154 ParaBreakState *state,
5157 gboolean ellipsized = FALSE;
5159 DEBUG ("postprocessing", line, state);
5161 /* Truncate the logical-final whitespace in the line if we broke the line at it
5164 /* The runs are in reverse order at this point, since we prepended them to the list.
5165 * So, the first run is the last logical run. */
5166 zero_line_final_space (line, state, line->runs->data);
5170 line->runs = g_slist_reverse (line->runs);
5172 /* Ellipsize the line if necessary
5174 if (G_UNLIKELY (state->line_width >= 0 &&
5175 should_ellipsize_current_line (line->layout, state)))
5177 ellipsized = _pango_layout_line_ellipsize (line, state->attrs, state->line_width);
5180 DEBUG ("after removing final space", line, state);
5182 /* Now convert logical to visual order
5184 pango_layout_line_reorder (line);
5186 DEBUG ("after reordering", line, state);
5188 /* Fixup letter spacing between runs
5190 adjust_line_letter_spacing (line, state);
5192 DEBUG ("after letter spacing", line, state);
5194 /* Distribute extra space between words if justifying and line was wrapped
5196 if (line->layout->justify && (wrapped || ellipsized))
5198 /* if we ellipsized, we don't have remaining_width set */
5199 if (state->remaining_width < 0)
5200 state->remaining_width = state->line_width - pango_layout_line_get_width (line);
5202 justify_words (line, state);
5205 DEBUG ("after justification", line, state);
5207 line->layout->is_wrapped |= wrapped;
5208 line->layout->is_ellipsized |= ellipsized;
5212 pango_layout_get_item_properties (PangoItem *item,
5213 ItemProperties *properties)
5215 GSList *tmp_list = item->analysis.extra_attrs;
5217 properties->uline = PANGO_UNDERLINE_NONE;
5218 properties->strikethrough = FALSE;
5219 properties->letter_spacing = 0;
5220 properties->rise = 0;
5221 properties->shape_set = FALSE;
5222 properties->shape_ink_rect = NULL;
5223 properties->shape_logical_rect = NULL;
5227 PangoAttribute *attr = tmp_list->data;
5229 switch ((int) attr->klass->type)
5231 case PANGO_ATTR_UNDERLINE:
5232 properties->uline = ((PangoAttrInt *)attr)->value;
5235 case PANGO_ATTR_STRIKETHROUGH:
5236 properties->strikethrough = ((PangoAttrInt *)attr)->value;
5239 case PANGO_ATTR_RISE:
5240 properties->rise = ((PangoAttrInt *)attr)->value;
5243 case PANGO_ATTR_LETTER_SPACING:
5244 properties->letter_spacing = ((PangoAttrInt *)attr)->value;
5247 case PANGO_ATTR_SHAPE:
5248 properties->shape_set = TRUE;
5249 properties->shape_logical_rect = &((PangoAttrShape *)attr)->logical_rect;
5250 properties->shape_ink_rect = &((PangoAttrShape *)attr)->ink_rect;
5256 tmp_list = tmp_list->next;
5261 next_cluster_start (PangoGlyphString *gs,
5266 i = cluster_start + 1;
5267 while (i < gs->num_glyphs)
5269 if (gs->glyphs[i].attr.is_cluster_start)
5275 return gs->num_glyphs;
5279 cluster_width (PangoGlyphString *gs,
5285 width = gs->glyphs[cluster_start].geometry.width;
5286 i = cluster_start + 1;
5287 while (i < gs->num_glyphs)
5289 if (gs->glyphs[i].attr.is_cluster_start)
5292 width += gs->glyphs[i].geometry.width;
5300 offset_y (PangoLayoutIter *iter,
5305 line_ext = (Extents*)iter->line_extents_link->data;
5307 *y += line_ext->baseline;
5310 /* Sets up the iter for the start of a new cluster. cluster_start_index
5311 * is the byte index of the cluster start relative to the run.
5314 update_cluster (PangoLayoutIter *iter,
5315 int cluster_start_index)
5318 PangoGlyphString *gs;
5321 iter->character_position = 0;
5323 gs = iter->run->glyphs;
5324 iter->cluster_width = cluster_width (gs, iter->cluster_start);
5325 iter->next_cluster_glyph = next_cluster_start (gs, iter->cluster_start);
5329 /* For LTR text, finding the length of the cluster is easy
5330 * since logical and visual runs are in the same direction.
5332 if (iter->next_cluster_glyph < gs->num_glyphs)
5333 cluster_length = gs->log_clusters[iter->next_cluster_glyph] - cluster_start_index;
5335 cluster_length = iter->run->item->length - cluster_start_index;
5339 /* For RTL text, we have to scan backwards to find the previous
5340 * visual cluster which is the next logical cluster.
5342 int i = iter->cluster_start;
5343 while (i > 0 && gs->log_clusters[i - 1] == cluster_start_index)
5347 cluster_length = iter->run->item->length - cluster_start_index;
5349 cluster_length = gs->log_clusters[i - 1] - cluster_start_index;
5352 cluster_text = iter->layout->text + iter->run->item->offset + cluster_start_index;
5353 iter->cluster_num_chars = pango_utf8_strlen (cluster_text, cluster_length);
5356 iter->index = cluster_text - iter->layout->text;
5358 iter->index = g_utf8_prev_char (cluster_text + cluster_length) - iter->layout->text;
5362 update_run (PangoLayoutIter *iter,
5363 int run_start_index)
5367 line_ext = (Extents*)iter->line_extents_link->data;
5369 /* Note that in iter_new() the iter->run_width
5370 * is garbage but we don't use it since we're on the first run of
5373 if (iter->run_list_link == iter->line->runs)
5374 iter->run_x = line_ext->logical_rect.x;
5376 iter->run_x += iter->run_width;
5380 iter->run_width = pango_glyph_string_get_width (iter->run->glyphs);
5384 /* The empty run at the end of a line */
5385 iter->run_width = 0;
5389 iter->ltr = (iter->run->item->analysis.level % 2) == 0;
5393 iter->cluster_start = 0;
5394 iter->cluster_x = iter->run_x;
5398 update_cluster (iter, iter->run->glyphs->log_clusters[0]);
5402 iter->cluster_width = 0;
5403 iter->character_position = 0;
5404 iter->cluster_num_chars = 0;
5405 iter->index = run_start_index;
5410 * pango_layout_iter_copy:
5411 * @iter: a #PangoLayoutIter, may be %NULL
5413 * Copies a #PangLayoutIter.
5415 * Return value: the newly allocated #PangoLayoutIter, which should
5416 * be freed with pango_layout_iter_free(), or %NULL if
5422 pango_layout_iter_copy (PangoLayoutIter *iter)
5424 PangoLayoutIter *new;
5430 new = g_slice_new (PangoLayoutIter);
5432 new->layout = g_object_ref (iter->layout);
5433 new->line_list_link = iter->line_list_link;
5434 new->line = iter->line;
5435 pango_layout_line_ref (new->line);
5437 new->run_list_link = iter->run_list_link;
5438 new->run = iter->run;
5439 new->index = iter->index;
5441 new->line_extents = NULL;
5442 new->line_extents_link = NULL;
5443 for (l = iter->line_extents; l; l = l->next)
5445 new->line_extents = g_slist_prepend (new->line_extents,
5446 g_slice_dup (Extents, l->data));
5447 if (l == iter->line_extents_link)
5448 new->line_extents_link = new->line_extents;
5450 new->line_extents = g_slist_reverse (new->line_extents);
5452 new->run_x = iter->run_x;
5453 new->run_width = iter->run_width;
5454 new->ltr = iter->ltr;
5456 new->cluster_x = iter->cluster_x;
5457 new->cluster_width = iter->cluster_width;
5459 new->cluster_start = iter->cluster_start;
5460 new->next_cluster_glyph = iter->next_cluster_glyph;
5462 new->cluster_num_chars = iter->cluster_num_chars;
5463 new->character_position = iter->character_position;
5465 new->layout_width = iter->layout_width;
5471 pango_layout_iter_get_type (void)
5473 static GType our_type = 0;
5475 if (G_UNLIKELY (our_type == 0))
5476 our_type = g_boxed_type_register_static (I_("PangoLayoutIter"),
5477 (GBoxedCopyFunc) pango_layout_iter_copy,
5478 (GBoxedFreeFunc) pango_layout_iter_free);
5484 * pango_layout_get_iter:
5485 * @layout: a #PangoLayout
5487 * Returns an iterator to iterate over the visual extents of the layout.
5489 * Return value: the new #PangoLayoutIter that should be freed using
5490 * pango_layout_iter_free().
5493 pango_layout_get_iter (PangoLayout *layout)
5495 int run_start_index;
5496 PangoLayoutIter *iter;
5497 PangoRectangle logical_rect;
5499 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
5501 iter = g_slice_new (PangoLayoutIter);
5503 iter->layout = layout;
5504 g_object_ref (iter->layout);
5506 pango_layout_check_lines (layout);
5508 iter->line_list_link = layout->lines;
5509 iter->line = iter->line_list_link->data;
5510 pango_layout_line_ref (iter->line);
5512 run_start_index = iter->line->start_index;
5513 iter->run_list_link = iter->line->runs;
5515 if (iter->run_list_link)
5517 iter->run = iter->run_list_link->data;
5518 run_start_index = iter->run->item->offset;
5523 iter->line_extents = NULL;
5524 pango_layout_get_extents_internal (layout,
5527 &iter->line_extents);
5528 iter->layout_width = layout->width == -1 ? logical_rect.width : layout->width;
5530 iter->line_extents_link = iter->line_extents;
5532 update_run (iter, run_start_index);
5538 * pango_layout_iter_free:
5539 * @iter: a #PangoLayoutIter, may be %NULL
5541 * Frees an iterator that's no longer in use.
5544 pango_layout_iter_free (PangoLayoutIter *iter)
5549 g_slist_foreach (iter->line_extents, (GFunc)extents_free, NULL);
5550 g_slist_free (iter->line_extents);
5551 pango_layout_line_unref (iter->line);
5552 g_object_unref (iter->layout);
5553 g_slice_free (PangoLayoutIter, iter);
5557 * pango_layout_iter_get_index:
5558 * @iter: a #PangoLayoutIter
5560 * Gets the current byte index. Note that iterating forward by char
5561 * moves in visual order, not logical order, so indexes may not be
5562 * sequential. Also, the index may be equal to the length of the text
5563 * in the layout, if on the %NULL run (see pango_layout_iter_get_run()).
5565 * Return value: current byte index.
5568 pango_layout_iter_get_index (PangoLayoutIter *iter)
5570 if (ITER_IS_INVALID (iter))
5577 * pango_layout_iter_get_run:
5578 * @iter: a #PangoLayoutIter
5580 * Gets the current run. When iterating by run, at the end of each
5581 * line, there's a position with a %NULL run, so this function can return
5582 * %NULL. The %NULL run at the end of each line ensures that all lines have
5583 * at least one run, even lines consisting of only a newline.
5585 * Use the faster pango_layout_iter_get_run_readonly() if you do not plan
5586 * to modify the contents of the run (glyphs, glyph widths, etc.).
5588 * Return value: the current run.
5591 pango_layout_iter_get_run (PangoLayoutIter *iter)
5593 if (ITER_IS_INVALID (iter))
5596 pango_layout_line_leaked (iter->line);
5602 * pango_layout_iter_get_run_readonly:
5603 * @iter: a #PangoLayoutIter
5605 * Gets the current run. When iterating by run, at the end of each
5606 * line, there's a position with a %NULL run, so this function can return
5607 * %NULL. The %NULL run at the end of each line ensures that all lines have
5608 * at least one run, even lines consisting of only a newline.
5610 * This is a faster alternative to pango_layout_iter_get_run(),
5611 * but the user is not expected
5612 * to modify the contents of the run (glyphs, glyph widths, etc.).
5614 * Return value: the current run, that should not be modified.
5619 pango_layout_iter_get_run_readonly (PangoLayoutIter *iter)
5621 if (ITER_IS_INVALID (iter))
5624 pango_layout_line_leaked (iter->line);
5629 /* an inline-able version for local use */
5630 static PangoLayoutLine*
5631 _pango_layout_iter_get_line (PangoLayoutIter *iter)
5637 * pango_layout_iter_get_line:
5638 * @iter: a #PangoLayoutIter
5640 * Gets the current line.
5642 * Use the faster pango_layout_iter_get_line_readonly() if you do not plan
5643 * to modify the contents of the line (glyphs, glyph widths, etc.).
5645 * Return value: the current line.
5648 pango_layout_iter_get_line (PangoLayoutIter *iter)
5650 if (ITER_IS_INVALID (iter))
5653 pango_layout_line_leaked (iter->line);
5659 * pango_layout_iter_get_line_readonly:
5660 * @iter: a #PangoLayoutIter
5662 * Gets the current line for read-only access.
5664 * This is a faster alternative to pango_layout_iter_get_line(),
5665 * but the user is not expected
5666 * to modify the contents of the line (glyphs, glyph widths, etc.).
5668 * Return value: the current line, that should not be modified.
5673 pango_layout_iter_get_line_readonly (PangoLayoutIter *iter)
5675 if (ITER_IS_INVALID (iter))
5682 * pango_layout_iter_at_last_line:
5683 * @iter: a #PangoLayoutIter
5685 * Determines whether @iter is on the last line of the layout.
5687 * Return value: %TRUE if @iter is on the last line.
5690 pango_layout_iter_at_last_line (PangoLayoutIter *iter)
5692 if (ITER_IS_INVALID (iter))
5695 return iter->line_extents_link->next == NULL;
5699 * pango_layout_iter_get_layout:
5700 * @iter: a #PangoLayoutIter
5702 * Gets the layout associated with a #PangoLayoutIter.
5704 * Return value: the layout associated with @iter.
5709 pango_layout_iter_get_layout (PangoLayoutIter *iter)
5711 /* check is redundant as it simply checks that iter->layout is not NULL */
5712 if (ITER_IS_INVALID (iter))
5715 return iter->layout;
5720 line_is_terminated (PangoLayoutIter *iter)
5722 /* There is a real terminator at the end of each paragraph other
5725 if (iter->line_list_link->next)
5727 PangoLayoutLine *next_line = iter->line_list_link->next->data;
5728 if (next_line->is_paragraph_start)
5735 /* Moves to the next non-empty line. If @include_terminators
5736 * is set, a line with just an explicit paragraph separator
5737 * is considered non-empty.
5740 next_nonempty_line (PangoLayoutIter *iter,
5741 gboolean include_terminators)
5747 result = pango_layout_iter_next_line (iter);
5751 if (iter->line->runs)
5754 if (include_terminators && line_is_terminated (iter))
5761 /* Moves to the next non-empty run. If @include_terminators
5762 * is set, the trailing run at the end of a line with an explicit
5763 * paragraph separator is considered non-empty.
5766 next_nonempty_run (PangoLayoutIter *iter,
5767 gboolean include_terminators)
5773 result = pango_layout_iter_next_run (iter);
5780 if (include_terminators && line_is_terminated (iter))
5787 /* Like pango_layout_next_cluster(), but if @include_terminators
5788 * is set, includes the fake runs/clusters for empty lines.
5789 * (But not positions introduced by line wrapping).
5792 next_cluster_internal (PangoLayoutIter *iter,
5793 gboolean include_terminators)
5795 PangoGlyphString *gs;
5798 if (ITER_IS_INVALID (iter))
5801 if (iter->run == NULL)
5802 return next_nonempty_line (iter, include_terminators);
5804 gs = iter->run->glyphs;
5806 next_start = iter->next_cluster_glyph;
5807 if (next_start == gs->num_glyphs)
5809 return next_nonempty_run (iter, include_terminators);
5813 iter->cluster_start = next_start;
5814 iter->cluster_x += iter->cluster_width;
5815 update_cluster(iter, gs->log_clusters[iter->cluster_start]);
5822 * pango_layout_iter_next_char:
5823 * @iter: a #PangoLayoutIter
5825 * Moves @iter forward to the next character in visual order. If @iter was already at
5826 * the end of the layout, returns %FALSE.
5828 * Return value: whether motion was possible.
5831 pango_layout_iter_next_char (PangoLayoutIter *iter)
5835 if (ITER_IS_INVALID (iter))
5838 if (iter->run == NULL)
5840 /* We need to fake an iterator position in the middle of a \r\n line terminator */
5841 if (line_is_terminated (iter) &&
5842 strncmp (iter->layout->text + iter->line->start_index + iter->line->length, "\r\n", 2) == 0 &&
5843 iter->character_position == 0)
5845 iter->character_position++;
5849 return next_nonempty_line (iter, TRUE);
5852 iter->character_position++;
5853 if (iter->character_position >= iter->cluster_num_chars)
5854 return next_cluster_internal (iter, TRUE);
5856 text = iter->layout->text;
5858 iter->index = g_utf8_next_char (text + iter->index) - text;
5860 iter->index = g_utf8_prev_char (text + iter->index) - text;
5866 * pango_layout_iter_next_cluster:
5867 * @iter: a #PangoLayoutIter
5869 * Moves @iter forward to the next cluster in visual order. If @iter
5870 * was already at the end of the layout, returns %FALSE.
5872 * Return value: whether motion was possible.
5875 pango_layout_iter_next_cluster (PangoLayoutIter *iter)
5877 return next_cluster_internal (iter, FALSE);
5881 * pango_layout_iter_next_run:
5882 * @iter: a #PangoLayoutIter
5884 * Moves @iter forward to the next run in visual order. If @iter was
5885 * already at the end of the layout, returns %FALSE.
5887 * Return value: whether motion was possible.
5890 pango_layout_iter_next_run (PangoLayoutIter *iter)
5892 int next_run_start; /* byte index */
5895 if (ITER_IS_INVALID (iter))
5898 if (iter->run == NULL)
5899 return pango_layout_iter_next_line (iter);
5901 next_link = iter->run_list_link->next;
5903 if (next_link == NULL)
5905 /* Moving on to the zero-width "virtual run" at the end of each
5908 next_run_start = iter->run->item->offset + iter->run->item->length;
5910 iter->run_list_link = NULL;
5914 iter->run_list_link = next_link;
5915 iter->run = iter->run_list_link->data;
5916 next_run_start = iter->run->item->offset;
5919 update_run (iter, next_run_start);
5925 * pango_layout_iter_next_line:
5926 * @iter: a #PangoLayoutIter
5928 * Moves @iter forward to the start of the next line. If @iter is
5929 * already on the last line, returns %FALSE.
5931 * Return value: whether motion was possible.
5934 pango_layout_iter_next_line (PangoLayoutIter *iter)
5938 if (ITER_IS_INVALID (iter))
5941 next_link = iter->line_list_link->next;
5943 if (next_link == NULL)
5946 iter->line_list_link = next_link;
5948 pango_layout_line_unref (iter->line);
5950 iter->line = iter->line_list_link->data;
5952 pango_layout_line_ref (iter->line);
5954 iter->run_list_link = iter->line->runs;
5956 if (iter->run_list_link)
5957 iter->run = iter->run_list_link->data;
5961 iter->line_extents_link = iter->line_extents_link->next;
5962 g_assert (iter->line_extents_link != NULL);
5964 update_run (iter, iter->line->start_index);
5970 * pango_layout_iter_get_char_extents:
5971 * @iter: a #PangoLayoutIter
5972 * @logical_rect: rectangle to fill with logical extents
5974 * Gets the extents of the current character, in layout coordinates
5975 * (origin is the top left of the entire layout). Only logical extents
5976 * can sensibly be obtained for characters; ink extents make sense only
5977 * down to the level of clusters.
5981 pango_layout_iter_get_char_extents (PangoLayoutIter *iter,
5982 PangoRectangle *logical_rect)
5984 PangoRectangle cluster_rect;
5987 if (ITER_IS_INVALID (iter))
5990 if (logical_rect == NULL)
5993 pango_layout_iter_get_cluster_extents (iter, NULL, &cluster_rect);
5995 if (iter->run == NULL)
5997 /* When on the NULL run, cluster, char, and run all have the
6000 *logical_rect = cluster_rect;
6004 x0 = (iter->character_position * cluster_rect.width) / iter->cluster_num_chars;
6005 x1 = ((iter->character_position + 1) * cluster_rect.width) / iter->cluster_num_chars;
6007 logical_rect->width = x1 - x0;
6008 logical_rect->height = cluster_rect.height;
6009 logical_rect->y = cluster_rect.y;
6010 logical_rect->x = cluster_rect.x + x0;
6014 * pango_layout_iter_get_cluster_extents:
6015 * @iter: a #PangoLayoutIter
6016 * @ink_rect: rectangle to fill with ink extents, or %NULL
6017 * @logical_rect: rectangle to fill with logical extents, or %NULL
6019 * Gets the extents of the current cluster, in layout coordinates
6020 * (origin is the top left of the entire layout).
6024 pango_layout_iter_get_cluster_extents (PangoLayoutIter *iter,
6025 PangoRectangle *ink_rect,
6026 PangoRectangle *logical_rect)
6028 if (ITER_IS_INVALID (iter))
6031 if (iter->run == NULL)
6033 /* When on the NULL run, cluster, char, and run all have the
6036 pango_layout_iter_get_run_extents (iter, ink_rect, logical_rect);
6040 pango_glyph_string_extents_range (iter->run->glyphs,
6041 iter->cluster_start,
6042 iter->next_cluster_glyph,
6043 iter->run->item->analysis.font,
6049 ink_rect->x += iter->cluster_x;
6050 offset_y (iter, &ink_rect->y);
6055 g_assert (logical_rect->width == iter->cluster_width);
6056 logical_rect->x += iter->cluster_x;
6057 offset_y (iter, &logical_rect->y);
6062 * pango_layout_iter_get_run_extents:
6063 * @iter: a #PangoLayoutIter
6064 * @ink_rect: rectangle to fill with ink extents, or %NULL
6065 * @logical_rect: rectangle to fill with logical extents, or %NULL
6067 * Gets the extents of the current run in layout coordinates
6068 * (origin is the top left of the entire layout).
6072 pango_layout_iter_get_run_extents (PangoLayoutIter *iter,
6073 PangoRectangle *ink_rect,
6074 PangoRectangle *logical_rect)
6076 if (G_UNLIKELY (!ink_rect && !logical_rect))
6079 if (ITER_IS_INVALID (iter))
6084 pango_layout_run_get_extents (iter->run, ink_rect, logical_rect);
6088 offset_y (iter, &ink_rect->y);
6089 ink_rect->x += iter->run_x;
6094 offset_y (iter, &logical_rect->y);
6095 logical_rect->x += iter->run_x;
6100 /* The empty run at the end of a line */
6102 pango_layout_iter_get_line_extents (iter, ink_rect, logical_rect);
6106 ink_rect->x = iter->run_x;
6107 ink_rect->width = 0;
6112 logical_rect->x = iter->run_x;
6113 logical_rect->width = 0;
6119 * pango_layout_iter_get_line_extents:
6120 * @iter: a #PangoLayoutIter
6121 * @ink_rect: rectangle to fill with ink extents, or %NULL
6122 * @logical_rect: rectangle to fill with logical extents, or %NULL
6124 * Obtains the extents of the current line. @ink_rect or @logical_rect
6125 * can be %NULL if you aren't interested in them. Extents are in layout
6126 * coordinates (origin is the top-left corner of the entire
6127 * #PangoLayout). Thus the extents returned by this function will be
6128 * the same width/height but not at the same x/y as the extents
6129 * returned from pango_layout_line_get_extents().
6133 pango_layout_iter_get_line_extents (PangoLayoutIter *iter,
6134 PangoRectangle *ink_rect,
6135 PangoRectangle *logical_rect)
6139 if (ITER_IS_INVALID (iter))
6142 ext = iter->line_extents_link->data;
6146 get_line_extents_layout_coords (iter->layout, iter->line,
6148 ext->logical_rect.y,
6155 *logical_rect = ext->logical_rect;
6159 * pango_layout_iter_get_line_yrange:
6160 * @iter: a #PangoLayoutIter
6161 * @y0_: start of line
6164 * Divides the vertical space in the #PangoLayout being iterated over
6165 * between the lines in the layout, and returns the space belonging to
6166 * the current line. A line's range includes the line's logical
6167 * extents, plus half of the spacing above and below the line, if
6168 * pango_layout_set_spacing() has been called to set layout spacing.
6169 * The Y positions are in layout coordinates (origin at top left of the
6174 pango_layout_iter_get_line_yrange (PangoLayoutIter *iter,
6181 if (ITER_IS_INVALID (iter))
6184 ext = iter->line_extents_link->data;
6186 half_spacing = iter->layout->spacing / 2;
6188 /* Note that if layout->spacing is odd, the remainder spacing goes
6189 * above the line (this is pretty arbitrary of course)
6194 /* No spacing above the first line */
6196 if (iter->line_extents_link == iter->line_extents)
6197 *y0 = ext->logical_rect.y;
6199 *y0 = ext->logical_rect.y - (iter->layout->spacing - half_spacing);
6204 /* No spacing below the last line */
6205 if (iter->line_extents_link->next == NULL)
6206 *y1 = ext->logical_rect.y + ext->logical_rect.height;
6208 *y1 = ext->logical_rect.y + ext->logical_rect.height + half_spacing;
6213 * pango_layout_iter_get_baseline:
6214 * @iter: a #PangoLayoutIter
6216 * Gets the Y position of the current line's baseline, in layout
6217 * coordinates (origin at top left of the entire layout).
6219 * Return value: baseline of current line.
6222 pango_layout_iter_get_baseline (PangoLayoutIter *iter)
6226 if (ITER_IS_INVALID (iter))
6229 ext = iter->line_extents_link->data;
6231 return ext->baseline;
6235 * pango_layout_iter_get_layout_extents:
6236 * @iter: a #PangoLayoutIter
6237 * @ink_rect: rectangle to fill with ink extents, or %NULL
6238 * @logical_rect: rectangle to fill with logical extents, or %NULL
6240 * Obtains the extents of the #PangoLayout being iterated
6241 * over. @ink_rect or @logical_rect can be %NULL if you
6242 * aren't interested in them.
6246 pango_layout_iter_get_layout_extents (PangoLayoutIter *iter,
6247 PangoRectangle *ink_rect,
6248 PangoRectangle *logical_rect)
6250 if (ITER_IS_INVALID (iter))
6253 pango_layout_get_extents (iter->layout, ink_rect, logical_rect);