Upload tizen 2.0 beta source
[external/pango1.0.git] / pango / pango-layout.c
1 /* Pango
2  * pango-layout.c: High-level layout driver
3  *
4  * Copyright (C) 2000, 2001, 2006 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23 #include "pango-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"
29 #include <string.h>
30
31 #include "pango-layout-private.h"
32
33
34 typedef struct _Extents Extents;
35 typedef struct _ItemProperties ItemProperties;
36 typedef struct _ParaBreakState ParaBreakState;
37
38 struct _Extents
39 {
40   /* Vertical position of the line's baseline in layout coords */
41   int baseline;
42
43   /* Line extents in layout coords */
44   PangoRectangle ink_rect;
45   PangoRectangle logical_rect;
46 };
47
48 struct _ItemProperties
49 {
50   PangoUnderline  uline;
51   gboolean        strikethrough;
52   gint            rise;
53   gint            letter_spacing;
54   gboolean        shape_set;
55   PangoRectangle *shape_ink_rect;
56   PangoRectangle *shape_logical_rect;
57 };
58
59 struct _PangoLayoutIter
60 {
61   PangoLayout *layout;
62   GSList *line_list_link;
63   PangoLayoutLine *line;
64
65   /* If run is NULL, it means we're on a "virtual run"
66    * at the end of the line with 0 width
67    */
68   GSList *run_list_link;
69   PangoLayoutRun *run; /* FIXME nuke this, just keep the link */
70   int index;
71
72   /* list of Extents for each line in layout coordinates */
73   GSList *line_extents;
74   GSList *line_extents_link;
75
76   /* X position of the current run */
77   int run_x;
78
79   /* Width of the current run */
80   int run_width;
81
82   /* this run is left-to-right */
83   gboolean ltr;
84
85   /* X position of the left side of the current cluster */
86   int cluster_x;
87
88   /* The width of the current cluster */
89   int cluster_width;
90
91   /* glyph offset to the current cluster start */
92   int cluster_start;
93
94   /* first glyph in the next cluster */
95   int next_cluster_glyph;
96
97   /* number of Unicode chars in current cluster */
98   int cluster_num_chars;
99
100   /* visual position of current character within the cluster */
101   int character_position;
102
103   /* the real width of layout */
104   int layout_width;
105 };
106
107 typedef struct _PangoLayoutLinePrivate PangoLayoutLinePrivate;
108
109 struct _PangoLayoutLinePrivate
110 {
111   PangoLayoutLine line;
112   guint ref_count;
113
114   /* Extents cache status:
115    *
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.
119    */
120   enum {
121     NOT_CACHED,
122     CACHED,
123     LEAKED
124   } cache_status;
125   PangoRectangle ink_rect;
126   PangoRectangle logical_rect;
127 };
128
129 struct _PangoLayoutClass
130 {
131   GObjectClass parent_class;
132
133
134 };
135
136 #define LINE_IS_VALID(line) ((line)->layout != NULL)
137
138 #ifdef G_DISABLE_CHECKS
139 #define ITER_IS_INVALID(iter) FALSE
140 #else
141 #define ITER_IS_INVALID(iter) G_UNLIKELY (check_invalid ((iter), G_STRLOC))
142 static gboolean
143 check_invalid (PangoLayoutIter *iter,
144                const char      *loc)
145 {
146   if (iter->line->layout == NULL)
147     {
148       g_warning ("%s: PangoLayout changed since PangoLayoutIter was created, iterator invalid", loc);
149       return TRUE;
150     }
151   else
152     {
153       return FALSE;
154     }
155 }
156 #endif
157
158 static void pango_layout_clear_lines (PangoLayout *layout);
159 static void pango_layout_check_lines (PangoLayout *layout);
160
161 static PangoAttrList *pango_layout_get_effective_attributes (PangoLayout *layout);
162
163 static PangoLayoutLine * pango_layout_line_new         (PangoLayout     *layout);
164 static void              pango_layout_line_postprocess (PangoLayoutLine *line,
165                                                         ParaBreakState  *state,
166                                                         gboolean         wrapped);
167
168 static int *pango_layout_line_get_log2vis_map (PangoLayoutLine  *line,
169                                                gboolean          strong);
170 static int *pango_layout_line_get_vis2log_map (PangoLayoutLine  *line,
171                                                gboolean          strong);
172 static void pango_layout_line_leaked (PangoLayoutLine *line);
173
174 /* doesn't leak line */
175 static PangoLayoutLine* _pango_layout_iter_get_line (PangoLayoutIter *iter);
176
177 static void pango_layout_get_item_properties (PangoItem      *item,
178                                               ItemProperties *properties);
179
180 static void pango_layout_get_empty_extents_at_index (PangoLayout    *layout,
181                                                      int             index,
182                                                      PangoRectangle *logical_rect);
183
184 static void pango_layout_finalize    (GObject          *object);
185
186 G_DEFINE_TYPE (PangoLayout, pango_layout, G_TYPE_OBJECT)
187
188 static void
189 pango_layout_init (PangoLayout *layout)
190 {
191   layout->attrs = NULL;
192   layout->font_desc = NULL;
193   layout->text = NULL;
194   layout->length = 0;
195   layout->width = -1;
196   layout->height = -1;
197   layout->indent = 0;
198
199   layout->alignment = PANGO_ALIGN_LEFT;
200   layout->justify = FALSE;
201   layout->auto_dir = TRUE;
202
203   layout->log_attrs = NULL;
204   layout->lines = NULL;
205   layout->line_count = 0;
206
207   layout->tab_width = -1;
208   layout->unknown_glyphs_count = -1;
209
210   layout->wrap = PANGO_WRAP_WORD;
211   layout->is_wrapped = FALSE;
212   layout->ellipsize = PANGO_ELLIPSIZE_NONE;
213   layout->is_ellipsized = FALSE;
214 }
215
216 static void
217 pango_layout_class_init (PangoLayoutClass *klass)
218 {
219   GObjectClass *object_class = G_OBJECT_CLASS (klass);
220
221   object_class->finalize = pango_layout_finalize;
222 }
223
224 static void
225 pango_layout_finalize (GObject *object)
226 {
227   PangoLayout *layout;
228
229   layout = PANGO_LAYOUT (object);
230
231   pango_layout_clear_lines (layout);
232
233   if (layout->context)
234     g_object_unref (layout->context);
235
236   if (layout->attrs)
237     pango_attr_list_unref (layout->attrs);
238
239   g_free (layout->text);
240
241   if (layout->font_desc)
242     pango_font_description_free (layout->font_desc);
243
244   if (layout->tabs)
245     pango_tab_array_free (layout->tabs);
246
247   G_OBJECT_CLASS (pango_layout_parent_class)->finalize (object);
248 }
249
250
251 /**
252  * pango_layout_new:
253  * @context: a #PangoContext
254  *
255  * Create a new #PangoLayout object with attributes initialized to
256  * default values for a particular #PangoContext.
257  *
258  * Return value: the newly allocated #PangoLayout, with a reference
259  *               count of one, which should be freed with
260  *               g_object_unref().
261  **/
262 PangoLayout *
263 pango_layout_new (PangoContext *context)
264 {
265   PangoLayout *layout;
266
267   g_return_val_if_fail (context != NULL, NULL);
268
269   layout = g_object_new (PANGO_TYPE_LAYOUT, NULL);
270
271   layout->context = context;
272   g_object_ref (context);
273
274   return layout;
275 }
276
277 /**
278  * pango_layout_copy:
279  * @src: a #PangoLayout
280  *
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
283  * value.
284  *
285  * Return value: the newly allocated #PangoLayout, with a reference
286  *               count of one, which should be freed with
287  *               g_object_unref().
288  **/
289 PangoLayout*
290 pango_layout_copy (PangoLayout *src)
291 {
292   PangoLayout *layout;
293
294   g_return_val_if_fail (PANGO_IS_LAYOUT (src), NULL);
295
296   /* Copy referenced members */
297
298   layout = pango_layout_new (src->context);
299   if (src->attrs)
300     layout->attrs = pango_attr_list_copy (src->attrs);
301   if (src->font_desc)
302     layout->font_desc = pango_font_description_copy (src->font_desc);
303   if (src->tabs)
304     layout->tabs = pango_tab_array_copy (src->tabs);
305
306   /* Dupped */
307   layout->text = g_strdup (src->text);
308
309   /* Value fields */
310   memcpy (&layout->copy_begin, &src->copy_begin,
311           G_STRUCT_OFFSET (PangoLayout, copy_end) - G_STRUCT_OFFSET (PangoLayout, copy_begin));
312
313   return layout;
314 }
315
316 /**
317  * pango_layout_get_context:
318  * @layout: a #PangoLayout
319  *
320  * Retrieves the #PangoContext used for this layout.
321  *
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.
325  **/
326 PangoContext *
327 pango_layout_get_context (PangoLayout *layout)
328 {
329   g_return_val_if_fail (layout != NULL, NULL);
330
331   return layout->context;
332 }
333
334 /**
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.
339  *
340  * Sets the width to which the lines of the #PangoLayout should wrap or
341  * ellipsized.  The default value is -1: no width set.
342  **/
343 void
344 pango_layout_set_width (PangoLayout *layout,
345                         int          width)
346 {
347   g_return_if_fail (layout != NULL);
348
349   if (width != layout->width)
350     {
351       layout->width = width;
352       pango_layout_clear_lines (layout);
353     }
354 }
355
356 /**
357  * pango_layout_get_width:
358  * @layout: a #PangoLayout
359  *
360  * Gets the width to which the lines of the #PangoLayout should wrap.
361  *
362  * Return value: the width in Pango units, or -1 if no width set.
363  **/
364 int
365 pango_layout_get_width (PangoLayout    *layout)
366 {
367   g_return_val_if_fail (layout != NULL, 0);
368   return layout->width;
369 }
370
371 /**
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.
376  *
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
379  * negative.
380  *
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.
386  *
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.
395  *
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
400  * future.
401  *
402  * Since: 1.20
403  **/
404 void
405 pango_layout_set_height (PangoLayout *layout,
406                          int          height)
407 {
408   g_return_if_fail (layout != NULL);
409
410   if (height != layout->height)
411     {
412       layout->height = height;
413
414       /* Do not invalidate if the number of lines requested is
415        * larger than the total number of lines in layout.
416        * Bug 549003
417        */
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);
422     }
423 }
424
425 /**
426  * pango_layout_get_height:
427  * @layout: a #PangoLayout
428  *
429  * Gets the height of layout used for ellipsization.  See
430  * pango_layout_set_height() for details.
431  *
432  * Return value: the height, in Pango units if positive, or
433  * number of lines if negative.
434  *
435  * Since: 1.20
436  **/
437 int
438 pango_layout_get_height (PangoLayout    *layout)
439 {
440   g_return_val_if_fail (layout != NULL, 0);
441   return layout->height;
442 }
443
444 /**
445  * pango_layout_set_wrap:
446  * @layout: a #PangoLayout
447  * @wrap: the wrap mode
448  *
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.
452  **/
453 void
454 pango_layout_set_wrap (PangoLayout  *layout,
455                        PangoWrapMode wrap)
456 {
457   g_return_if_fail (PANGO_IS_LAYOUT (layout));
458
459   if (layout->wrap != wrap)
460     {
461       layout->wrap = wrap;
462
463       if (layout->width != -1)
464         pango_layout_clear_lines (layout);
465     }
466 }
467
468 /**
469  * pango_layout_get_wrap:
470  * @layout: a #PangoLayout
471  *
472  * Gets the wrap mode for the layout.
473  *
474  * Use pango_layout_is_wrapped() to query whether any paragraphs
475  * were actually wrapped.
476  *
477  * Return value: active wrap mode.
478  **/
479 PangoWrapMode
480 pango_layout_get_wrap (PangoLayout *layout)
481 {
482   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), 0);
483
484   return layout->wrap;
485 }
486
487 /**
488  * pango_layout_is_wrapped:
489  * @layout: a #PangoLayout
490  *
491  * Queries whether the layout had to wrap any paragraphs.
492  *
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
496  * to be wrapped.
497  *
498  * Return value: %TRUE if any paragraphs had to be wrapped, %FALSE
499  * otherwise.
500  *
501  * Since: 1.16
502  */
503 gboolean
504 pango_layout_is_wrapped (PangoLayout *layout)
505 {
506   g_return_val_if_fail (layout != NULL, FALSE);
507
508   pango_layout_check_lines (layout);
509
510   return layout->is_wrapped;
511 }
512
513 /**
514  * pango_layout_set_indent
515  * @layout: a #PangoLayout.
516  * @indent: the amount by which to indent.
517  *
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.
522  *
523  * The indent setting is ignored if layout alignment is set to
524  * %PANGO_ALIGN_CENTER.
525  **/
526 void
527 pango_layout_set_indent (PangoLayout *layout,
528                          int          indent)
529 {
530   g_return_if_fail (layout != NULL);
531
532   if (indent != layout->indent)
533     {
534       layout->indent = indent;
535       pango_layout_clear_lines (layout);
536     }
537 }
538
539 /**
540  * pango_layout_get_indent:
541  * @layout: a #PangoLayout
542  *
543  * Gets the paragraph indent width in Pango units. A negative value
544  * indicates a hanging indentation.
545  *
546  * Return value: the indent in Pango units.
547  **/
548 int
549 pango_layout_get_indent (PangoLayout *layout)
550 {
551   g_return_val_if_fail (layout != NULL, 0);
552   return layout->indent;
553 }
554
555 /**
556  * pango_layout_set_spacing:
557  * @layout: a #PangoLayout.
558  * @spacing: the amount of spacing
559  *
560  * Sets the amount of spacing in Pango unit between the lines of the
561  * layout.
562  **/
563 void
564 pango_layout_set_spacing (PangoLayout *layout,
565                           int          spacing)
566 {
567   g_return_if_fail (layout != NULL);
568
569   if (spacing != layout->spacing)
570     {
571       layout->spacing = spacing;
572       pango_layout_clear_lines (layout);
573     }
574 }
575
576 /**
577  * pango_layout_get_spacing:
578  * @layout: a #PangoLayout
579  *
580  * Gets the amount of spacing between the lines of the layout.
581  *
582  * Return value: the spacing in Pango units.
583  **/
584 int
585 pango_layout_get_spacing (PangoLayout *layout)
586 {
587   g_return_val_if_fail (layout != NULL, 0);
588   return layout->spacing;
589 }
590
591 /**
592  * pango_layout_set_attributes:
593  * @layout: a #PangoLayout
594  * @attrs: a #PangoAttrList, can be %NULL
595  *
596  * Sets the text attributes for a layout object.
597  * References @attrs, so the caller can unref its reference.
598  **/
599 void
600 pango_layout_set_attributes (PangoLayout   *layout,
601                              PangoAttrList *attrs)
602 {
603   PangoAttrList *old_attrs;
604   g_return_if_fail (layout != NULL);
605
606   old_attrs = layout->attrs;
607
608   /* We always clear lines such that this function can be called
609    * whenever attrs changes.
610    */
611
612   layout->attrs = attrs;
613   if (layout->attrs)
614     pango_attr_list_ref (layout->attrs);
615   pango_layout_clear_lines (layout);
616
617   if (old_attrs)
618     pango_attr_list_unref (old_attrs);
619   layout->tab_width = -1;
620 }
621
622 /**
623  * pango_layout_get_attributes:
624  * @layout: a #PangoLayout
625  *
626  * Gets the attribute list for the layout, if any.
627  *
628  * Return value: a #PangoAttrList.
629  **/
630 PangoAttrList*
631 pango_layout_get_attributes (PangoLayout *layout)
632 {
633   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
634
635   return layout->attrs;
636 }
637
638 /**
639  * pango_layout_set_font_description:
640  * @layout: a #PangoLayout
641  * @desc: the new #PangoFontDescription, or %NULL to unset the
642  *        current font description
643  *
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.
647  **/
648 void
649 pango_layout_set_font_description (PangoLayout                 *layout,
650                                     const PangoFontDescription *desc)
651 {
652   g_return_if_fail (layout != NULL);
653
654   if (desc != layout->font_desc &&
655       (!desc || !layout->font_desc || !pango_font_description_equal(desc, layout->font_desc)))
656     {
657       if (layout->font_desc)
658         pango_font_description_free (layout->font_desc);
659
660       layout->font_desc = desc ? pango_font_description_copy (desc) : NULL;
661
662       pango_layout_clear_lines (layout);
663       layout->tab_width = -1;
664     }
665 }
666
667 /**
668  * pango_layout_get_font_description:
669  * @layout: a #PangoLayout
670  *
671  * Gets the font description for the layout, if any.
672  *
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.
677  *
678  * Since: 1.8
679  **/
680 G_CONST_RETURN PangoFontDescription *
681 pango_layout_get_font_description (PangoLayout *layout)
682 {
683   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
684
685   return layout->font_desc;
686 }
687
688 /**
689  * pango_layout_set_justify:
690  * @layout: a #PangoLayout
691  * @justify: whether the lines in the layout should be justified.
692  *
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
697  * the characters.
698  *
699  * Note that this setting is not implemented and so is ignored in Pango
700  * older than 1.18.
701  **/
702 void
703 pango_layout_set_justify (PangoLayout *layout,
704                           gboolean     justify)
705 {
706   g_return_if_fail (layout != NULL);
707
708   if (justify != layout->justify)
709     {
710       layout->justify = justify;
711
712       if (layout->is_ellipsized || layout->is_wrapped)
713         pango_layout_clear_lines (layout);
714     }
715 }
716
717 /**
718  * pango_layout_get_justify:
719  * @layout: a #PangoLayout
720  *
721  * Gets whether each complete line should be stretched to fill the entire
722  * width of the layout.
723  *
724  * Return value: the justify.
725  **/
726 gboolean
727 pango_layout_get_justify (PangoLayout *layout)
728 {
729   g_return_val_if_fail (layout != NULL, FALSE);
730   return layout->justify;
731 }
732
733 /**
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.
738  *
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.
747  *
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()).
751  *
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.
755  *
756  * Since: 1.4
757  **/
758 void
759 pango_layout_set_auto_dir (PangoLayout *layout,
760                            gboolean     auto_dir)
761 {
762   g_return_if_fail (PANGO_IS_LAYOUT (layout));
763
764   auto_dir = auto_dir != FALSE;
765
766   if (auto_dir != layout->auto_dir)
767     {
768       layout->auto_dir = auto_dir;
769       pango_layout_clear_lines (layout);
770     }
771 }
772
773 /**
774  * pango_layout_get_auto_dir:
775  * @layout: a #PangoLayout
776  *
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().
780  *
781  * Return value: %TRUE if the bidirectional base direction
782  *   is computed from the layout's contents, %FALSE otherwise.
783  *
784  * Since: 1.4
785  **/
786 gboolean
787 pango_layout_get_auto_dir (PangoLayout *layout)
788 {
789   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE);
790
791   return layout->auto_dir;
792 }
793
794 /**
795  * pango_layout_set_alignment:
796  * @layout: a #PangoLayout
797  * @alignment: the alignment
798  *
799  * Sets the alignment for the layout: how partial lines are
800  * positioned within the horizontal space available.
801  **/
802 void
803 pango_layout_set_alignment (PangoLayout   *layout,
804                             PangoAlignment alignment)
805 {
806   g_return_if_fail (layout != NULL);
807
808   if (alignment != layout->alignment)
809     {
810       layout->alignment = alignment;
811       pango_layout_clear_lines (layout);
812     }
813 }
814
815 /**
816  * pango_layout_get_alignment:
817  * @layout: a #PangoLayout
818  *
819  * Gets the alignment for the layout: how partial lines are
820  * positioned within the horizontal space available.
821  *
822  * Return value: the alignment.
823  **/
824 PangoAlignment
825 pango_layout_get_alignment (PangoLayout *layout)
826 {
827   g_return_val_if_fail (layout != NULL, PANGO_ALIGN_LEFT);
828   return layout->alignment;
829 }
830
831
832 /**
833  * pango_layout_set_tabs:
834  * @layout: a #PangoLayout
835  * @tabs: a #PangoTabArray, or %NULL
836  *
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.
841  **/
842 void
843 pango_layout_set_tabs (PangoLayout   *layout,
844                        PangoTabArray *tabs)
845 {
846   g_return_if_fail (PANGO_IS_LAYOUT (layout));
847
848
849   if (tabs != layout->tabs)
850     {
851       if (layout->tabs)
852         pango_tab_array_free (layout->tabs);
853
854       layout->tabs = tabs ? pango_tab_array_copy (tabs) : NULL;
855
856       pango_layout_clear_lines (layout);
857     }
858 }
859
860 /**
861  * pango_layout_get_tabs:
862  * @layout: a #PangoLayout
863  *
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().
868  *
869  * Return value: a copy of the tabs for this layout, or %NULL.
870  **/
871 PangoTabArray*
872 pango_layout_get_tabs (PangoLayout *layout)
873 {
874   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
875
876   if (layout->tabs)
877     return pango_tab_array_copy (layout->tabs);
878   else
879     return NULL;
880 }
881
882 /**
883  * pango_layout_set_single_paragraph_mode:
884  * @layout: a #PangoLayout
885  * @setting: new setting
886  *
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.
891  **/
892 void
893 pango_layout_set_single_paragraph_mode (PangoLayout *layout,
894                                         gboolean     setting)
895 {
896   g_return_if_fail (PANGO_IS_LAYOUT (layout));
897
898   setting = setting != FALSE;
899
900   if (layout->single_paragraph != setting)
901     {
902       layout->single_paragraph = setting;
903
904       pango_layout_clear_lines (layout);
905     }
906 }
907
908 /**
909  * pango_layout_get_single_paragraph_mode:
910  * @layout: a #PangoLayout
911  *
912  * Obtains the value set by pango_layout_set_single_paragraph_mode().
913  *
914  * Return value: %TRUE if the layout does not break paragraphs at
915  * paragraph separator characters, %FALSE otherwise.
916  **/
917 gboolean
918 pango_layout_get_single_paragraph_mode (PangoLayout *layout)
919 {
920   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE);
921
922   return layout->single_paragraph;
923 }
924
925 /**
926  * pango_layout_set_ellipsize:
927  * @layout: a #PangoLayout
928  * @ellipsize: the new ellipsization mode for @layout
929  *
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().
935  *
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.
941  *
942  * Since: 1.6
943  **/
944 void
945 pango_layout_set_ellipsize (PangoLayout        *layout,
946                             PangoEllipsizeMode  ellipsize)
947 {
948   g_return_if_fail (PANGO_IS_LAYOUT (layout));
949
950   if (ellipsize != layout->ellipsize)
951     {
952       layout->ellipsize = ellipsize;
953
954       if (layout->is_ellipsized || layout->is_wrapped)
955         pango_layout_clear_lines (layout);
956     }
957 }
958
959 /**
960  * pango_layout_get_ellipsize:
961  * @layout: a #PangoLayout
962  *
963  * Gets the type of ellipsization being performed for @layout.
964  * See pango_layout_set_ellipsize()
965  *
966  * Return value: the current ellipsization mode for @layout.
967  *
968  * Use pango_layout_is_ellipsized() to query whether any paragraphs
969  * were actually ellipsized.
970  *
971  * Since: 1.6
972  **/
973 PangoEllipsizeMode
974 pango_layout_get_ellipsize (PangoLayout *layout)
975 {
976   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), PANGO_ELLIPSIZE_NONE);
977
978   return layout->ellipsize;
979 }
980
981 /**
982  * pango_layout_is_ellipsized:
983  * @layout: a #PangoLayout
984  *
985  * Queries whether the layout had to ellipsize any paragraphs.
986  *
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
990  * ellipsized.
991  *
992  * Return value: %TRUE if any paragraphs had to be ellipsized, %FALSE
993  * otherwise.
994  *
995  * Since: 1.16
996  */
997 gboolean
998 pango_layout_is_ellipsized (PangoLayout *layout)
999 {
1000   g_return_val_if_fail (layout != NULL, FALSE);
1001
1002   pango_layout_check_lines (layout);
1003
1004   return layout->is_ellipsized;
1005 }
1006
1007 /**
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
1015  *          positive.
1016  *
1017  * Sets the text of the layout.
1018  *
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
1023  * clear attributes.
1024  **/
1025 void
1026 pango_layout_set_text (PangoLayout *layout,
1027                        const char  *text,
1028                        int          length)
1029 {
1030   char *old_text, *start, *end;
1031
1032   g_return_if_fail (layout != NULL);
1033   g_return_if_fail (length == 0 || text != NULL);
1034
1035   old_text = layout->text;
1036
1037   if (length < 0)
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...
1041      */
1042     layout->text = g_strndup (text, length);
1043   else
1044     layout->text = g_malloc0 (1);
1045
1046   layout->length = strlen (layout->text);
1047
1048   /* validate it, and replace invalid bytes with '?'
1049    */
1050   start = layout->text;
1051   for (;;) {
1052     gboolean valid;
1053
1054     valid = g_utf8_validate (start, -1, (const char **)&end);
1055
1056     if (!*end)
1057       break;
1058
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.
1063      */
1064     if (!valid)
1065       *end++ = -1;
1066
1067     start = end;
1068   }
1069
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()");
1073
1074   layout->n_chars = pango_utf8_strlen (layout->text, -1);
1075
1076   pango_layout_clear_lines (layout);
1077
1078   g_free (old_text);
1079 }
1080
1081 /**
1082  * pango_layout_get_text:
1083  * @layout: a #PangoLayout
1084  *
1085  * Gets the text in the layout. The returned text should not
1086  * be freed or modified.
1087  *
1088  * Return value: the text in the @layout.
1089  **/
1090 G_CONST_RETURN char*
1091 pango_layout_get_text (PangoLayout *layout)
1092 {
1093   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
1094
1095   return layout->text;
1096 }
1097
1098 /**
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
1103  * nul-terminated
1104  *
1105  * Same as pango_layout_set_markup_with_accel(), but
1106  * the markup text isn't scanned for accelerators.
1107  *
1108  **/
1109 void
1110 pango_layout_set_markup (PangoLayout *layout,
1111                          const char  *markup,
1112                          int          length)
1113 {
1114   pango_layout_set_markup_with_accel (layout, markup, length, 0, NULL);
1115 }
1116
1117 /**
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
1123  * nul-terminated
1124  * @accel_marker: marker for accelerators in the text
1125  * @accel_char: return location for first located accelerator, or %NULL
1126  *
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.
1130  *
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.
1138  **/
1139 void
1140 pango_layout_set_markup_with_accel (PangoLayout    *layout,
1141                                     const char     *markup,
1142                                     int             length,
1143                                     gunichar        accel_marker,
1144                                     gunichar       *accel_char)
1145 {
1146   PangoAttrList *list = NULL;
1147   char *text = NULL;
1148   GError *error;
1149
1150   g_return_if_fail (PANGO_IS_LAYOUT (layout));
1151   g_return_if_fail (markup != NULL);
1152
1153   error = NULL;
1154   if (!pango_parse_markup (markup, length,
1155                            accel_marker,
1156                            &list, &text,
1157                            accel_char,
1158                            &error))
1159     {
1160       g_warning ("pango_layout_set_markup_with_accel: %s", error->message);
1161       g_error_free (error);
1162       return;
1163     }
1164
1165   pango_layout_set_text (layout, text, -1);
1166   pango_layout_set_attributes (layout, list);
1167   pango_attr_list_unref (list);
1168   g_free (text);
1169 }
1170
1171 /**
1172  * pango_layout_get_unknown_glyphs_count:
1173  * @layout: a #PangoLayout
1174  *
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.
1178  *
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.
1183  *
1184  * Return value: The number of unknown glyphs in @layout.
1185  *
1186  * Since: 1.16
1187  */
1188 int
1189 pango_layout_get_unknown_glyphs_count (PangoLayout *layout)
1190 {
1191     PangoLayoutLine *line;
1192     PangoLayoutRun *run;
1193     GSList *lines_list;
1194     GSList *runs_list;
1195     int i, count = 0;
1196
1197     g_return_val_if_fail (PANGO_IS_LAYOUT (layout), 0);
1198
1199     pango_layout_check_lines (layout);
1200
1201     if (layout->unknown_glyphs_count >= 0)
1202       return layout->unknown_glyphs_count;
1203
1204     lines_list = layout->lines;
1205     while (lines_list)
1206       {
1207         line = lines_list->data;
1208         runs_list = line->runs;
1209
1210         while (runs_list)
1211           {
1212             run = runs_list->data;
1213
1214             for (i = 0; i < run->glyphs->num_glyphs; i++)
1215               {
1216                 if (run->glyphs->glyphs[i].glyph & PANGO_GLYPH_UNKNOWN_FLAG)
1217                     count++;
1218               }
1219
1220             runs_list = runs_list->next;
1221           }
1222         lines_list = lines_list->next;
1223       }
1224
1225     layout->unknown_glyphs_count = count;
1226     return count;
1227 }
1228
1229 /**
1230  * pango_layout_context_changed:
1231  * @layout: a #PangoLayout
1232  *
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.
1237  **/
1238 void
1239 pango_layout_context_changed (PangoLayout *layout)
1240 {
1241   pango_layout_clear_lines (layout);
1242   layout->tab_width = -1;
1243 }
1244
1245 /**
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.)
1255  *
1256  * Retrieves an array of logical attributes for each character in
1257  * the @layout.
1258  **/
1259 void
1260 pango_layout_get_log_attrs (PangoLayout    *layout,
1261                             PangoLogAttr  **attrs,
1262                             gint           *n_attrs)
1263 {
1264   g_return_if_fail (layout != NULL);
1265
1266   pango_layout_check_lines (layout);
1267
1268   if (attrs)
1269     {
1270       *attrs = g_new (PangoLogAttr, layout->n_chars + 1);
1271       memcpy (*attrs, layout->log_attrs, sizeof(PangoLogAttr) * (layout->n_chars + 1));
1272     }
1273
1274   if (n_attrs)
1275     *n_attrs = layout->n_chars + 1;
1276 }
1277
1278
1279 /**
1280  * pango_layout_get_line_count:
1281  * @layout: #PangoLayout
1282  *
1283  * Retrieves the count of lines for the @layout.
1284  *
1285  * Return value: the line count.
1286  **/
1287 int
1288 pango_layout_get_line_count (PangoLayout   *layout)
1289 {
1290   g_return_val_if_fail (layout != NULL, 0);
1291
1292   pango_layout_check_lines (layout);
1293   return layout->line_count;
1294 }
1295
1296 /**
1297  * pango_layout_get_lines:
1298  * @layout: a #PangoLayout
1299  *
1300  * Returns the lines of the @layout as a list.
1301  *
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.).
1304  *
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.
1309  **/
1310 GSList *
1311 pango_layout_get_lines (PangoLayout *layout)
1312 {
1313   pango_layout_check_lines (layout);
1314
1315   if (layout->lines)
1316     {
1317       GSList *tmp_list = layout->lines;
1318       while (tmp_list)
1319         {
1320           PangoLayoutLine *line = tmp_list->data;
1321           tmp_list = tmp_list->next;
1322
1323           pango_layout_line_leaked (line);
1324         }
1325     }
1326
1327   return layout->lines;
1328 }
1329
1330 /**
1331  * pango_layout_get_lines_readonly:
1332  * @layout: a #PangoLayout
1333  *
1334  * Returns the lines of the @layout as a list.
1335  *
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.).
1339  *
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.
1344  *
1345  * Since: 1.16
1346  **/
1347 GSList *
1348 pango_layout_get_lines_readonly (PangoLayout *layout)
1349 {
1350   pango_layout_check_lines (layout);
1351
1352   return layout->lines;
1353 }
1354
1355 /**
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.
1360  *
1361  * Retrieves a particular line from a #PangoLayout.
1362  *
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.).
1365  *
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.
1370  **/
1371 PangoLayoutLine *
1372 pango_layout_get_line (PangoLayout *layout,
1373                        int          line)
1374 {
1375   GSList *list_item;
1376   g_return_val_if_fail (layout != NULL, NULL);
1377   g_return_val_if_fail (line >= 0, NULL);
1378
1379   if (line < 0)
1380     return NULL;
1381
1382   pango_layout_check_lines (layout);
1383
1384   list_item = g_slist_nth (layout->lines, line);
1385
1386   if (list_item)
1387     {
1388       PangoLayoutLine *line = list_item->data;
1389
1390       pango_layout_line_leaked (line);
1391       return line;
1392     }
1393
1394   return NULL;
1395 }
1396
1397 /**
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.
1402  *
1403  * Retrieves a particular line from a #PangoLayout.
1404  *
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.).
1408  *
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.
1414  *
1415  * Since: 1.16
1416  **/
1417 PangoLayoutLine *
1418 pango_layout_get_line_readonly (PangoLayout *layout,
1419                                 int          line)
1420 {
1421   GSList *list_item;
1422   g_return_val_if_fail (layout != NULL, NULL);
1423   g_return_val_if_fail (line >= 0, NULL);
1424
1425   if (line < 0)
1426     return NULL;
1427
1428   pango_layout_check_lines (layout);
1429
1430   list_item = g_slist_nth (layout->lines, line);
1431
1432   if (list_item)
1433     {
1434       PangoLayoutLine *line = list_item->data;
1435       return line;
1436     }
1437
1438   return NULL;
1439 }
1440
1441 /**
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)
1449  *
1450  * Converts an index within a line to a X position.
1451  *
1452  **/
1453 void
1454 pango_layout_line_index_to_x (PangoLayoutLine  *line,
1455                               int               index,
1456                               int               trailing,
1457                               int              *x_pos)
1458 {
1459   PangoLayout *layout = line->layout;
1460   GSList *run_list = line->runs;
1461   int width = 0;
1462
1463   while (run_list)
1464     {
1465       PangoLayoutRun *run = run_list->data;
1466       ItemProperties properties;
1467
1468       pango_layout_get_item_properties (run->item, &properties);
1469
1470       if (run->item->offset <= index && run->item->offset + run->item->length > index)
1471         {
1472           int offset = g_utf8_pointer_to_offset (layout->text, layout->text + index);
1473           if (trailing)
1474             {
1475               while (index < line->start_index + line->length &&
1476                      offset + 1 < layout->n_chars &&
1477                      !layout->log_attrs[offset + 1].is_cursor_position)
1478                 {
1479                   offset++;
1480                   index = g_utf8_next_char (layout->text + index) - layout->text;
1481                 }
1482             }
1483           else
1484             {
1485               while (index > line->start_index &&
1486                      !layout->log_attrs[offset].is_cursor_position)
1487                 {
1488                   offset--;
1489                   index = g_utf8_prev_char (layout->text + index) - layout->text;
1490                 }
1491
1492             }
1493
1494           pango_glyph_string_index_to_x (run->glyphs,
1495                                          layout->text + run->item->offset,
1496                                          run->item->length,
1497                                          &run->item->analysis,
1498                                          index - run->item->offset, trailing, x_pos);
1499           if (x_pos)
1500             *x_pos += width;
1501
1502           return;
1503         }
1504
1505       width += pango_glyph_string_get_width (run->glyphs);
1506
1507       run_list = run_list->next;
1508     }
1509
1510   if (x_pos)
1511     *x_pos = width;
1512 }
1513
1514 static PangoLayoutLine *
1515 pango_layout_index_to_line (PangoLayout      *layout,
1516                             int               index,
1517                             int              *line_nr,
1518                             PangoLayoutLine **line_before,
1519                             PangoLayoutLine **line_after)
1520 {
1521   GSList *tmp_list;
1522   GSList *line_list;
1523   PangoLayoutLine *line = NULL;
1524   PangoLayoutLine *prev_line = NULL;
1525   int i = -1;
1526
1527   line_list = tmp_list = layout->lines;
1528   while (tmp_list)
1529     {
1530       PangoLayoutLine *tmp_line = tmp_list->data;
1531
1532       if (tmp_line->start_index > index)
1533         break; /* index was in paragraph delimiters */
1534
1535       prev_line = line;
1536       line = tmp_line;
1537       line_list = tmp_list;
1538       i++;
1539
1540       if (line->start_index + line->length > index)
1541         break;
1542
1543       tmp_list = tmp_list->next;
1544     }
1545
1546   if (line_nr)
1547     *line_nr = i;
1548
1549   if (line_before)
1550     *line_before = prev_line;
1551
1552   if (line_after)
1553     *line_after = (line_list && line_list->next) ? line_list->next->data : NULL;
1554
1555   return line;
1556 }
1557
1558 static PangoLayoutLine *
1559 pango_layout_index_to_line_and_extents (PangoLayout     *layout,
1560                                         int              index,
1561                                         PangoRectangle  *line_rect)
1562 {
1563   PangoLayoutIter *iter;
1564   PangoLayoutLine *line = NULL;
1565
1566   iter = pango_layout_get_iter (layout);
1567
1568   if (!ITER_IS_INVALID (iter))
1569     while (TRUE)
1570       {
1571         PangoLayoutLine *tmp_line = _pango_layout_iter_get_line (iter);
1572
1573         if (tmp_line->start_index > index)
1574             break; /* index was in paragraph delimiters */
1575
1576         line = tmp_line;
1577
1578         pango_layout_iter_get_line_extents (iter, NULL, line_rect);
1579
1580         if (line->start_index + line->length > index)
1581           break;
1582
1583         if (!pango_layout_iter_next_line (iter))
1584           break; /* Use end of last line */
1585       }
1586
1587   pango_layout_iter_free (iter);
1588
1589   return line;
1590 }
1591
1592 /**
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)
1603  *
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)
1606  */
1607 void
1608 pango_layout_index_to_line_x (PangoLayout *layout,
1609                               int          index,
1610                               gboolean     trailing,
1611                               int         *line,
1612                               int         *x_pos)
1613 {
1614   int line_num;
1615   PangoLayoutLine *layout_line = NULL;
1616
1617   g_return_if_fail (layout != NULL);
1618   g_return_if_fail (index >= 0);
1619   g_return_if_fail (index <= layout->length);
1620
1621   pango_layout_check_lines (layout);
1622
1623   layout_line = pango_layout_index_to_line (layout, index,
1624                                             &line_num, NULL, NULL);
1625
1626   if (layout_line)
1627     {
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;
1631
1632       if (line)
1633         *line = line_num;
1634
1635       pango_layout_line_index_to_x (layout_line, index, trailing, x_pos);
1636     }
1637   else
1638     {
1639       if (line)
1640         *line = -1;
1641       if (x_pos)
1642         *x_pos = -1;
1643     }
1644 }
1645
1646 /**
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.
1668  *
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.
1675  *
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.
1680  *
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.
1685  **/
1686 void
1687 pango_layout_move_cursor_visually (PangoLayout *layout,
1688                                    gboolean     strong,
1689                                    int          old_index,
1690                                    int          old_trailing,
1691                                    int          direction,
1692                                    int         *new_index,
1693                                    int         *new_trailing)
1694 {
1695   PangoLayoutLine *line = NULL;
1696   PangoLayoutLine *prev_line;
1697   PangoLayoutLine *next_line;
1698
1699   int *log2vis_map;
1700   int *vis2log_map;
1701   int n_vis;
1702   int vis_pos, vis_pos_old, log_pos;
1703   int start_offset;
1704   gboolean off_start = FALSE;
1705   gboolean off_end = FALSE;
1706
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);
1712
1713   direction = (direction >= 0 ? 1 : -1);
1714
1715   pango_layout_check_lines (layout);
1716
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);
1720
1721   start_offset = g_utf8_pointer_to_offset (layout->text, layout->text + line->start_index);
1722
1723   while (old_trailing--)
1724     old_index = g_utf8_next_char (layout->text + old_index) - layout->text;
1725
1726   log2vis_map = pango_layout_line_get_log2vis_map (line, strong);
1727   n_vis = pango_utf8_strlen (layout->text + line->start_index, line->length);
1728
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;
1732
1733   vis_pos = log2vis_map[old_index - line->start_index];
1734
1735   g_free (log2vis_map);
1736
1737   /* Handling movement between lines */
1738   if (vis_pos == 0 && direction < 0)
1739     {
1740       if (line->resolved_dir == PANGO_DIRECTION_LTR)
1741         off_start = TRUE;
1742       else
1743         off_end = TRUE;
1744     }
1745   else if (vis_pos == n_vis && direction > 0)
1746     {
1747       if (line->resolved_dir == PANGO_DIRECTION_LTR)
1748         off_end = TRUE;
1749       else
1750         off_start = TRUE;
1751     }
1752
1753   if (off_start || off_end)
1754     {
1755       /* If we move over a paragraph boundary, count that as
1756        * an extra position in the motion
1757        */
1758       gboolean paragraph_boundary;
1759
1760       if (off_start)
1761         {
1762           if (!prev_line)
1763             {
1764               *new_index = -1;
1765               *new_trailing = 0;
1766               return;
1767             }
1768           line = prev_line;
1769           paragraph_boundary = (line->start_index + line->length != old_index);
1770         }
1771       else
1772         {
1773           if (!next_line)
1774             {
1775               *new_index = G_MAXINT;
1776               *new_trailing = 0;
1777               return;
1778             }
1779           line = next_line;
1780           paragraph_boundary = (line->start_index != old_index);
1781         }
1782
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);
1785
1786       if (vis_pos == 0 && direction < 0)
1787         {
1788           vis_pos = n_vis;
1789           if (paragraph_boundary)
1790             vis_pos++;
1791         }
1792       else /* (vis_pos == n_vis && direction > 0) */
1793         {
1794           vis_pos = 0;
1795           if (paragraph_boundary)
1796             vis_pos--;
1797         }
1798     }
1799
1800   vis2log_map = pango_layout_line_get_vis2log_map (line, strong);
1801
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]);
1805   do
1806     {
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;
1811     }
1812   while (vis_pos > 0 && vis_pos < n_vis &&
1813          !layout->log_attrs[start_offset + log_pos].is_cursor_position);
1814
1815   *new_index = line->start_index + vis2log_map[vis_pos];
1816   g_free (vis2log_map);
1817
1818   *new_trailing = 0;
1819
1820   if (*new_index == line->start_index + line->length && line->length > 0)
1821     {
1822       do
1823         {
1824           log_pos--;
1825           *new_index = g_utf8_prev_char (layout->text + *new_index) - layout->text;
1826           (*new_trailing)++;
1827         }
1828       while (log_pos > 0 && !layout->log_attrs[start_offset + log_pos].is_cursor_position);
1829     }
1830 }
1831
1832 /**
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.
1844  *
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.
1853  *
1854  * Return value: %TRUE if the coordinates were inside text, %FALSE otherwise.
1855  **/
1856 gboolean
1857 pango_layout_xy_to_index (PangoLayout *layout,
1858                           int          x,
1859                           int          y,
1860                           int         *index,
1861                           gint        *trailing)
1862 {
1863   PangoLayoutIter *iter;
1864   PangoLayoutLine *prev_line = NULL;
1865   PangoLayoutLine *found = NULL;
1866   int found_line_x = 0;
1867   int prev_last = 0;
1868   int prev_line_x = 0;
1869   gboolean retval = FALSE;
1870   gboolean outside = FALSE;
1871
1872   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), FALSE);
1873
1874   iter = pango_layout_get_iter (layout);
1875
1876   do
1877     {
1878       PangoRectangle line_logical;
1879       int first_y, last_y;
1880
1881       pango_layout_iter_get_line_extents (iter, NULL, &line_logical);
1882       pango_layout_iter_get_line_yrange (iter, &first_y, &last_y);
1883
1884       if (y < first_y)
1885         {
1886           if (prev_line && y < (prev_last + (first_y - prev_last) / 2))
1887             {
1888               found = prev_line;
1889               found_line_x = prev_line_x;
1890             }
1891           else
1892             {
1893               if (prev_line == NULL)
1894                 outside = TRUE; /* off the top */
1895
1896               found = _pango_layout_iter_get_line (iter);
1897               found_line_x = x - line_logical.x;
1898             }
1899         }
1900       else if (y >= first_y &&
1901                y < last_y)
1902         {
1903           found = _pango_layout_iter_get_line (iter);
1904           found_line_x = x - line_logical.x;
1905         }
1906
1907       prev_line = _pango_layout_iter_get_line (iter);
1908       prev_last = last_y;
1909       prev_line_x = x - line_logical.x;
1910
1911       if (found != NULL)
1912         break;
1913     }
1914   while (pango_layout_iter_next_line (iter));
1915
1916   pango_layout_iter_free (iter);
1917
1918   if (found == NULL)
1919     {
1920       /* Off the bottom of the layout */
1921       outside = TRUE;
1922
1923       found = prev_line;
1924       found_line_x = prev_line_x;
1925     }
1926
1927   retval = pango_layout_line_x_to_index (found,
1928                                          found_line_x,
1929                                          index, trailing);
1930
1931   if (outside)
1932     retval = FALSE;
1933
1934   return retval;
1935 }
1936
1937 /**
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
1942  *
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.
1949  **/
1950 void
1951 pango_layout_index_to_pos (PangoLayout    *layout,
1952                            int             index,
1953                            PangoRectangle *pos)
1954 {
1955   PangoRectangle logical_rect;
1956   PangoLayoutIter *iter;
1957   PangoLayoutLine *layout_line = NULL;
1958   int x_pos;
1959
1960   g_return_if_fail (layout != NULL);
1961   g_return_if_fail (index >= 0);
1962   g_return_if_fail (pos != NULL);
1963
1964   iter = pango_layout_get_iter (layout);
1965
1966   if (!ITER_IS_INVALID (iter))
1967     {
1968       while (TRUE)
1969         {
1970           PangoLayoutLine *tmp_line = _pango_layout_iter_get_line (iter);
1971
1972           if (tmp_line->start_index > index)
1973             {
1974               /* index is in the paragraph delimiters, move to
1975                * end of previous line
1976                */
1977               index = layout_line->start_index + layout_line->length;
1978               break;
1979             }
1980
1981           layout_line = tmp_line;
1982
1983           pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
1984
1985           if (layout_line->start_index + layout_line->length > index)
1986             break;
1987
1988           if (!pango_layout_iter_next_line (iter))
1989             {
1990               index = layout_line->start_index + layout_line->length;
1991               break;
1992             }
1993         }
1994
1995       pos->y = logical_rect.y;
1996       pos->height = logical_rect.height;
1997
1998       pango_layout_line_index_to_x (layout_line, index, 0, &x_pos);
1999       pos->x = logical_rect.x + x_pos;
2000
2001       if (index < layout_line->start_index + layout_line->length)
2002         {
2003           pango_layout_line_index_to_x (layout_line, index, 1, &x_pos);
2004           pos->width = (logical_rect.x + x_pos) - pos->x;
2005         }
2006       else
2007         pos->width = 0;
2008     }
2009
2010   pango_layout_iter_free (iter);
2011 }
2012
2013 static void
2014 pango_layout_line_get_range (PangoLayoutLine *line,
2015                              char           **start,
2016                              char           **end)
2017 {
2018   char *p;
2019
2020   p = line->layout->text + line->start_index;
2021
2022   if (start)
2023     *start = p;
2024   if (end)
2025     *end = p + line->length;
2026 }
2027
2028 static int *
2029 pango_layout_line_get_vis2log_map (PangoLayoutLine *line,
2030                                    gboolean         strong)
2031 {
2032   PangoLayout *layout = line->layout;
2033   PangoDirection prev_dir;
2034   PangoDirection cursor_dir;
2035   GSList *tmp_list;
2036   gchar *start, *end;
2037   int *result;
2038   int pos;
2039   int n_chars;
2040
2041   pango_layout_line_get_range (line, &start, &end);
2042   n_chars = pango_utf8_strlen (start, end - start);
2043
2044   result = g_new (int, n_chars + 1);
2045
2046   if (strong)
2047     cursor_dir = line->resolved_dir;
2048   else
2049     cursor_dir = (line->resolved_dir == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
2050
2051   /* Handle the first visual position
2052    */
2053   if (line->resolved_dir == cursor_dir)
2054     result[0] = line->resolved_dir == PANGO_DIRECTION_LTR ? 0 : end - start;
2055
2056   prev_dir = line->resolved_dir;
2057   pos = 0;
2058   tmp_list = line->runs;
2059   while (tmp_list)
2060     {
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;
2065       int i;
2066
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 */
2069
2070       if (run_dir == PANGO_DIRECTION_LTR)
2071         {
2072           if ((cursor_dir == PANGO_DIRECTION_LTR) ||
2073               (prev_dir == run_dir))
2074             result[pos] = p - start;
2075
2076           p = g_utf8_next_char (p);
2077
2078           for (i = 1; i < run_n_chars; i++)
2079             {
2080               result[pos + i] = p - start;
2081               p = g_utf8_next_char (p);
2082             }
2083
2084           if (cursor_dir == PANGO_DIRECTION_LTR)
2085             result[pos + run_n_chars] = p - start;
2086         }
2087       else
2088         {
2089           if (cursor_dir == PANGO_DIRECTION_RTL)
2090             result[pos + run_n_chars] = p - start;
2091
2092           p = g_utf8_next_char (p);
2093
2094           for (i = 1; i < run_n_chars; i++)
2095             {
2096               result[pos + run_n_chars - i] = p - start;
2097               p = g_utf8_next_char (p);
2098             }
2099
2100           if ((cursor_dir == PANGO_DIRECTION_RTL) ||
2101               (prev_dir == run_dir))
2102             result[pos] = p - start;
2103         }
2104
2105       pos += run_n_chars;
2106       prev_dir = run_dir;
2107       tmp_list = tmp_list->next;
2108     }
2109
2110   /* And the last visual position
2111    */
2112   if ((cursor_dir == line->resolved_dir) || (prev_dir == line->resolved_dir))
2113     result[pos] = line->resolved_dir == PANGO_DIRECTION_LTR ? end - start : 0;
2114
2115   return result;
2116 }
2117
2118 static int *
2119 pango_layout_line_get_log2vis_map (PangoLayoutLine *line,
2120                                    gboolean         strong)
2121 {
2122   gchar *start, *end;
2123   int *reverse_map;
2124   int *result;
2125   int i;
2126   int n_chars;
2127
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);
2131
2132   reverse_map = pango_layout_line_get_vis2log_map (line, strong);
2133
2134   for (i=0; i <= n_chars; i++)
2135     result[reverse_map[i]] = i;
2136
2137   g_free (reverse_map);
2138
2139   return result;
2140 }
2141
2142 static PangoDirection
2143 pango_layout_line_get_char_direction (PangoLayoutLine *layout_line,
2144                                       int              index)
2145 {
2146   GSList *run_list;
2147
2148   run_list = layout_line->runs;
2149   while (run_list)
2150     {
2151       PangoLayoutRun *run = run_list->data;
2152
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;
2155
2156       run_list = run_list->next;
2157     }
2158
2159   g_assert_not_reached ();
2160
2161   return PANGO_DIRECTION_LTR;
2162 }
2163
2164 /**
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)
2170  *
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.
2179  **/
2180 void
2181 pango_layout_get_cursor_pos (PangoLayout    *layout,
2182                              int             index,
2183                              PangoRectangle *strong_pos,
2184                              PangoRectangle *weak_pos)
2185 {
2186   PangoDirection dir1;
2187   PangoRectangle line_rect;
2188   PangoLayoutLine *layout_line = NULL; /* Quiet GCC */
2189   int x1_trailing;
2190   int x2;
2191
2192   g_return_if_fail (layout != NULL);
2193   g_return_if_fail (index >= 0 && index <= layout->length);
2194
2195   layout_line = pango_layout_index_to_line_and_extents (layout, index,
2196                                                         &line_rect);
2197
2198   g_assert (index >= layout_line->start_index);
2199
2200   /* Examine the trailing edge of the character before the cursor */
2201   if (index == layout_line->start_index)
2202     {
2203       dir1 = layout_line->resolved_dir;
2204       if (layout_line->resolved_dir == PANGO_DIRECTION_LTR)
2205         x1_trailing = 0;
2206       else
2207         x1_trailing = line_rect.width;
2208     }
2209   else if (index >= layout_line->start_index + layout_line->length)
2210     {
2211       dir1 = layout_line->resolved_dir;
2212       if (layout_line->resolved_dir == PANGO_DIRECTION_LTR)
2213         x1_trailing = line_rect.width;
2214       else
2215         x1_trailing = 0;
2216     }
2217   else
2218     {
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);
2222     }
2223
2224   /* Examine the leading edge of the character after the cursor */
2225   if (index >= layout_line->start_index + layout_line->length)
2226     {
2227       if (layout_line->resolved_dir == PANGO_DIRECTION_LTR)
2228         x2 = line_rect.width;
2229       else
2230         x2 = 0;
2231     }
2232   else
2233     {
2234       pango_layout_line_index_to_x (layout_line, index, FALSE, &x2);
2235     }
2236
2237   if (strong_pos)
2238     {
2239       strong_pos->x = line_rect.x;
2240
2241       if (dir1 == layout_line->resolved_dir)
2242         strong_pos->x += x1_trailing;
2243       else
2244         strong_pos->x += x2;
2245
2246       strong_pos->y = line_rect.y;
2247       strong_pos->width = 0;
2248       strong_pos->height = line_rect.height;
2249     }
2250
2251   if (weak_pos)
2252     {
2253       weak_pos->x = line_rect.x;
2254
2255       if (dir1 == layout_line->resolved_dir)
2256         weak_pos->x += x2;
2257       else
2258         weak_pos->x += x1_trailing;
2259
2260       weak_pos->y = line_rect.y;
2261       weak_pos->width = 0;
2262       weak_pos->height = line_rect.height;
2263     }
2264 }
2265
2266 static inline int
2267 direction_simple (PangoDirection d)
2268 {
2269   switch (d)
2270     {
2271     case PANGO_DIRECTION_LTR :
2272     case PANGO_DIRECTION_WEAK_LTR :
2273     case PANGO_DIRECTION_TTB_RTL :
2274       return 1;
2275     case PANGO_DIRECTION_RTL :
2276     case PANGO_DIRECTION_WEAK_RTL :
2277     case PANGO_DIRECTION_TTB_LTR :
2278       return -1;
2279     case PANGO_DIRECTION_NEUTRAL :
2280       return 0;
2281     /* no default, compiler should complain if a new values is added */
2282     }
2283   /* not reached */
2284   return 0;
2285 }
2286
2287 static PangoAlignment
2288 get_alignment (PangoLayout     *layout,
2289                PangoLayoutLine *line)
2290 {
2291   PangoAlignment alignment = layout->alignment;
2292
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)))
2296     {
2297       if (alignment == PANGO_ALIGN_LEFT)
2298         alignment = PANGO_ALIGN_RIGHT;
2299       else if (alignment == PANGO_ALIGN_RIGHT)
2300         alignment = PANGO_ALIGN_LEFT;
2301     }
2302
2303   return alignment;
2304 }
2305
2306 static void
2307 get_x_offset (PangoLayout     *layout,
2308               PangoLayoutLine *line,
2309               int              layout_width,
2310               int              line_width,
2311               int             *x_offset)
2312 {
2313   PangoAlignment alignment = get_alignment (layout, line);
2314
2315   /* Alignment */
2316   if (layout_width == 0)
2317     *x_offset = 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;
2322     /* hinting */
2323     if (((layout_width | line_width) & (PANGO_SCALE - 1)) == 0)
2324       {
2325         *x_offset = PANGO_UNITS_ROUND (*x_offset);
2326       }
2327   } else
2328     *x_offset = 0;
2329
2330   /* Indentation */
2331
2332
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
2337    */
2338
2339   if (alignment == PANGO_ALIGN_CENTER)
2340     return;
2341
2342   if (line->is_paragraph_start)
2343     {
2344       if (layout->indent > 0)
2345         {
2346           if (alignment == PANGO_ALIGN_LEFT)
2347             *x_offset += layout->indent;
2348           else
2349             *x_offset -= layout->indent;
2350         }
2351     }
2352   else
2353     {
2354       if (layout->indent < 0)
2355         {
2356           if (alignment == PANGO_ALIGN_LEFT)
2357             *x_offset -= layout->indent;
2358           else
2359             *x_offset += layout->indent;
2360         }
2361     }
2362 }
2363
2364 static void
2365 get_line_extents_layout_coords (PangoLayout     *layout,
2366                                 PangoLayoutLine *line,
2367                                 int              layout_width,
2368                                 int              y_offset,
2369                                 int             *baseline,
2370                                 PangoRectangle  *line_ink_layout,
2371                                 PangoRectangle  *line_logical_layout)
2372 {
2373   int x_offset;
2374   /* Line extents in line coords (origin at line baseline) */
2375   PangoRectangle line_ink;
2376   PangoRectangle line_logical;
2377
2378   pango_layout_line_get_extents (line, line_ink_layout ? &line_ink : NULL,
2379                                  &line_logical);
2380
2381   get_x_offset (layout, line, layout_width, line_logical.width, &x_offset);
2382
2383   /* Convert the line's extents into layout coordinates */
2384   if (line_ink_layout)
2385     {
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;
2389     }
2390
2391   if (line_logical_layout)
2392     {
2393       *line_logical_layout = line_logical;
2394       line_logical_layout->x = line_logical.x + x_offset;
2395       line_logical_layout->y = y_offset;
2396     }
2397
2398   if (baseline)
2399     *baseline = y_offset - line_logical.y;
2400 }
2401
2402 /* if non-NULL line_extents returns a list of line extents
2403  * in layout coordinates
2404  */
2405 static void
2406 pango_layout_get_extents_internal (PangoLayout    *layout,
2407                                    PangoRectangle *ink_rect,
2408                                    PangoRectangle *logical_rect,
2409                                    GSList        **line_extents)
2410 {
2411   GSList *line_list;
2412   int y_offset = 0;
2413   int width;
2414   gboolean need_width = FALSE;
2415
2416   g_return_if_fail (layout != NULL);
2417
2418   if (ink_rect && layout->ink_rect_cached)
2419     {
2420       *ink_rect = layout->ink_rect;
2421       ink_rect = NULL;
2422     }
2423   if (logical_rect && layout->logical_rect_cached)
2424     {
2425       *logical_rect = layout->logical_rect;
2426       logical_rect = NULL;
2427     }
2428   if (!ink_rect && !logical_rect && !line_extents)
2429     return;
2430
2431   pango_layout_check_lines (layout);
2432
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.
2436    */
2437   width = layout->width;
2438
2439   if (layout->auto_dir)
2440     {
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.
2444        */
2445       line_list = layout->lines;
2446       while (line_list && !need_width)
2447         {
2448           PangoLayoutLine *line = line_list->data;
2449
2450           if (get_alignment (layout, line) != PANGO_ALIGN_LEFT)
2451             need_width = TRUE;
2452
2453           line_list = line_list->next;
2454         }
2455     }
2456   else if (layout->alignment != PANGO_ALIGN_LEFT)
2457     need_width = TRUE;
2458
2459   if (width == -1 && need_width && (ink_rect || line_extents))
2460     {
2461       PangoRectangle overall_logical;
2462
2463       pango_layout_get_extents_internal (layout, NULL, &overall_logical, NULL);
2464       width = overall_logical.width;
2465     }
2466
2467   if (logical_rect)
2468     {
2469       logical_rect->x = 0;
2470       logical_rect->y = 0;
2471       logical_rect->width = 0;
2472       logical_rect->height = 0;
2473     }
2474
2475   line_list = layout->lines;
2476   while (line_list)
2477     {
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;
2482
2483       int new_pos;
2484
2485       /* This block gets the line extents in layout coords */
2486       {
2487         int baseline;
2488
2489         get_line_extents_layout_coords (layout, line,
2490                                         width, y_offset,
2491                                         &baseline,
2492                                         ink_rect ? &line_ink_layout : NULL,
2493                                         &line_logical_layout);
2494
2495         if (line_extents)
2496           {
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);
2502           }
2503       }
2504
2505       if (ink_rect)
2506         {
2507           /* Compute the union of the current ink_rect with
2508            * line_ink_layout
2509            */
2510
2511           if (line_list == layout->lines)
2512             {
2513               *ink_rect = line_ink_layout;
2514             }
2515           else
2516             {
2517               new_pos = MIN (ink_rect->x, line_ink_layout.x);
2518               ink_rect->width =
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;
2522
2523               new_pos = MIN (ink_rect->y, line_ink_layout.y);
2524               ink_rect->height =
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;
2528             }
2529         }
2530
2531       if (logical_rect)
2532         {
2533           if (layout->width == -1)
2534             {
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.
2537                */
2538               logical_rect->width = MAX (logical_rect->width, line_logical_layout.width);
2539             }
2540           else
2541             {
2542               /* When a width is set, we have to compute the union of the horizontal
2543                * extents of all the lines.
2544                */
2545               if (line_list == layout->lines)
2546                 {
2547                   logical_rect->x = line_logical_layout.x;
2548                   logical_rect->width = line_logical_layout.width;
2549                 }
2550               else
2551                 {
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;
2557
2558                 }
2559             }
2560
2561           logical_rect->height += line_logical_layout.height;
2562
2563           /* No space after the last line, of course. */
2564           if (line_list->next != NULL)
2565             logical_rect->height += layout->spacing;
2566         }
2567
2568       y_offset += line_logical_layout.height + layout->spacing;
2569       line_list = line_list->next;
2570     }
2571
2572   if (ink_rect)
2573     {
2574       layout->ink_rect = *ink_rect;
2575       layout->ink_rect_cached = TRUE;
2576     }
2577   if (logical_rect)
2578     {
2579       layout->logical_rect = *logical_rect;
2580       layout->logical_rect_cached = TRUE;
2581     }
2582   if (line_extents)
2583     *line_extents = g_slist_reverse (*line_extents);
2584 }
2585
2586 /**
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.
2593  *
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
2599  * a set width.
2600  *
2601  * The extents are given in layout coordinates and in Pango units; layout
2602  * coordinates begin at the top left corner of the layout.
2603  */
2604 void
2605 pango_layout_get_extents (PangoLayout    *layout,
2606                           PangoRectangle *ink_rect,
2607                           PangoRectangle *logical_rect)
2608 {
2609   g_return_if_fail (layout != NULL);
2610
2611   pango_layout_get_extents_internal (layout, ink_rect, logical_rect, NULL);
2612 }
2613
2614 /**
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.
2621  *
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()).
2627  **/
2628 void
2629 pango_layout_get_pixel_extents (PangoLayout *layout,
2630                                 PangoRectangle *ink_rect,
2631                                 PangoRectangle *logical_rect)
2632 {
2633   g_return_if_fail (PANGO_IS_LAYOUT (layout));
2634
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);
2638 }
2639
2640 /**
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
2645  *
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().
2649  **/
2650 void
2651 pango_layout_get_size (PangoLayout *layout,
2652                        int         *width,
2653                        int         *height)
2654 {
2655   PangoRectangle logical_rect;
2656
2657   pango_layout_get_extents (layout, NULL, &logical_rect);
2658
2659   if (width)
2660     *width = logical_rect.width;
2661   if (height)
2662     *height = logical_rect.height;
2663 }
2664
2665 /**
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
2670  *
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().
2676  **/
2677 void
2678 pango_layout_get_pixel_size (PangoLayout *layout,
2679                              int         *width,
2680                              int         *height)
2681 {
2682   PangoRectangle logical_rect;
2683
2684   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2685
2686   if (width)
2687     *width = logical_rect.width;
2688   if (height)
2689     *height = logical_rect.height;
2690 }
2691
2692 /**
2693  * pango_layout_get_baseline:
2694  * @layout: a #PangoLayout
2695  *
2696  * Gets the Y position of baseline of the first line in @layout.
2697  *
2698  * Return value: baseline of first line, from top of @layout.
2699  *
2700  * Since: 1.22
2701  **/
2702 int
2703 pango_layout_get_baseline (PangoLayout    *layout)
2704 {
2705   int baseline;
2706
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);
2711
2712   return baseline;
2713 }
2714
2715 static void
2716 pango_layout_clear_lines (PangoLayout *layout)
2717 {
2718   if (layout->lines)
2719     {
2720       GSList *tmp_list = layout->lines;
2721       while (tmp_list)
2722         {
2723           PangoLayoutLine *line = tmp_list->data;
2724           tmp_list = tmp_list->next;
2725
2726           line->layout = NULL;
2727           pango_layout_line_unref (line);
2728         }
2729
2730       g_slist_free (layout->lines);
2731       layout->lines = NULL;
2732       layout->line_count = 0;
2733
2734       /* This could be handled separately, since we don't need to
2735        * recompute log_attrs on a width change, but this is easiest
2736        */
2737       g_free (layout->log_attrs);
2738       layout->log_attrs = NULL;
2739     }
2740
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;
2746 }
2747
2748 static void
2749 pango_layout_line_leaked (PangoLayoutLine *line)
2750 {
2751   PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
2752
2753   private->cache_status = LEAKED;
2754
2755   if (line->layout)
2756     {
2757       line->layout->logical_rect_cached = FALSE;
2758       line->layout->ink_rect_cached = FALSE;
2759     }
2760 }
2761
2762 \f
2763 /*****************
2764  * Line Breaking *
2765  *****************/
2766
2767 static void shape_tab (PangoLayoutLine  *line,
2768                        PangoGlyphString *glyphs);
2769
2770 static void
2771 free_run (PangoLayoutRun *run, gpointer data)
2772 {
2773   gboolean free_item = data != NULL;
2774   if (free_item)
2775     pango_item_free (run->item);
2776
2777   pango_glyph_string_free (run->glyphs);
2778   g_slice_free (PangoLayoutRun, run);
2779 }
2780
2781 static void
2782 extents_free (Extents *ext, gpointer data G_GNUC_UNUSED)
2783 {
2784   g_slice_free (Extents, ext);
2785 }
2786
2787 static PangoItem *
2788 uninsert_run (PangoLayoutLine *line)
2789 {
2790   PangoLayoutRun *run;
2791   PangoItem *item;
2792
2793   GSList *tmp_node = line->runs;
2794
2795   run = tmp_node->data;
2796   item = run->item;
2797
2798   line->runs = tmp_node->next;
2799   line->length -= item->length;
2800
2801   g_slist_free_1 (tmp_node);
2802   free_run (run, (gpointer)FALSE);
2803
2804   return item;
2805 }
2806
2807 static void
2808 ensure_tab_width (PangoLayout *layout)
2809 {
2810   if (layout->tab_width == -1)
2811     {
2812       /* Find out how wide 8 spaces are in the context's default
2813        * font. Utter performance killer. :-(
2814        */
2815       PangoGlyphString *glyphs = pango_glyph_string_new ();
2816       PangoItem *item;
2817       GList *items;
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;
2824
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);
2828
2829       tmp_attrs = pango_attr_list_new ();
2830
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);
2834
2835       if (language)
2836         {
2837           attr = pango_attr_language_new (language);
2838           pango_attr_list_insert_before (tmp_attrs, attr);
2839         }
2840
2841       items = pango_itemize (layout->context, " ", 0, 1, tmp_attrs, NULL);
2842
2843       pango_attr_iterator_destroy (iter);
2844       if (layout_attrs != layout->attrs)
2845         {
2846           pango_attr_list_unref (layout_attrs);
2847           layout_attrs = NULL;
2848         }
2849       pango_attr_list_unref (tmp_attrs);
2850
2851       item = items->data;
2852       pango_shape ("        ", 8, &item->analysis, glyphs);
2853
2854       pango_item_free (item);
2855       g_list_free (items);
2856
2857       layout->tab_width = pango_glyph_string_get_width (glyphs);
2858
2859       pango_glyph_string_free (glyphs);
2860
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.
2864        */
2865       if (layout->tab_width <= 0)
2866         layout->tab_width = 50 * PANGO_SCALE; /* pretty much arbitrary */
2867     }
2868 }
2869
2870 /* For now we only need the tab position, we assume
2871  * all tabs are left-aligned.
2872  */
2873 static int
2874 get_tab_pos (PangoLayout *layout, int index, gboolean *is_default)
2875 {
2876   gint n_tabs;
2877   gboolean in_pixels;
2878
2879   if (layout->tabs)
2880     {
2881       n_tabs = pango_tab_array_get_size (layout->tabs);
2882       in_pixels = pango_tab_array_get_positions_in_pixels (layout->tabs);
2883       if (is_default)
2884         *is_default = FALSE;
2885     }
2886   else
2887     {
2888       n_tabs = 0;
2889       in_pixels = FALSE;
2890       if (is_default)
2891         *is_default = TRUE;
2892     }
2893
2894   if (index < n_tabs)
2895     {
2896       gint pos = 0;
2897
2898       pango_tab_array_get_tab (layout->tabs, index, NULL, &pos);
2899
2900       if (in_pixels)
2901         return pos * PANGO_SCALE;
2902       else
2903         return pos;
2904     }
2905
2906   if (n_tabs > 0)
2907     {
2908       /* Extrapolate tab position, repeating the last tab gap to
2909        * infinity.
2910        */
2911       int last_pos = 0;
2912       int next_to_last_pos = 0;
2913       int tab_width;
2914
2915       pango_tab_array_get_tab (layout->tabs, n_tabs - 1, NULL, &last_pos);
2916
2917       if (n_tabs > 1)
2918         pango_tab_array_get_tab (layout->tabs, n_tabs - 2, NULL, &next_to_last_pos);
2919       else
2920         next_to_last_pos = 0;
2921
2922       if (in_pixels)
2923         {
2924           next_to_last_pos *= PANGO_SCALE;
2925           last_pos *= PANGO_SCALE;
2926         }
2927
2928       if (last_pos > next_to_last_pos)
2929         {
2930           tab_width = last_pos - next_to_last_pos;
2931         }
2932       else
2933         {
2934           tab_width = layout->tab_width;
2935         }
2936
2937       return last_pos + tab_width * (index - n_tabs + 1);
2938     }
2939   else
2940     {
2941       /* No tab array set, so use default tab width
2942        */
2943       return layout->tab_width * index;
2944     }
2945 }
2946
2947 static int
2948 line_width (PangoLayoutLine *line)
2949 {
2950   GSList *l;
2951   int i;
2952   int width = 0;
2953
2954   /* Compute the width of the line currently - inefficient, but easier
2955    * than keeping the current width of the line up to date everywhere
2956    */
2957   for (l = line->runs; l; l = l->next)
2958     {
2959       PangoLayoutRun *run = l->data;
2960
2961       for (i=0; i < run->glyphs->num_glyphs; i++)
2962         width += run->glyphs->glyphs[i].geometry.width;
2963     }
2964
2965   return width;
2966 }
2967
2968 static void
2969 shape_tab (PangoLayoutLine  *line,
2970            PangoGlyphString *glyphs)
2971 {
2972   int i, space_width;
2973
2974   int current_width = line_width (line);
2975
2976   pango_glyph_string_set_size (glyphs, 1);
2977
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;
2982
2983   glyphs->log_clusters[0] = 0;
2984
2985   ensure_tab_width (line->layout);
2986   space_width = line->layout->tab_width / 8;
2987
2988   for (i=0;;i++)
2989     {
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
2996        * the pixel.
2997        */
2998       if (tab_pos >= current_width + (is_default ? space_width : 1))
2999         {
3000           glyphs->glyphs[0].geometry.width = tab_pos - current_width;
3001           break;
3002         }
3003     }
3004 }
3005
3006 static inline gboolean
3007 can_break_at (PangoLayout *layout,
3008               gint         offset,
3009               gboolean     always_wrap_char)
3010 {
3011   PangoWrapMode wrap;
3012   /* We probably should have a mode where we treat all white-space as
3013    * of fungible width - appropriate for typography but not for
3014    * editing.
3015    */
3016   wrap = layout->wrap;
3017
3018   if (wrap == PANGO_WRAP_WORD_CHAR)
3019     wrap = always_wrap_char ? PANGO_WRAP_CHAR : PANGO_WRAP_WORD;
3020
3021   if (offset == layout->n_chars)
3022     return TRUE;
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;
3027   else
3028     {
3029       g_warning (G_STRLOC": broken PangoLayout");
3030       return TRUE;
3031     }
3032 }
3033
3034 static inline gboolean
3035 can_break_in (PangoLayout *layout,
3036               int          start_offset,
3037               int          num_chars,
3038               gboolean     allow_break_at_start)
3039 {
3040   int i;
3041
3042   for (i = allow_break_at_start ? 0 : 1; i < num_chars; i++)
3043     if (can_break_at (layout, start_offset + i, FALSE))
3044       return TRUE;
3045
3046   return FALSE;
3047 }
3048
3049 static inline void
3050 distribute_letter_spacing (int  letter_spacing,
3051                            int *space_left,
3052                            int *space_right)
3053 {
3054   *space_left = letter_spacing / 2;
3055   /* hinting */
3056   if ((letter_spacing & (PANGO_SCALE - 1)) == 0)
3057     {
3058       *space_left = PANGO_UNITS_ROUND (*space_left);
3059     }
3060   *space_right = letter_spacing - *space_left;
3061 }
3062
3063 typedef enum
3064 {
3065   BREAK_NONE_FIT,
3066   BREAK_SOME_FIT,
3067   BREAK_ALL_FIT,
3068   BREAK_EMPTY_FIT,
3069   BREAK_LINE_SEPARATOR
3070 } BreakResult;
3071
3072 struct _ParaBreakState
3073 {
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 */
3077
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 */
3083
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 */
3090
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 */
3093
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 */
3097 };
3098
3099 static gboolean
3100 should_ellipsize_current_line (PangoLayout    *layout,
3101                                ParaBreakState *state);
3102
3103 static PangoGlyphString *
3104 shape_run (PangoLayoutLine *line,
3105            ParaBreakState  *state,
3106            PangoItem       *item)
3107 {
3108   PangoLayout *layout = line->layout;
3109   PangoGlyphString *glyphs = pango_glyph_string_new ();
3110
3111   if (layout->text[item->offset] == '\t')
3112     shape_tab (line, glyphs);
3113   else
3114     {
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,
3118                             glyphs);
3119       else
3120         pango_shape (layout->text + item->offset, item->length, &item->analysis, glyphs);
3121
3122       if (state->properties.letter_spacing)
3123         {
3124           PangoGlyphItem glyph_item;
3125           int space_left, space_right;
3126
3127           glyph_item.item = item;
3128           glyph_item.glyphs = glyphs;
3129
3130           pango_glyph_item_letter_space (&glyph_item,
3131                                          layout->text,
3132                                          layout->log_attrs + state->start_offset,
3133                                          state->properties.letter_spacing);
3134
3135           distribute_letter_spacing (state->properties.letter_spacing, &space_left, &space_right);
3136
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;
3140         }
3141     }
3142
3143   return glyphs;
3144 }
3145
3146 static void
3147 insert_run (PangoLayoutLine *line,
3148             ParaBreakState  *state,
3149             PangoItem       *run_item,
3150             gboolean         last_run)
3151 {
3152   PangoLayoutRun *run = g_slice_new (PangoLayoutRun);
3153
3154   run->item = run_item;
3155
3156   if (last_run && state->log_widths_offset == 0)
3157     run->glyphs = state->glyphs;
3158   else
3159     run->glyphs = shape_run (line, state, run_item);
3160
3161   if (last_run)
3162     {
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;
3168     }
3169
3170   line->runs = g_slist_prepend (line->runs, run);
3171   line->length += run_item->length;
3172 }
3173
3174 #if 0
3175 # define DEBUG debug
3176 void
3177 debug (const char *where, PangoLayoutLine *line, ParaBreakState *state)
3178 {
3179   int line_width = pango_layout_line_get_width (line);
3180
3181   g_debug ("rem %d + line %d = %d               %s",
3182            state->remaining_width,
3183            line_width,
3184            state->remaining_width + line_width,
3185            where);
3186 }
3187 #else
3188 # define DEBUG(where, line, state) do { } while (0)
3189 #endif
3190
3191 /* Tries to insert as much as possible of the item at the head of
3192  * state->items onto @line. Five results are possible:
3193  *
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.
3199  *
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.
3204  *
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.
3209  */
3210 static BreakResult
3211 process_item (PangoLayout     *layout,
3212               PangoLayoutLine *line,
3213               ParaBreakState  *state,
3214               gboolean         force_fit,
3215               gboolean         no_break_at_end)
3216 {
3217   PangoItem *item = state->items->data;
3218   gboolean shape_set = FALSE;
3219   int width;
3220   int length;
3221   int i;
3222   gboolean processing_new_item = FALSE;
3223
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
3227
3228   if (!state->glyphs)
3229     {
3230       pango_layout_get_item_properties (item, &state->properties);
3231       state->glyphs = shape_run (line, state, item);
3232
3233       state->log_widths = NULL;
3234       state->log_widths_offset = 0;
3235
3236       processing_new_item = TRUE;
3237     }
3238
3239   if (!layout->single_paragraph &&
3240       g_utf8_get_char (layout->text + item->offset) == LINE_SEPARATOR &&
3241       !should_ellipsize_current_line (layout, state))
3242     {
3243       insert_run (line, state, item, TRUE);
3244       state->log_widths_offset += item->num_chars;
3245       return BREAK_LINE_SEPARATOR;
3246     }
3247
3248   if (state->remaining_width < 0 && !no_break_at_end)  /* Wrapping off */
3249     {
3250       insert_run (line, state, item, TRUE);
3251
3252       return BREAK_ALL_FIT;
3253     }
3254
3255   width = 0;
3256   if (processing_new_item)
3257     {
3258       width = pango_glyph_string_get_width (state->glyphs);
3259     }
3260   else
3261     {
3262       for (i = 0; i < item->num_chars; i++)
3263         width += state->log_widths[state->log_widths_offset + i];
3264     }
3265
3266   if ((width <= state->remaining_width || (item->num_chars == 1 && !line->runs)) &&
3267       !no_break_at_end)
3268     {
3269       state->remaining_width -= width;
3270       state->remaining_width = MAX (state->remaining_width, 0);
3271       insert_run (line, state, item, TRUE);
3272
3273       return BREAK_ALL_FIT;
3274     }
3275   else
3276     {
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;
3282
3283       if (processing_new_item)
3284         {
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);
3288         }
3289
3290     retry_break:
3291
3292       /* See how much of the item we can stuff in the line
3293        */
3294       width = 0;
3295       for (num_chars = 0; num_chars < item->num_chars; num_chars++)
3296         {
3297           if (width > state->remaining_width && break_num_chars < item->num_chars)
3298             break;
3299
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))
3303             {
3304               break_num_chars = num_chars;
3305               break_width = width;
3306             }
3307
3308           width += state->log_widths[state->log_widths_offset + num_chars];
3309         }
3310
3311       if (layout->wrap == PANGO_WRAP_WORD_CHAR && force_fit && break_width > state->remaining_width && !retrying_with_char_breaks)
3312         {
3313           retrying_with_char_breaks = TRUE;
3314           num_chars = item->num_chars;
3315           width = orig_width;
3316           break_num_chars = num_chars;
3317           break_width = width;
3318           goto retry_break;
3319         }
3320
3321       if (force_fit || break_width <= state->remaining_width)   /* Successfully broke the item */
3322         {
3323           if (state->remaining_width >= 0)
3324             {
3325               state->remaining_width -= break_width;
3326               state->remaining_width = MAX (state->remaining_width, 0);
3327             }
3328
3329           if (break_num_chars == item->num_chars)
3330             {
3331               insert_run (line, state, item, TRUE);
3332
3333               return BREAK_ALL_FIT;
3334             }
3335           else if (break_num_chars == 0)
3336             {
3337               return BREAK_EMPTY_FIT;
3338             }
3339           else
3340             {
3341               PangoItem *new_item;
3342
3343               length = g_utf8_offset_to_pointer (layout->text + item->offset, break_num_chars) - (layout->text + item->offset);
3344
3345               new_item = pango_item_split (item, length, break_num_chars);
3346               
3347               /* reshaping may slightly change the item width.  update
3348                * remaining_width if we are justifying */
3349
3350               state->remaining_width += break_width;
3351
3352               insert_run (line, state, new_item, FALSE);
3353
3354               if (layout->justify)
3355                 break_width = pango_glyph_string_get_width (((PangoGlyphItem *)(line->runs->data))->glyphs);
3356
3357               state->remaining_width -= break_width;
3358
3359               state->log_widths_offset += break_num_chars;
3360
3361               /* Shaped items should never be broken */
3362               g_assert (!shape_set);
3363
3364               return BREAK_SOME_FIT;
3365             }
3366         }
3367       else
3368         {
3369           pango_glyph_string_free (state->glyphs);
3370           state->glyphs = NULL;
3371           g_free (state->log_widths);
3372           state->log_widths = NULL;
3373
3374           return BREAK_NONE_FIT;
3375         }
3376     }
3377 }
3378
3379 /* The resolved direction for the line is always one
3380  * of LTR/RTL; not a week or neutral directions
3381  */
3382 static void
3383 line_set_resolved_dir (PangoLayoutLine *line,
3384                        PangoDirection   direction)
3385 {
3386   switch (direction)
3387     {
3388     default:
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;
3394       break;
3395     case PANGO_DIRECTION_RTL:
3396     case PANGO_DIRECTION_WEAK_RTL:
3397     case PANGO_DIRECTION_TTB_LTR:
3398       line->resolved_dir = PANGO_DIRECTION_RTL;
3399       break;
3400     }
3401
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.
3411    *
3412    * A similar dance is performed in pango-context.c:
3413    * itemize_state_add_character().  Keep in synch.
3414    */
3415   switch (pango_context_get_gravity (line->layout->context))
3416     {
3417     default:
3418     case PANGO_GRAVITY_AUTO:
3419     case PANGO_GRAVITY_SOUTH:
3420       break;
3421     case PANGO_GRAVITY_NORTH:
3422       line->resolved_dir = PANGO_DIRECTION_LTR
3423                          + PANGO_DIRECTION_RTL
3424                          - line->resolved_dir;
3425       break;
3426     case PANGO_GRAVITY_EAST:
3427       /* This is in fact why deprecated TTB_RTL is LTR */
3428       line->resolved_dir = PANGO_DIRECTION_LTR;
3429       break;
3430     case PANGO_GRAVITY_WEST:
3431       /* This is in fact why deprecated TTB_LTR is RTL */
3432       line->resolved_dir = PANGO_DIRECTION_RTL;
3433       break;
3434     }
3435 }
3436
3437 static gboolean
3438 should_ellipsize_current_line (PangoLayout    *layout, 
3439                                ParaBreakState *state)
3440 {
3441   if (G_LIKELY (layout->ellipsize == PANGO_ELLIPSIZE_NONE || layout->width < 0))
3442     return FALSE;
3443   
3444
3445   if (layout->height >= 0)
3446     {
3447       /* state->remaining_height is height of layout left */
3448
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;
3452     }
3453   else
3454     {
3455       /* -layout->height is number of lines per paragraph to show */
3456       return state->line_of_par == - layout->height;
3457     }
3458 }
3459
3460 static void
3461 add_line (PangoLayoutLine *line,
3462           ParaBreakState  *state)
3463 {
3464   PangoLayout *layout = line->layout;
3465
3466   /* we prepend, then reverse the list later */
3467   layout->lines = g_slist_prepend (layout->lines, line);
3468   layout->line_count++;
3469
3470   if (layout->height >= 0)
3471     {
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;
3476     }
3477 }
3478
3479 static void
3480 process_line (PangoLayout    *layout,
3481               ParaBreakState *state)
3482 {
3483   PangoLayoutLine *line;
3484
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 */
3490
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);
3495
3496   state->line_width = layout->width;
3497   if (state->line_width >= 0 && layout->alignment != PANGO_ALIGN_CENTER)
3498     {
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;
3503
3504       if (state->line_width < 0)
3505         state->line_width = 0;
3506     }
3507
3508   if (G_UNLIKELY (should_ellipsize_current_line (layout, state)))
3509     state->remaining_width = -1;
3510   else
3511     state->remaining_width = state->line_width;
3512   DEBUG ("starting to fill line", line, state);
3513
3514   while (state->items)
3515     {
3516       PangoItem *item = state->items->data;
3517       BreakResult result;
3518       int old_num_chars;
3519       int old_remaining_width;
3520       gboolean first_item_in_line;
3521
3522       old_num_chars = item->num_chars;
3523       old_remaining_width = state->remaining_width;
3524       first_item_in_line = line->runs != NULL;
3525
3526       result = process_item (layout, line, state, !have_break, FALSE);
3527
3528       switch (result)
3529         {
3530         case BREAK_ALL_FIT:
3531           if (can_break_in (layout, state->start_offset, old_num_chars, first_item_in_line))
3532             {
3533               have_break = TRUE;
3534               break_remaining_width = old_remaining_width;
3535               break_start_offset = state->start_offset;
3536               break_link = line->runs->next;
3537             }
3538
3539           state->items = g_list_delete_link (state->items, state->items);
3540           state->start_offset += old_num_chars;
3541
3542           break;
3543
3544         case BREAK_EMPTY_FIT:
3545           wrapped = TRUE;
3546           goto done;
3547
3548         case BREAK_SOME_FIT:
3549           state->start_offset += old_num_chars - item->num_chars;
3550           wrapped = TRUE;
3551           goto done;
3552
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));
3557
3558           state->start_offset = break_start_offset;
3559           state->remaining_width = break_remaining_width;
3560
3561           /* Reshape run to break */
3562           item = state->items->data;
3563
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);
3567
3568           state->start_offset += old_num_chars - item->num_chars;
3569
3570           wrapped = TRUE;
3571           goto done;
3572
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
3577            * justification */
3578           wrapped = TRUE;
3579           goto done;
3580         }
3581     }
3582
3583  done:
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;
3589 }
3590
3591 static void
3592 get_items_log_attrs (const char   *text,
3593                      GList        *items,
3594                      PangoLogAttr *log_attrs,
3595                      int           para_delimiter_len)
3596 {
3597   int offset = 0;
3598   int index = 0;
3599
3600   while (items)
3601     {
3602       PangoItem tmp_item = *(PangoItem *)items->data;
3603
3604       /* Accumulate all the consecutive items that match in language
3605        * characteristics, ignoring font, style tags, etc.
3606        */
3607       while (items->next)
3608         {
3609           PangoItem *next_item = items->next->data;
3610
3611           /* FIXME: Handle language tags */
3612           if (next_item->analysis.lang_engine != tmp_item.analysis.lang_engine)
3613             break;
3614           else
3615             {
3616               tmp_item.length += next_item->length;
3617               tmp_item.num_chars += next_item->num_chars;
3618             }
3619
3620           items = items->next;
3621         }
3622
3623       /* Break the paragraph delimiters with the last item */
3624       if (items->next == NULL)
3625         {
3626           tmp_item.num_chars += pango_utf8_strlen (text + index + tmp_item.length, para_delimiter_len);
3627           tmp_item.length += para_delimiter_len;
3628         }
3629
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.
3633        */
3634       pango_break (text + index, tmp_item.length, &tmp_item.analysis,
3635                    log_attrs + offset, tmp_item.num_chars + 1);
3636
3637       offset += tmp_item.num_chars;
3638       index += tmp_item.length;
3639
3640       items = items->next;
3641     }
3642 }
3643
3644 static PangoAttrList *
3645 pango_layout_get_effective_attributes (PangoLayout *layout)
3646 {
3647   PangoAttrList *attrs;
3648
3649   if (layout->attrs)
3650     attrs = pango_attr_list_copy (layout->attrs);
3651   else
3652     attrs = pango_attr_list_new ();
3653
3654   if (layout->font_desc)
3655     {
3656       PangoAttribute *attr = pango_attr_font_desc_new (layout->font_desc);
3657       pango_attr_list_insert_before (attrs, attr);
3658     }
3659
3660   return attrs;
3661 }
3662
3663 static gboolean
3664 no_shape_filter_func (PangoAttribute *attribute,
3665                       gpointer        data G_GNUC_UNUSED)
3666 {
3667   static const PangoAttrType no_shape_types[] = {
3668     PANGO_ATTR_FOREGROUND,
3669     PANGO_ATTR_BACKGROUND,
3670     PANGO_ATTR_UNDERLINE,
3671     PANGO_ATTR_STRIKETHROUGH,
3672     PANGO_ATTR_RISE
3673   };
3674
3675   int i;
3676
3677   for (i = 0; i < (int)G_N_ELEMENTS (no_shape_types); i++)
3678     if (attribute->klass->type == no_shape_types[i])
3679       return TRUE;
3680
3681   return FALSE;
3682 }
3683
3684 static PangoAttrList *
3685 filter_no_shape_attributes (PangoAttrList *attrs)
3686 {
3687   return pango_attr_list_filter (attrs,
3688                                  no_shape_filter_func,
3689                                  NULL);
3690 }
3691
3692 static void
3693 apply_no_shape_attributes (PangoLayout   *layout,
3694                            PangoAttrList *no_shape_attrs)
3695 {
3696   GSList *line_list;
3697
3698   for (line_list = layout->lines; line_list; line_list = line_list->next)
3699     {
3700       PangoLayoutLine *line = line_list->data;
3701       GSList *old_runs = g_slist_reverse (line->runs);
3702       GSList *run_list;
3703
3704       line->runs = NULL;
3705       for (run_list = old_runs; run_list; run_list = run_list->next)
3706         {
3707           PangoGlyphItem *glyph_item = run_list->data;
3708           GSList *new_runs;
3709
3710           new_runs = pango_glyph_item_apply_attrs (glyph_item,
3711                                                    layout->text,
3712                                                    no_shape_attrs);
3713
3714           line->runs = g_slist_concat (new_runs, line->runs);
3715         }
3716
3717       g_slist_free (old_runs);
3718     }
3719 }
3720
3721 static void
3722 pango_layout_check_lines (PangoLayout *layout)
3723 {
3724   const char *start;
3725   gboolean done = FALSE;
3726   int start_offset;
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;
3732
3733   if (G_LIKELY (layout->lines))
3734     return;
3735
3736   g_assert (!layout->log_attrs);
3737
3738   /* For simplicity, we make sure at this point that layout->text
3739    * is non-NULL even if it is zero length
3740    */
3741   if (G_UNLIKELY (!layout->text))
3742     pango_layout_set_text (layout, NULL, 0);
3743
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);
3747
3748   layout->log_attrs = g_new (PangoLogAttr, layout->n_chars + 1);
3749
3750   start_offset = 0;
3751   start = layout->text;
3752
3753   /* Find the first strong direction of the text */
3754   if (layout->auto_dir)
3755     {
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);
3759     }
3760   else
3761     base_dir = pango_context_get_base_dir (layout->context);
3762
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)
3767     {
3768       PangoRectangle logical;
3769       pango_layout_get_empty_extents_at_index (layout, 0, &logical);
3770       state.line_height = logical.height;
3771     }
3772
3773   do
3774     {
3775       int delim_len;
3776       const char *end;
3777       int delimiter_index, next_para_index;
3778
3779       if (layout->single_paragraph)
3780         {
3781           delimiter_index = layout->length;
3782           next_para_index = layout->length;
3783         }
3784       else
3785         {
3786           pango_find_paragraph_boundary (start,
3787                                          (layout->text + layout->length) - start,
3788                                          &delimiter_index,
3789                                          &next_para_index);
3790         }
3791
3792       g_assert (next_para_index >= delimiter_index);
3793
3794       if (layout->auto_dir)
3795         {
3796           base_dir = pango_find_base_dir (start, delimiter_index);
3797
3798           /* Propagate the base direction for neutral paragraphs */
3799           if (base_dir == PANGO_DIRECTION_NEUTRAL)
3800             base_dir = prev_base_dir;
3801           else
3802             prev_base_dir = base_dir;
3803         }
3804
3805       end = start + delimiter_index;
3806
3807       delim_len = next_para_index - delimiter_index;
3808
3809       if (end == (layout->text + layout->length))
3810         done = TRUE;
3811
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);
3816
3817       state.attrs = attrs;
3818       state.items = pango_itemize_with_base_dir (layout->context,
3819                                                  base_dir,
3820                                                  layout->text,
3821                                                  start - layout->text,
3822                                                  end - start,
3823                                                  attrs,
3824                                                  iter);
3825
3826       get_items_log_attrs (start, state.items,
3827                            layout->log_attrs + start_offset,
3828                            delim_len);
3829
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;
3835
3836       state.glyphs = NULL;
3837       state.log_widths = NULL;
3838
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;
3843
3844       if (state.items)
3845         {
3846           while (state.items)
3847             process_line (layout, &state);
3848         }
3849       else
3850         {
3851           PangoLayoutLine *empty_line;
3852
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);
3857
3858           add_line (empty_line, &state);
3859         }
3860
3861       if (layout->height >= 0 && state.remaining_height < state.line_height)
3862         done = TRUE;
3863
3864       if (!done)
3865         start_offset += pango_utf8_strlen (start, (end - start) + delim_len);
3866
3867       start = end + delim_len;
3868     }
3869   while (!done);
3870
3871   pango_attr_iterator_destroy (iter);
3872   pango_attr_list_unref (attrs);
3873
3874   if (no_shape_attrs)
3875     {
3876       apply_no_shape_attributes (layout, no_shape_attrs);
3877       pango_attr_list_unref (no_shape_attrs);
3878     }
3879
3880   layout->lines = g_slist_reverse (layout->lines);
3881 }
3882
3883 /**
3884  * pango_layout_line_ref:
3885  * @line: a #PangoLayoutLine, may be %NULL
3886  *
3887  * Increase the reference count of a #PangoLayoutLine by one.
3888  *
3889  * Return value: the line passed in.
3890  *
3891  * Since: 1.10
3892  **/
3893 PangoLayoutLine *
3894 pango_layout_line_ref (PangoLayoutLine *line)
3895 {
3896   PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
3897
3898   if (line == NULL)
3899     return NULL;
3900
3901   g_atomic_int_inc ((int *) &private->ref_count);
3902
3903   return line;
3904 }
3905
3906 /**
3907  * pango_layout_line_unref:
3908  * @line: a #PangoLayoutLine
3909  *
3910  * Decrease the reference count of a #PangoLayoutLine by one.
3911  * If the result is zero, the line and all associated memory
3912  * will be freed.
3913  **/
3914 void
3915 pango_layout_line_unref (PangoLayoutLine *line)
3916 {
3917   PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
3918
3919   if (line == NULL)
3920     return;
3921
3922   g_return_if_fail (private->ref_count > 0);
3923
3924   if (g_atomic_int_dec_and_test ((int *) &private->ref_count))
3925     {
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);
3929     }
3930 }
3931
3932 GType
3933 pango_layout_line_get_type (void)
3934 {
3935   static GType our_type = 0;
3936
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);
3941   return our_type;
3942 }
3943
3944 /**
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.
3955  *
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.
3966  *
3967  * Return value: %FALSE if @x_pos was outside the line, %TRUE if inside
3968  **/
3969 gboolean
3970 pango_layout_line_x_to_index (PangoLayoutLine *line,
3971                               int              x_pos,
3972                               int             *index,
3973                               int             *trailing)
3974 {
3975   GSList *tmp_list;
3976   gint start_pos = 0;
3977   gint first_index = 0; /* line->start_index */
3978   gint first_offset;
3979   gint last_index;      /* start of last grapheme in line */
3980   gint last_offset;
3981   gint end_index;       /* end iterator for line */
3982   gint end_offset;      /* end iterator for line */
3983   PangoLayout *layout;
3984   gint last_trailing;
3985   gboolean suppress_last_trailing;
3986
3987   g_return_val_if_fail (line != NULL, FALSE);
3988   g_return_val_if_fail (LINE_IS_VALID (line), FALSE);
3989
3990   if (!LINE_IS_VALID (line))
3991     return FALSE;
3992
3993   layout = line->layout;
3994
3995   /* Find the last index in the line
3996    */
3997   first_index = line->start_index;
3998
3999   if (line->length == 0)
4000     {
4001       if (index)
4002         *index = first_index;
4003       if (trailing)
4004         *trailing = 0;
4005
4006       return FALSE;
4007     }
4008
4009   g_assert (line->length > 0);
4010
4011   first_offset = g_utf8_pointer_to_offset (layout->text, layout->text + line->start_index);
4012
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);
4015
4016   last_index = end_index;
4017   last_offset = end_offset;
4018   last_trailing = 0;
4019   do
4020     {
4021       last_index = g_utf8_prev_char (layout->text + last_index) - layout->text;
4022       last_offset--;
4023       last_trailing++;
4024     }
4025   while (last_offset > first_offset && !layout->log_attrs[last_offset].is_cursor_position);
4026
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
4031    * to zero.
4032    *
4033    * That is, if the text is "now is the time", and is broken between
4034    * 'now' and 'is'
4035    *
4036    * Then when the cursor is actually at:
4037    *
4038    * n|o|w| |i|s|
4039    *              ^
4040    * we lie and say it is at:
4041    *
4042    * n|o|w| |i|s|
4043    *            ^
4044    *
4045    * So the cursor won't appear on the next line before 'the'.
4046    *
4047    * Actually, any program keeping cursor
4048    * positions with wrapped lines should distinguish leading and
4049    * trailing cursors.
4050    */
4051   tmp_list = layout->lines;
4052   while (tmp_list->data != line)
4053     tmp_list = tmp_list->next;
4054
4055   if (tmp_list->next &&
4056       line->start_index + line->length == ((PangoLayoutLine *)tmp_list->next->data)->start_index)
4057     suppress_last_trailing = TRUE;
4058   else
4059     suppress_last_trailing = FALSE;
4060
4061   if (x_pos < 0)
4062     {
4063       /* pick the leftmost char */
4064       if (index)
4065         *index = (line->resolved_dir == PANGO_DIRECTION_LTR) ? first_index : last_index;
4066       /* and its leftmost edge */
4067       if (trailing)
4068         *trailing = (line->resolved_dir == PANGO_DIRECTION_LTR || suppress_last_trailing) ? 0 : last_trailing;
4069
4070       return FALSE;
4071     }
4072
4073   tmp_list = line->runs;
4074   while (tmp_list)
4075     {
4076       PangoLayoutRun *run = tmp_list->data;
4077       ItemProperties properties;
4078       int logical_width;
4079
4080       pango_layout_get_item_properties (run->item, &properties);
4081
4082       logical_width = pango_glyph_string_get_width (run->glyphs);
4083
4084       if (x_pos >= start_pos && x_pos < start_pos + logical_width)
4085         {
4086           int offset;
4087           gboolean char_trailing;
4088           int grapheme_start_index;
4089           int grapheme_start_offset;
4090           int grapheme_end_offset;
4091           int pos;
4092           int char_index;
4093
4094           pango_glyph_string_x_to_index (run->glyphs,
4095                                          layout->text + run->item->offset, run->item->length,
4096                                          &run->item->analysis,
4097                                          x_pos - start_pos,
4098                                          &pos, &char_trailing);
4099
4100           char_index = run->item->offset + pos;
4101
4102           /* Convert from characters to graphemes */
4103
4104           offset = g_utf8_pointer_to_offset (layout->text, layout->text + char_index);
4105
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)
4110             {
4111               grapheme_start_index = g_utf8_prev_char (layout->text + grapheme_start_index) - layout->text;
4112               grapheme_start_offset--;
4113             }
4114
4115           grapheme_end_offset = offset;
4116           do
4117             {
4118               grapheme_end_offset++;
4119             }
4120           while (grapheme_end_offset < end_offset &&
4121                  !layout->log_attrs[grapheme_end_offset].is_cursor_position);
4122
4123           if (index)
4124             *index = grapheme_start_index;
4125
4126           if (trailing)
4127             {
4128               if ((grapheme_end_offset == end_offset && suppress_last_trailing) ||
4129                   offset + char_trailing <= (grapheme_start_offset + grapheme_end_offset) / 2)
4130                 *trailing = 0;
4131               else
4132                 *trailing = grapheme_end_offset - grapheme_start_offset;
4133             }
4134
4135           return TRUE;
4136         }
4137
4138       start_pos += logical_width;
4139       tmp_list = tmp_list->next;
4140     }
4141
4142   /* pick the rightmost char */
4143   if (index)
4144     *index = (line->resolved_dir == PANGO_DIRECTION_LTR) ? last_index : first_index;
4145
4146   /* and its rightmost edge */
4147   if (trailing)
4148     *trailing = (line->resolved_dir == PANGO_DIRECTION_LTR && !suppress_last_trailing) ? last_trailing : 0;
4149
4150   return FALSE;
4151 }
4152
4153 static int
4154 pango_layout_line_get_width (PangoLayoutLine *line)
4155 {
4156   int width = 0;
4157   GSList *tmp_list = line->runs;
4158
4159   while (tmp_list)
4160     {
4161       PangoLayoutRun *run = tmp_list->data;
4162
4163       width += pango_glyph_string_get_width (run->glyphs);
4164
4165       tmp_list = tmp_list->next;
4166     }
4167
4168   return width;
4169 }
4170
4171 /**
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.
4192  *
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.
4198  **/
4199 void
4200 pango_layout_line_get_x_ranges (PangoLayoutLine  *line,
4201                                 int               start_index,
4202                                 int               end_index,
4203                                 int             **ranges,
4204                                 int              *n_ranges)
4205 {
4206   gint line_start_index = 0;
4207   GSList *tmp_list;
4208   int range_count = 0;
4209   int accumulated_width = 0;
4210   int x_offset;
4211   int width, line_width;
4212   PangoAlignment alignment;
4213
4214   g_return_if_fail (line != NULL);
4215   g_return_if_fail (line->layout != NULL);
4216   g_return_if_fail (start_index <= end_index);
4217
4218   alignment = get_alignment (line->layout, line);
4219
4220   width = line->layout->width;
4221   if (width == -1 && alignment != PANGO_ALIGN_LEFT)
4222     {
4223       PangoRectangle logical_rect;
4224       pango_layout_get_extents (line->layout, NULL, &logical_rect);
4225       width = logical_rect.width;
4226     }
4227
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.
4231    */
4232
4233   {
4234     PangoRectangle logical_rect;
4235     pango_layout_line_get_extents (line, NULL, &logical_rect);
4236     line_width = logical_rect.width;
4237   }
4238
4239   get_x_offset (line->layout, line, width, line_width, &x_offset);
4240
4241   line_start_index = line->start_index;
4242
4243   /* Allocate the maximum possible size */
4244   if (ranges)
4245     *ranges = g_new (int, 2 * (2 + g_slist_length (line->runs)));
4246
4247   if (x_offset > 0 &&
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)))
4250     {
4251       if (ranges)
4252         {
4253           (*ranges)[2*range_count] = 0;
4254           (*ranges)[2*range_count + 1] = x_offset;
4255         }
4256
4257       range_count ++;
4258     }
4259
4260   tmp_list = line->runs;
4261   while (tmp_list)
4262     {
4263       PangoLayoutRun *run = (PangoLayoutRun *)tmp_list->data;
4264
4265       if ((start_index < run->item->offset + run->item->length &&
4266            end_index > run->item->offset))
4267         {
4268           if (ranges)
4269             {
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;
4273
4274               g_assert (run_end_index > 0);
4275
4276               /* Back the end_index off one since we want to find the trailing edge of the preceding character */
4277
4278               run_end_index = g_utf8_prev_char (line->layout->text + run_end_index) - line->layout->text;
4279
4280               pango_glyph_string_index_to_x (run->glyphs,
4281                                              line->layout->text + run->item->offset,
4282                                              run->item->length,
4283                                              &run->item->analysis,
4284                                              run_start_index - run->item->offset, FALSE,
4285                                              &run_start_x);
4286               pango_glyph_string_index_to_x (run->glyphs,
4287                                              line->layout->text + run->item->offset,
4288                                              run->item->length,
4289                                              &run->item->analysis,
4290                                              run_end_index - run->item->offset, TRUE,
4291                                              &run_end_x);
4292
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);
4295             }
4296
4297           range_count++;
4298         }
4299
4300       if (tmp_list->next)
4301         accumulated_width += pango_glyph_string_get_width (run->glyphs);
4302
4303       tmp_list = tmp_list->next;
4304     }
4305
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)))
4309     {
4310       if (ranges)
4311         {
4312           (*ranges)[2*range_count] = x_offset + line_width;
4313           (*ranges)[2*range_count + 1] = line->layout->width;
4314         }
4315
4316       range_count ++;
4317     }
4318
4319   if (n_ranges)
4320     *n_ranges = range_count;
4321 }
4322
4323 static void
4324 pango_layout_get_empty_extents_at_index (PangoLayout    *layout,
4325                                          int             index,
4326                                          PangoRectangle *logical_rect)
4327 {
4328   if (logical_rect)
4329     {
4330       PangoFont *font;
4331       PangoFontDescription *font_desc = NULL;
4332       gboolean free_font_desc = FALSE;
4333
4334       font_desc = pango_context_get_font_description (layout->context);
4335
4336       if (layout->font_desc)
4337         {
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;
4341         }
4342
4343       /* Find the font description for this line
4344        */
4345       if (layout->attrs)
4346         {
4347           PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->attrs);
4348           int start, end;
4349
4350           do
4351             {
4352               pango_attr_iterator_range (iter, &start, &end);
4353
4354               if (start <= index && index < end)
4355                 {
4356                   PangoFontDescription *base_font_desc;
4357
4358                   if (layout->font_desc)
4359                     base_font_desc = layout->font_desc;
4360                   else
4361                     base_font_desc = pango_context_get_font_description (layout->context);
4362
4363                   if (!free_font_desc)
4364                     {
4365                       font_desc = pango_font_description_copy_static (font_desc);
4366                       free_font_desc = TRUE;
4367                     }
4368
4369                   pango_attr_iterator_get_font (iter,
4370                                                 font_desc,
4371                                                 NULL,
4372                                                 NULL);
4373
4374                   break;
4375                 }
4376
4377             }
4378           while (pango_attr_iterator_next (iter));
4379
4380           pango_attr_iterator_destroy (iter);
4381         }
4382
4383       font = pango_context_load_font (layout->context, font_desc);
4384       if (font)
4385         {
4386           PangoFontMetrics *metrics;
4387
4388           metrics = pango_font_get_metrics (font,
4389                                             pango_context_get_language (layout->context));
4390
4391           if (metrics)
4392             {
4393               logical_rect->y = - pango_font_metrics_get_ascent (metrics);
4394               logical_rect->height = - logical_rect->y + pango_font_metrics_get_descent (metrics);
4395
4396               pango_font_metrics_unref (metrics);
4397             }
4398           else
4399             {
4400               logical_rect->y = 0;
4401               logical_rect->height = 0;
4402             }
4403           g_object_unref (font);
4404         }
4405       else
4406         {
4407           logical_rect->y = 0;
4408           logical_rect->height = 0;
4409         }
4410
4411       if (free_font_desc)
4412         pango_font_description_free (font_desc);
4413
4414       logical_rect->x = 0;
4415       logical_rect->width = 0;
4416     }
4417 }
4418
4419 static void
4420 pango_layout_line_get_empty_extents (PangoLayoutLine *line,
4421                                      PangoRectangle  *logical_rect)
4422 {
4423   pango_layout_get_empty_extents_at_index (line->layout, line->start_index, logical_rect);
4424 }
4425
4426 static void
4427 pango_layout_run_get_extents (PangoLayoutRun *run,
4428                               PangoRectangle *run_ink,
4429                               PangoRectangle *run_logical)
4430 {
4431   PangoRectangle logical;
4432   ItemProperties properties;
4433
4434   if (G_UNLIKELY (!run_ink && !run_logical))
4435     return;
4436
4437   pango_layout_get_item_properties (run->item, &properties);
4438
4439   if (!run_logical && (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE))
4440     run_logical = &logical;
4441
4442   if (!run_logical && (properties.uline != PANGO_UNDERLINE_NONE || properties.strikethrough))
4443     run_logical = &logical;
4444
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);
4450   else
4451     pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
4452                                 run_ink, run_logical);
4453
4454   if (run_ink && (properties.uline != PANGO_UNDERLINE_NONE || properties.strikethrough))
4455     {
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);
4462
4463       int new_pos;
4464
4465       /* the underline/strikethrough takes x,width of logical_rect.  reflect
4466        * that into ink_rect.
4467        */
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;
4471
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.
4474        */
4475
4476       if (properties.strikethrough)
4477         {
4478           if (run_ink->height == 0)
4479             {
4480               run_ink->height = strikethrough_thickness;
4481               run_ink->y = -strikethrough_position;
4482             }
4483         }
4484
4485       switch (properties.uline)
4486         {
4487         case PANGO_UNDERLINE_ERROR:
4488           run_ink->height = MAX (run_ink->height,
4489                                  3 * underline_thickness - underline_position - run_ink->y);
4490           break;
4491         case PANGO_UNDERLINE_SINGLE:
4492           run_ink->height = MAX (run_ink->height,
4493                                  underline_thickness - underline_position - run_ink->y);
4494           break;
4495         case PANGO_UNDERLINE_DOUBLE:
4496           run_ink->height = MAX (run_ink->height,
4497                                  3 * underline_thickness - underline_position - run_ink->y);
4498           break;
4499         case PANGO_UNDERLINE_LOW:
4500           run_ink->height += 2 * underline_thickness;
4501           break;
4502         case PANGO_UNDERLINE_NONE:
4503           break;
4504         default:
4505           g_critical ("unknown underline mode");
4506           break;
4507         }
4508
4509       pango_font_metrics_unref (metrics);
4510     }
4511
4512   if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE)
4513     {
4514       gboolean is_hinted = (run_logical->y & run_logical->height & (PANGO_SCALE - 1)) == 0;
4515       int adjustment = run_logical->y + run_logical->height / 2;
4516
4517       if (is_hinted)
4518         adjustment = PANGO_UNITS_ROUND (adjustment);
4519
4520       properties.rise += adjustment;
4521     }
4522
4523   if (properties.rise != 0)
4524     {
4525       if (run_ink)
4526         run_ink->y -= properties.rise;
4527
4528       if (run_logical)
4529         run_logical->y -= properties.rise;
4530     }
4531 }
4532
4533 /**
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
4539  *            string, or %NULL
4540  *
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.
4544  */
4545 void
4546 pango_layout_line_get_extents (PangoLayoutLine *line,
4547                                PangoRectangle  *ink_rect,
4548                                PangoRectangle  *logical_rect)
4549 {
4550   PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
4551   GSList *tmp_list;
4552   int x_pos = 0;
4553   gboolean caching = FALSE;
4554
4555   g_return_if_fail (LINE_IS_VALID (line));
4556
4557   if (!LINE_IS_VALID (line))
4558     return;
4559
4560   if (G_UNLIKELY (!ink_rect && !logical_rect))
4561     return;
4562
4563   switch (private->cache_status)
4564     {
4565     case CACHED:
4566       {
4567         if (ink_rect)
4568           *ink_rect = private->ink_rect;
4569         if (logical_rect)
4570           *logical_rect = private->logical_rect;
4571         return;
4572       }
4573     case NOT_CACHED:
4574       {
4575         caching = TRUE;
4576         if (!ink_rect)
4577           ink_rect = &private->ink_rect;
4578         if (!logical_rect)
4579           logical_rect = &private->logical_rect;
4580         break;
4581       }
4582     case LEAKED:
4583       {
4584         break;
4585       }
4586     }
4587
4588   if (ink_rect)
4589     {
4590       ink_rect->x = 0;
4591       ink_rect->y = 0;
4592       ink_rect->width = 0;
4593       ink_rect->height = 0;
4594     }
4595
4596   if (logical_rect)
4597     {
4598       logical_rect->x = 0;
4599       logical_rect->y = 0;
4600       logical_rect->width = 0;
4601       logical_rect->height = 0;
4602     }
4603
4604   tmp_list = line->runs;
4605   while (tmp_list)
4606     {
4607       PangoLayoutRun *run = tmp_list->data;
4608       int new_pos;
4609       PangoRectangle run_ink;
4610       PangoRectangle run_logical;
4611
4612       pango_layout_run_get_extents (run,
4613                                     ink_rect ? &run_ink : NULL,
4614                                     &run_logical);
4615
4616       if (ink_rect)
4617         {
4618           if (ink_rect->width == 0 || ink_rect->height == 0)
4619             {
4620               *ink_rect = run_ink;
4621               ink_rect->x += x_pos;
4622             }
4623           else if (run_ink.width != 0 && run_ink.height != 0)
4624             {
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;
4629
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;
4634             }
4635         }
4636
4637       if (logical_rect)
4638         {
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;
4643
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;
4648         }
4649
4650      x_pos += run_logical.width;
4651      tmp_list = tmp_list->next;
4652     }
4653
4654   if (logical_rect && !line->runs)
4655     pango_layout_line_get_empty_extents (line, logical_rect);
4656
4657   if (caching)
4658     {
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;
4664     }
4665 }
4666
4667 static PangoLayoutLine *
4668 pango_layout_line_new (PangoLayout *layout)
4669 {
4670   PangoLayoutLinePrivate *private = g_slice_new (PangoLayoutLinePrivate);
4671
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;
4677
4678   /* Note that we leave start_index, resolved_dir, and is_paragraph_start
4679    *  uninitialized */
4680
4681   return (PangoLayoutLine *) private;
4682 }
4683
4684 /**
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
4690  *               string, or %NULL
4691  *
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()).
4697  **/
4698 void
4699 pango_layout_line_get_pixel_extents (PangoLayoutLine *layout_line,
4700                                      PangoRectangle  *ink_rect,
4701                                      PangoRectangle  *logical_rect)
4702 {
4703   g_return_if_fail (LINE_IS_VALID (layout_line));
4704
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);
4708 }
4709
4710 /*
4711  * NB: This implement the exact same algorithm as
4712  *     reorder-items.c:pango_reorder_items().
4713  */
4714
4715 static GSList *
4716 reorder_runs_recurse (GSList *items, int n_items)
4717 {
4718   GSList *tmp_list, *level_start_node;
4719   int i, level_start_i;
4720   int min_level = G_MAXINT;
4721   GSList *result = NULL;
4722
4723   if (n_items == 0)
4724     return NULL;
4725
4726   tmp_list = items;
4727   for (i=0; i<n_items; i++)
4728     {
4729       PangoLayoutRun *run = tmp_list->data;
4730
4731       min_level = MIN (min_level, run->item->analysis.level);
4732
4733       tmp_list = tmp_list->next;
4734     }
4735
4736   level_start_i = 0;
4737   level_start_node = items;
4738   tmp_list = items;
4739   for (i=0; i<n_items; i++)
4740     {
4741       PangoLayoutRun *run = tmp_list->data;
4742
4743       if (run->item->analysis.level == min_level)
4744         {
4745           if (min_level % 2)
4746             {
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);
4750             }
4751           else
4752             {
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);
4756             }
4757
4758           level_start_i = i + 1;
4759           level_start_node = tmp_list->next;
4760         }
4761
4762       tmp_list = tmp_list->next;
4763     }
4764
4765   if (min_level % 2)
4766     {
4767       if (i > level_start_i)
4768         result = g_slist_concat (reorder_runs_recurse (level_start_node, i - level_start_i), result);
4769     }
4770   else
4771     {
4772       if (i > level_start_i)
4773         result = g_slist_concat (result, reorder_runs_recurse (level_start_node, i - level_start_i));
4774     }
4775
4776   return result;
4777 }
4778
4779 static void
4780 pango_layout_line_reorder (PangoLayoutLine *line)
4781 {
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);
4785 }
4786
4787 static int
4788 get_item_letter_spacing (PangoItem *item)
4789 {
4790   ItemProperties properties;
4791
4792   pango_layout_get_item_properties (item, &properties);
4793
4794   return properties.letter_spacing;
4795 }
4796
4797 static void
4798 pad_glyphstring_right (PangoGlyphString *glyphs,
4799                        ParaBreakState   *state,
4800                        int               adjustment)
4801 {
4802   int glyph = glyphs->num_glyphs - 1;
4803
4804   while (glyph >= 0 && glyphs->glyphs[glyph].geometry.width == 0)
4805     glyph--;
4806
4807   if (glyph < 0)
4808     return;
4809
4810   state->remaining_width -= adjustment;
4811   glyphs->glyphs[glyph].geometry.width += adjustment;
4812   if (glyphs->glyphs[glyph].geometry.width < 0)
4813     {
4814       state->remaining_width += glyphs->glyphs[glyph].geometry.width;
4815       glyphs->glyphs[glyph].geometry.width = 0;
4816     }
4817 }
4818
4819 static void
4820 pad_glyphstring_left (PangoGlyphString *glyphs,
4821                       ParaBreakState   *state,
4822                       int               adjustment)
4823 {
4824   int glyph = 0;
4825
4826   while (glyph < glyphs->num_glyphs && glyphs->glyphs[glyph].geometry.width == 0)
4827     glyph++;
4828
4829   if (glyph == glyphs->num_glyphs)
4830     return;
4831
4832   state->remaining_width -= adjustment;
4833   glyphs->glyphs[glyph].geometry.width += adjustment;
4834   glyphs->glyphs[glyph].geometry.x_offset += adjustment;
4835 }
4836
4837 static gboolean
4838 is_tab_run (PangoLayout    *layout,
4839             PangoLayoutRun *run)
4840 {
4841   return (layout->text[run->item->offset] == '\t');
4842 }
4843
4844 static void
4845 zero_line_final_space (PangoLayoutLine *line,
4846                        ParaBreakState  *state,
4847                        PangoLayoutRun  *run)
4848 {
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;
4853   const char *p;
4854
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
4857    */
4858
4859   if (glyphs->num_glyphs < 1 || state->start_offset == 0 ||
4860       !layout->log_attrs[state->start_offset - 1].is_white)
4861     return;
4862
4863   p = g_utf8_prev_char (layout->text + item->offset + item->length);
4864   if (p != layout->text + item->offset + glyphs->log_clusters[glyph])
4865     return;
4866
4867   state->remaining_width += glyphs->glyphs[glyph].geometry.width;
4868   glyphs->glyphs[glyph].geometry.width = 0;
4869 }
4870
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.
4875  *
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
4881  * cases.
4882  */
4883 static void
4884 adjust_line_letter_spacing (PangoLayoutLine *line,
4885                             ParaBreakState  *state)
4886 {
4887   PangoLayout *layout = line->layout;
4888   gboolean reversed;
4889   PangoLayoutRun *last_run;
4890   int tab_adjustment;
4891   GSList *l;
4892
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
4896    * tab stops.
4897    */
4898   reversed = FALSE;
4899   if (line->resolved_dir == PANGO_DIRECTION_RTL)
4900     {
4901       for (l = line->runs; l; l = l->next)
4902         if (is_tab_run (layout, l->data))
4903           {
4904             line->runs = g_slist_reverse (line->runs);
4905             reversed = TRUE;
4906             break;
4907           }
4908     }
4909
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.
4914    *
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.
4918    */
4919
4920   last_run = NULL;
4921   tab_adjustment = 0;
4922   for (l = line->runs; l; l = l->next)
4923     {
4924       PangoLayoutRun *run = l->data;
4925       PangoLayoutRun *next_run = l->next ? l->next->data : NULL;
4926
4927       if (is_tab_run (layout, run))
4928         {
4929           pad_glyphstring_right (run->glyphs, state, tab_adjustment);
4930           tab_adjustment = 0;
4931         }
4932       else
4933         {
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;
4938
4939           distribute_letter_spacing (run_spacing, &space_left, &space_right);
4940
4941           if (run->glyphs->glyphs[0].geometry.width == 0)
4942             {
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);
4946             }
4947           else if (!visual_last_run || is_tab_run (layout, visual_last_run))
4948             {
4949               pad_glyphstring_left (run->glyphs, state, - space_left);
4950               tab_adjustment += space_left;
4951             }
4952
4953           if (run->glyphs->glyphs[run->glyphs->num_glyphs - 1].geometry.width == 0)
4954             {
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);
4958             }
4959           else if (!visual_next_run || is_tab_run (layout, visual_next_run))
4960             {
4961               pad_glyphstring_right (run->glyphs, state, - space_right);
4962               tab_adjustment += space_right;
4963             }
4964         }
4965
4966       last_run = run;
4967     }
4968
4969   if (reversed)
4970     line->runs = g_slist_reverse (line->runs);
4971 }
4972
4973 static void
4974 justify_clusters (PangoLayoutLine *line,
4975                   ParaBreakState  *state)
4976 {
4977   int total_remaining_width, total_gaps = 0;
4978   int added_so_far, gaps_so_far;
4979   gboolean is_hinted;
4980   GSList *run_iter;
4981   enum {
4982     MEASURE,
4983     ADJUST
4984   } mode;
4985
4986   total_remaining_width = state->remaining_width;
4987   if (total_remaining_width <= 0)
4988     return;
4989
4990   /* hint to full pixel if total remaining width was so */
4991   is_hinted = (total_remaining_width & (PANGO_SCALE - 1)) == 0;
4992
4993   for (mode = MEASURE; mode <= ADJUST; mode++)
4994     {
4995       added_so_far = 0;
4996       gaps_so_far = 0;
4997
4998       for (run_iter = line->runs; run_iter; run_iter = run_iter->next)
4999         {
5000           PangoLayoutRun *run = run_iter->data;
5001           PangoGlyphString *glyphs = run->glyphs;
5002           gboolean is_first_gap = TRUE;
5003
5004           int i;
5005
5006           for (i = 0; i < glyphs->num_glyphs; i++)
5007             {
5008               if (!glyphs->glyphs[i].attr.is_cluster_start)
5009                 continue;
5010
5011               /* also don't expand zero-width spaces at the end of runs */
5012               if (glyphs->glyphs[i].geometry.width == 0)
5013                 {
5014                   if (i == glyphs->num_glyphs -1)
5015                     continue;
5016
5017                   if (i == 0 && glyphs->num_glyphs > 1 && glyphs->glyphs[i+1].attr.is_cluster_start)
5018                     continue;
5019                 }
5020
5021               if (is_first_gap)
5022                 {
5023                   is_first_gap = FALSE;
5024                   continue;
5025                 }
5026
5027               gaps_so_far++;
5028
5029               if (mode == ADJUST)
5030                 {
5031                   int adjustment, space_left, space_right;
5032
5033                   adjustment = (gaps_so_far * total_remaining_width) / total_gaps - added_so_far;
5034                   if (is_hinted)
5035                     adjustment = PANGO_UNITS_ROUND (adjustment);
5036                   /* distribute to before/after */
5037                   distribute_letter_spacing (adjustment, &space_left, &space_right);
5038
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;
5042
5043                   added_so_far += adjustment;
5044                 }
5045             }
5046         }
5047
5048       if (mode == MEASURE)
5049         {
5050           total_gaps = gaps_so_far;
5051
5052           if (total_gaps == 0)
5053             {
5054               /* a single cluster, can't really justify it */
5055               return;
5056             }
5057         }
5058     }
5059
5060   state->remaining_width -= added_so_far;
5061 }
5062
5063 static void
5064 justify_words (PangoLayoutLine *line,
5065                ParaBreakState  *state)
5066 {
5067   const gchar *text = line->layout->text;
5068   const PangoLogAttr *log_attrs = line->layout->log_attrs;
5069
5070   int offset;
5071   int total_remaining_width, total_space_width = 0;
5072   int added_so_far, spaces_so_far;
5073   gboolean is_hinted;
5074   GSList *run_iter;
5075   enum {
5076     MEASURE,
5077     ADJUST
5078   } mode;
5079
5080   total_remaining_width = state->remaining_width;
5081   if (total_remaining_width <= 0)
5082     return;
5083
5084   /* hint to full pixel if total remaining width was so */
5085   is_hinted = (total_remaining_width & (PANGO_SCALE - 1)) == 0;
5086
5087   for (mode = MEASURE; mode <= ADJUST; mode++)
5088     {
5089       added_so_far = 0;
5090       spaces_so_far = 0;
5091
5092       offset = state->line_start_offset;
5093       for (run_iter = line->runs; run_iter; run_iter = run_iter->next)
5094         {
5095           PangoLayoutRun *run = run_iter->data;
5096           PangoGlyphString *glyphs = run->glyphs;
5097           PangoGlyphItemIter cluster_iter;
5098           gboolean have_cluster;
5099
5100           for (have_cluster = pango_glyph_item_iter_init_start (&cluster_iter, run, text);
5101                have_cluster;
5102                have_cluster = pango_glyph_item_iter_next_cluster (&cluster_iter))
5103             {
5104               int i;
5105               int dir;
5106
5107               if (!log_attrs[offset + cluster_iter.start_char].is_expandable_space)
5108                 continue;
5109
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)
5112                 {
5113                   int glyph_width = glyphs->glyphs[i].geometry.width;
5114
5115                   if (glyph_width == 0)
5116                     continue;
5117
5118                   spaces_so_far += glyph_width;
5119
5120                   if (mode == ADJUST)
5121                     {
5122                       int adjustment;
5123
5124                       adjustment = ((guint64) spaces_so_far * total_remaining_width) / total_space_width - added_so_far;
5125                       if (is_hinted)
5126                         adjustment = PANGO_UNITS_ROUND (adjustment);
5127
5128                       glyphs->glyphs[i].geometry.width += adjustment;
5129                       added_so_far += adjustment;
5130                     }
5131                 }
5132             }
5133
5134           offset += glyphs->num_glyphs;
5135         }
5136
5137       if (mode == MEASURE)
5138         {
5139           total_space_width = spaces_so_far;
5140
5141           if (total_space_width == 0)
5142             {
5143               justify_clusters (line, state);
5144               return;
5145             }
5146         }
5147     }
5148
5149   state->remaining_width -= added_so_far;
5150 }
5151
5152 static void
5153 pango_layout_line_postprocess (PangoLayoutLine *line,
5154                                ParaBreakState  *state,
5155                                gboolean         wrapped)
5156 {
5157   gboolean ellipsized = FALSE;
5158   
5159   DEBUG ("postprocessing", line, state);
5160
5161   /* Truncate the logical-final whitespace in the line if we broke the line at it
5162    */
5163   if (wrapped)
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);
5167
5168   /* Reverse the runs
5169    */
5170   line->runs = g_slist_reverse (line->runs);
5171
5172   /* Ellipsize the line if necessary
5173    */
5174   if (G_UNLIKELY (state->line_width >= 0 &&
5175                   should_ellipsize_current_line (line->layout, state)))
5176     {
5177       ellipsized = _pango_layout_line_ellipsize (line, state->attrs, state->line_width);
5178     }
5179
5180   DEBUG ("after removing final space", line, state);
5181
5182   /* Now convert logical to visual order
5183    */
5184   pango_layout_line_reorder (line);
5185
5186   DEBUG ("after reordering", line, state);
5187
5188   /* Fixup letter spacing between runs
5189    */
5190   adjust_line_letter_spacing (line, state);
5191
5192   DEBUG ("after letter spacing", line, state);
5193
5194   /* Distribute extra space between words if justifying and line was wrapped
5195    */
5196   if (line->layout->justify && (wrapped || ellipsized))
5197     {
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);
5201
5202       justify_words (line, state);
5203     }
5204
5205   DEBUG ("after justification", line, state);
5206
5207   line->layout->is_wrapped |= wrapped;
5208   line->layout->is_ellipsized |= ellipsized;
5209 }
5210
5211 static void
5212 pango_layout_get_item_properties (PangoItem      *item,
5213                                   ItemProperties *properties)
5214 {
5215   GSList *tmp_list = item->analysis.extra_attrs;
5216
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;
5224
5225   while (tmp_list)
5226     {
5227       PangoAttribute *attr = tmp_list->data;
5228
5229       switch ((int) attr->klass->type)
5230         {
5231         case PANGO_ATTR_UNDERLINE:
5232           properties->uline = ((PangoAttrInt *)attr)->value;
5233           break;
5234
5235         case PANGO_ATTR_STRIKETHROUGH:
5236           properties->strikethrough = ((PangoAttrInt *)attr)->value;
5237           break;
5238
5239         case PANGO_ATTR_RISE:
5240           properties->rise = ((PangoAttrInt *)attr)->value;
5241           break;
5242
5243         case PANGO_ATTR_LETTER_SPACING:
5244           properties->letter_spacing = ((PangoAttrInt *)attr)->value;
5245           break;
5246
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;
5251           break;
5252
5253         default:
5254           break;
5255         }
5256       tmp_list = tmp_list->next;
5257     }
5258 }
5259
5260 static int
5261 next_cluster_start (PangoGlyphString *gs,
5262                     int               cluster_start)
5263 {
5264   int i;
5265
5266   i = cluster_start + 1;
5267   while (i < gs->num_glyphs)
5268     {
5269       if (gs->glyphs[i].attr.is_cluster_start)
5270         return i;
5271
5272       i++;
5273     }
5274
5275   return gs->num_glyphs;
5276 }
5277
5278 static int
5279 cluster_width (PangoGlyphString *gs,
5280                int               cluster_start)
5281 {
5282   int i;
5283   int width;
5284
5285   width = gs->glyphs[cluster_start].geometry.width;
5286   i = cluster_start + 1;
5287   while (i < gs->num_glyphs)
5288     {
5289       if (gs->glyphs[i].attr.is_cluster_start)
5290         break;
5291
5292       width += gs->glyphs[i].geometry.width;
5293       i++;
5294     }
5295
5296   return width;
5297 }
5298
5299 static inline void
5300 offset_y (PangoLayoutIter *iter,
5301           int             *y)
5302 {
5303   Extents *line_ext;
5304
5305   line_ext = (Extents*)iter->line_extents_link->data;
5306
5307   *y += line_ext->baseline;
5308 }
5309
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.
5312  */
5313 static void
5314 update_cluster (PangoLayoutIter *iter,
5315                 int              cluster_start_index)
5316 {
5317   char             *cluster_text;
5318   PangoGlyphString *gs;
5319   int               cluster_length;
5320
5321   iter->character_position = 0;
5322
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);
5326
5327   if (iter->ltr)
5328     {
5329       /* For LTR text, finding the length of the cluster is easy
5330        * since logical and visual runs are in the same direction.
5331        */
5332       if (iter->next_cluster_glyph < gs->num_glyphs)
5333         cluster_length = gs->log_clusters[iter->next_cluster_glyph] - cluster_start_index;
5334       else
5335         cluster_length = iter->run->item->length - cluster_start_index;
5336     }
5337   else
5338     {
5339       /* For RTL text, we have to scan backwards to find the previous
5340        * visual cluster which is the next logical cluster.
5341        */
5342       int i = iter->cluster_start;
5343       while (i > 0 && gs->log_clusters[i - 1] == cluster_start_index)
5344         i--;
5345
5346       if (i == 0)
5347         cluster_length = iter->run->item->length - cluster_start_index;
5348       else
5349         cluster_length = gs->log_clusters[i - 1] - cluster_start_index;
5350     }
5351
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);
5354
5355   if (iter->ltr)
5356     iter->index = cluster_text - iter->layout->text;
5357   else
5358     iter->index = g_utf8_prev_char (cluster_text + cluster_length) - iter->layout->text;
5359 }
5360
5361 static void
5362 update_run (PangoLayoutIter *iter,
5363             int              run_start_index)
5364 {
5365   Extents *line_ext;
5366
5367   line_ext = (Extents*)iter->line_extents_link->data;
5368
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
5371    * a line.
5372    */
5373   if (iter->run_list_link == iter->line->runs)
5374     iter->run_x = line_ext->logical_rect.x;
5375   else
5376     iter->run_x += iter->run_width;
5377
5378   if (iter->run)
5379     {
5380       iter->run_width = pango_glyph_string_get_width (iter->run->glyphs);
5381     }
5382   else
5383     {
5384       /* The empty run at the end of a line */
5385       iter->run_width = 0;
5386     }
5387
5388   if (iter->run)
5389     iter->ltr = (iter->run->item->analysis.level % 2) == 0;
5390   else
5391     iter->ltr = TRUE;
5392
5393   iter->cluster_start = 0;
5394   iter->cluster_x = iter->run_x;
5395
5396   if (iter->run)
5397     {
5398       update_cluster (iter, iter->run->glyphs->log_clusters[0]);
5399     }
5400   else
5401     {
5402       iter->cluster_width = 0;
5403       iter->character_position = 0;
5404       iter->cluster_num_chars = 0;
5405       iter->index = run_start_index;
5406     }
5407 }
5408
5409 /**
5410  * pango_layout_iter_copy:
5411  * @iter: a #PangoLayoutIter, may be %NULL
5412  *
5413  * Copies a #PangLayoutIter.
5414  *
5415  * Return value: the newly allocated #PangoLayoutIter, which should
5416  *               be freed with pango_layout_iter_free(), or %NULL if
5417  *               @iter was %NULL.
5418  *
5419  * Since: 1.20
5420  **/
5421 PangoLayoutIter *
5422 pango_layout_iter_copy (PangoLayoutIter *iter)
5423 {
5424   PangoLayoutIter *new;
5425   GSList *l;
5426
5427   if (iter == NULL)
5428     return NULL;
5429
5430   new = g_slice_new (PangoLayoutIter);
5431
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);
5436
5437   new->run_list_link = iter->run_list_link;
5438   new->run = iter->run;
5439   new->index = iter->index;
5440
5441   new->line_extents = NULL;
5442   new->line_extents_link = NULL;
5443   for (l = iter->line_extents; l; l = l->next)
5444     {
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;
5449     }
5450   new->line_extents = g_slist_reverse (new->line_extents);
5451
5452   new->run_x = iter->run_x;
5453   new->run_width = iter->run_width;
5454   new->ltr = iter->ltr;
5455
5456   new->cluster_x = iter->cluster_x;
5457   new->cluster_width = iter->cluster_width;
5458
5459   new->cluster_start = iter->cluster_start;
5460   new->next_cluster_glyph = iter->next_cluster_glyph;
5461
5462   new->cluster_num_chars = iter->cluster_num_chars;
5463   new->character_position = iter->character_position;
5464
5465   new->layout_width = iter->layout_width;
5466
5467   return new;
5468 }
5469
5470 GType
5471 pango_layout_iter_get_type (void)
5472 {
5473   static GType our_type = 0;
5474
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);
5479
5480   return our_type;
5481 }
5482
5483 /**
5484  * pango_layout_get_iter:
5485  * @layout: a #PangoLayout
5486  *
5487  * Returns an iterator to iterate over the visual extents of the layout.
5488  *
5489  * Return value: the new #PangoLayoutIter that should be freed using
5490  *               pango_layout_iter_free().
5491  **/
5492 PangoLayoutIter*
5493 pango_layout_get_iter (PangoLayout *layout)
5494 {
5495   int run_start_index;
5496   PangoLayoutIter *iter;
5497   PangoRectangle logical_rect;
5498
5499   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
5500
5501   iter = g_slice_new (PangoLayoutIter);
5502
5503   iter->layout = layout;
5504   g_object_ref (iter->layout);
5505
5506   pango_layout_check_lines (layout);
5507
5508   iter->line_list_link = layout->lines;
5509   iter->line = iter->line_list_link->data;
5510   pango_layout_line_ref (iter->line);
5511
5512   run_start_index = iter->line->start_index;
5513   iter->run_list_link = iter->line->runs;
5514
5515   if (iter->run_list_link)
5516     {
5517       iter->run = iter->run_list_link->data;
5518       run_start_index = iter->run->item->offset;
5519     }
5520   else
5521     iter->run = NULL;
5522
5523   iter->line_extents = NULL;
5524   pango_layout_get_extents_internal (layout,
5525                                      NULL,
5526                                      &logical_rect,
5527                                      &iter->line_extents);
5528   iter->layout_width = layout->width == -1 ? logical_rect.width : layout->width;
5529
5530   iter->line_extents_link = iter->line_extents;
5531
5532   update_run (iter, run_start_index);
5533
5534   return iter;
5535 }
5536
5537 /**
5538  * pango_layout_iter_free:
5539  * @iter: a #PangoLayoutIter, may be %NULL
5540  *
5541  * Frees an iterator that's no longer in use.
5542  **/
5543 void
5544 pango_layout_iter_free (PangoLayoutIter *iter)
5545 {
5546   if (iter == NULL)
5547     return;
5548
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);
5554 }
5555
5556 /**
5557  * pango_layout_iter_get_index:
5558  * @iter: a #PangoLayoutIter
5559  *
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()).
5564  *
5565  * Return value: current byte index.
5566  **/
5567 int
5568 pango_layout_iter_get_index (PangoLayoutIter *iter)
5569 {
5570   if (ITER_IS_INVALID (iter))
5571     return 0;
5572
5573   return iter->index;
5574 }
5575
5576 /**
5577  * pango_layout_iter_get_run:
5578  * @iter: a #PangoLayoutIter
5579  *
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.
5584  *
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.).
5587  *
5588  * Return value: the current run.
5589  **/
5590 PangoLayoutRun*
5591 pango_layout_iter_get_run (PangoLayoutIter *iter)
5592 {
5593   if (ITER_IS_INVALID (iter))
5594     return NULL;
5595
5596   pango_layout_line_leaked (iter->line);
5597
5598   return iter->run;
5599 }
5600
5601 /**
5602  * pango_layout_iter_get_run_readonly:
5603  * @iter: a #PangoLayoutIter
5604  *
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.
5609  *
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.).
5613  *
5614  * Return value: the current run, that should not be modified.
5615  *
5616  * Since: 1.16
5617  **/
5618 PangoLayoutRun*
5619 pango_layout_iter_get_run_readonly (PangoLayoutIter *iter)
5620 {
5621   if (ITER_IS_INVALID (iter))
5622     return NULL;
5623
5624   pango_layout_line_leaked (iter->line);
5625
5626   return iter->run;
5627 }
5628
5629 /* an inline-able version for local use */
5630 static PangoLayoutLine*
5631 _pango_layout_iter_get_line (PangoLayoutIter *iter)
5632 {
5633   return iter->line;
5634 }
5635
5636 /**
5637  * pango_layout_iter_get_line:
5638  * @iter: a #PangoLayoutIter
5639  *
5640  * Gets the current line.
5641  *
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.).
5644  *
5645  * Return value: the current line.
5646  **/
5647 PangoLayoutLine*
5648 pango_layout_iter_get_line (PangoLayoutIter *iter)
5649 {
5650   if (ITER_IS_INVALID (iter))
5651     return NULL;
5652
5653   pango_layout_line_leaked (iter->line);
5654
5655   return iter->line;
5656 }
5657
5658 /**
5659  * pango_layout_iter_get_line_readonly:
5660  * @iter: a #PangoLayoutIter
5661  *
5662  * Gets the current line for read-only access.
5663  *
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.).
5667  *
5668  * Return value: the current line, that should not be modified.
5669  *
5670  * Since: 1.16
5671  **/
5672 PangoLayoutLine*
5673 pango_layout_iter_get_line_readonly (PangoLayoutIter *iter)
5674 {
5675   if (ITER_IS_INVALID (iter))
5676     return NULL;
5677
5678   return iter->line;
5679 }
5680
5681 /**
5682  * pango_layout_iter_at_last_line:
5683  * @iter: a #PangoLayoutIter
5684  *
5685  * Determines whether @iter is on the last line of the layout.
5686  *
5687  * Return value: %TRUE if @iter is on the last line.
5688  **/
5689 gboolean
5690 pango_layout_iter_at_last_line (PangoLayoutIter *iter)
5691 {
5692   if (ITER_IS_INVALID (iter))
5693     return FALSE;
5694
5695   return iter->line_extents_link->next == NULL;
5696 }
5697
5698 /**
5699  * pango_layout_iter_get_layout:
5700  * @iter: a #PangoLayoutIter
5701  *
5702  * Gets the layout associated with a #PangoLayoutIter.
5703  *
5704  * Return value: the layout associated with @iter.
5705  *
5706  * Since: 1.20
5707  **/
5708 PangoLayout*
5709 pango_layout_iter_get_layout (PangoLayoutIter *iter)
5710 {
5711   /* check is redundant as it simply checks that iter->layout is not NULL */
5712   if (ITER_IS_INVALID (iter))
5713     return NULL;
5714
5715   return iter->layout;
5716 }
5717
5718
5719 static gboolean
5720 line_is_terminated (PangoLayoutIter *iter)
5721 {
5722   /* There is a real terminator at the end of each paragraph other
5723    * than the last.
5724    */
5725   if (iter->line_list_link->next)
5726     {
5727       PangoLayoutLine *next_line = iter->line_list_link->next->data;
5728       if (next_line->is_paragraph_start)
5729         return TRUE;
5730     }
5731
5732   return FALSE;
5733 }
5734
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.
5738  */
5739 static gboolean
5740 next_nonempty_line (PangoLayoutIter *iter,
5741                     gboolean         include_terminators)
5742 {
5743   gboolean result;
5744
5745   while (TRUE)
5746     {
5747       result = pango_layout_iter_next_line (iter);
5748       if (!result)
5749         break;
5750
5751       if (iter->line->runs)
5752         break;
5753
5754       if (include_terminators && line_is_terminated (iter))
5755         break;
5756     }
5757
5758   return result;
5759 }
5760
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.
5764  */
5765 static gboolean
5766 next_nonempty_run (PangoLayoutIter *iter,
5767                     gboolean         include_terminators)
5768 {
5769   gboolean result;
5770
5771   while (TRUE)
5772     {
5773       result = pango_layout_iter_next_run (iter);
5774       if (!result)
5775         break;
5776
5777       if (iter->run)
5778         break;
5779
5780       if (include_terminators && line_is_terminated (iter))
5781         break;
5782     }
5783
5784   return result;
5785 }
5786
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).
5790  */
5791 static gboolean
5792 next_cluster_internal (PangoLayoutIter *iter,
5793                        gboolean         include_terminators)
5794 {
5795   PangoGlyphString *gs;
5796   int               next_start;
5797
5798   if (ITER_IS_INVALID (iter))
5799     return FALSE;
5800
5801   if (iter->run == NULL)
5802     return next_nonempty_line (iter, include_terminators);
5803
5804   gs = iter->run->glyphs;
5805
5806   next_start = iter->next_cluster_glyph;
5807   if (next_start == gs->num_glyphs)
5808     {
5809       return next_nonempty_run (iter, include_terminators);
5810     }
5811   else
5812     {
5813       iter->cluster_start = next_start;
5814       iter->cluster_x += iter->cluster_width;
5815       update_cluster(iter, gs->log_clusters[iter->cluster_start]);
5816
5817       return TRUE;
5818     }
5819 }
5820
5821 /**
5822  * pango_layout_iter_next_char:
5823  * @iter: a #PangoLayoutIter
5824  *
5825  * Moves @iter forward to the next character in visual order. If @iter was already at
5826  * the end of the layout, returns %FALSE.
5827  *
5828  * Return value: whether motion was possible.
5829  **/
5830 gboolean
5831 pango_layout_iter_next_char (PangoLayoutIter *iter)
5832 {
5833   const char *text;
5834
5835   if (ITER_IS_INVALID (iter))
5836     return FALSE;
5837
5838   if (iter->run == NULL)
5839     {
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)
5844         {
5845           iter->character_position++;
5846           return TRUE;
5847         }
5848
5849       return next_nonempty_line (iter, TRUE);
5850     }
5851
5852   iter->character_position++;
5853   if (iter->character_position >= iter->cluster_num_chars)
5854     return next_cluster_internal (iter, TRUE);
5855
5856   text = iter->layout->text;
5857   if (iter->ltr)
5858     iter->index = g_utf8_next_char (text + iter->index) - text;
5859   else
5860     iter->index = g_utf8_prev_char (text + iter->index) - text;
5861
5862   return TRUE;
5863 }
5864
5865 /**
5866  * pango_layout_iter_next_cluster:
5867  * @iter: a #PangoLayoutIter
5868  *
5869  * Moves @iter forward to the next cluster in visual order. If @iter
5870  * was already at the end of the layout, returns %FALSE.
5871  *
5872  * Return value: whether motion was possible.
5873  **/
5874 gboolean
5875 pango_layout_iter_next_cluster (PangoLayoutIter *iter)
5876 {
5877   return next_cluster_internal (iter, FALSE);
5878 }
5879
5880 /**
5881  * pango_layout_iter_next_run:
5882  * @iter: a #PangoLayoutIter
5883  *
5884  * Moves @iter forward to the next run in visual order. If @iter was
5885  * already at the end of the layout, returns %FALSE.
5886  *
5887  * Return value: whether motion was possible.
5888  **/
5889 gboolean
5890 pango_layout_iter_next_run (PangoLayoutIter *iter)
5891 {
5892   int next_run_start; /* byte index */
5893   GSList *next_link;
5894
5895   if (ITER_IS_INVALID (iter))
5896     return FALSE;
5897
5898   if (iter->run == NULL)
5899     return pango_layout_iter_next_line (iter);
5900
5901   next_link = iter->run_list_link->next;
5902
5903   if (next_link == NULL)
5904     {
5905       /* Moving on to the zero-width "virtual run" at the end of each
5906        * line
5907        */
5908       next_run_start = iter->run->item->offset + iter->run->item->length;
5909       iter->run = NULL;
5910       iter->run_list_link = NULL;
5911     }
5912   else
5913     {
5914       iter->run_list_link = next_link;
5915       iter->run = iter->run_list_link->data;
5916       next_run_start = iter->run->item->offset;
5917     }
5918
5919   update_run (iter, next_run_start);
5920
5921   return TRUE;
5922 }
5923
5924 /**
5925  * pango_layout_iter_next_line:
5926  * @iter: a #PangoLayoutIter
5927  *
5928  * Moves @iter forward to the start of the next line. If @iter is
5929  * already on the last line, returns %FALSE.
5930  *
5931  * Return value: whether motion was possible.
5932  **/
5933 gboolean
5934 pango_layout_iter_next_line (PangoLayoutIter *iter)
5935 {
5936   GSList *next_link;
5937
5938   if (ITER_IS_INVALID (iter))
5939     return FALSE;
5940
5941   next_link = iter->line_list_link->next;
5942
5943   if (next_link == NULL)
5944     return FALSE;
5945
5946   iter->line_list_link = next_link;
5947
5948   pango_layout_line_unref (iter->line);
5949
5950   iter->line = iter->line_list_link->data;
5951
5952   pango_layout_line_ref (iter->line);
5953
5954   iter->run_list_link = iter->line->runs;
5955
5956   if (iter->run_list_link)
5957     iter->run = iter->run_list_link->data;
5958   else
5959     iter->run = NULL;
5960
5961   iter->line_extents_link = iter->line_extents_link->next;
5962   g_assert (iter->line_extents_link != NULL);
5963
5964   update_run (iter, iter->line->start_index);
5965
5966   return TRUE;
5967 }
5968
5969 /**
5970  * pango_layout_iter_get_char_extents:
5971  * @iter: a #PangoLayoutIter
5972  * @logical_rect: rectangle to fill with logical extents
5973  *
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.
5978  *
5979  **/
5980 void
5981 pango_layout_iter_get_char_extents (PangoLayoutIter *iter,
5982                                     PangoRectangle  *logical_rect)
5983 {
5984   PangoRectangle cluster_rect;
5985   int            x0, x1;
5986
5987   if (ITER_IS_INVALID (iter))
5988     return;
5989
5990   if (logical_rect == NULL)
5991     return;
5992
5993   pango_layout_iter_get_cluster_extents (iter, NULL, &cluster_rect);
5994
5995   if (iter->run == NULL)
5996     {
5997       /* When on the NULL run, cluster, char, and run all have the
5998        * same extents
5999        */
6000       *logical_rect = cluster_rect;
6001       return;
6002     }
6003
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;
6006
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;
6011 }
6012
6013 /**
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
6018  *
6019  * Gets the extents of the current cluster, in layout coordinates
6020  * (origin is the top left of the entire layout).
6021  *
6022  **/
6023 void
6024 pango_layout_iter_get_cluster_extents (PangoLayoutIter *iter,
6025                                        PangoRectangle  *ink_rect,
6026                                        PangoRectangle  *logical_rect)
6027 {
6028   if (ITER_IS_INVALID (iter))
6029     return;
6030
6031   if (iter->run == NULL)
6032     {
6033       /* When on the NULL run, cluster, char, and run all have the
6034        * same extents
6035        */
6036       pango_layout_iter_get_run_extents (iter, ink_rect, logical_rect);
6037       return;
6038     }
6039
6040   pango_glyph_string_extents_range (iter->run->glyphs,
6041                                     iter->cluster_start,
6042                                     iter->next_cluster_glyph,
6043                                     iter->run->item->analysis.font,
6044                                     ink_rect,
6045                                     logical_rect);
6046
6047   if (ink_rect)
6048     {
6049       ink_rect->x += iter->cluster_x;
6050       offset_y (iter, &ink_rect->y);
6051     }
6052
6053   if (logical_rect)
6054     {
6055       g_assert (logical_rect->width == iter->cluster_width);
6056       logical_rect->x += iter->cluster_x;
6057       offset_y (iter, &logical_rect->y);
6058     }
6059 }
6060
6061 /**
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
6066  *
6067  * Gets the extents of the current run in layout coordinates
6068  * (origin is the top left of the entire layout).
6069  *
6070  **/
6071 void
6072 pango_layout_iter_get_run_extents (PangoLayoutIter *iter,
6073                                    PangoRectangle  *ink_rect,
6074                                    PangoRectangle  *logical_rect)
6075 {
6076   if (G_UNLIKELY (!ink_rect && !logical_rect))
6077     return;
6078
6079   if (ITER_IS_INVALID (iter))
6080     return;
6081
6082   if (iter->run)
6083     {
6084       pango_layout_run_get_extents (iter->run, ink_rect, logical_rect);
6085
6086       if (ink_rect)
6087         {
6088           offset_y (iter, &ink_rect->y);
6089           ink_rect->x += iter->run_x;
6090         }
6091
6092       if (logical_rect)
6093         {
6094           offset_y (iter, &logical_rect->y);
6095           logical_rect->x += iter->run_x;
6096         }
6097     }
6098   else
6099     {
6100       /* The empty run at the end of a line */
6101
6102       pango_layout_iter_get_line_extents (iter, ink_rect, logical_rect);
6103
6104       if (ink_rect)
6105         {
6106           ink_rect->x = iter->run_x;
6107           ink_rect->width = 0;
6108         }
6109
6110       if (logical_rect)
6111         {
6112           logical_rect->x = iter->run_x;
6113           logical_rect->width = 0;
6114         }
6115     }
6116 }
6117
6118 /**
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
6123  *
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().
6130  *
6131  **/
6132 void
6133 pango_layout_iter_get_line_extents (PangoLayoutIter *iter,
6134                                     PangoRectangle  *ink_rect,
6135                                     PangoRectangle  *logical_rect)
6136 {
6137   Extents *ext;
6138
6139   if (ITER_IS_INVALID (iter))
6140     return;
6141
6142   ext = iter->line_extents_link->data;
6143
6144   if (ink_rect)
6145     {
6146       get_line_extents_layout_coords (iter->layout, iter->line,
6147                                       iter->layout_width,
6148                                       ext->logical_rect.y,
6149                                       NULL,
6150                                       ink_rect,
6151                                       NULL);
6152     }
6153
6154   if (logical_rect)
6155     *logical_rect = ext->logical_rect;
6156 }
6157
6158 /**
6159  * pango_layout_iter_get_line_yrange:
6160  * @iter: a #PangoLayoutIter
6161  * @y0_: start of line
6162  * @y1_: end of line
6163  *
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
6170  * entire layout).
6171  *
6172  **/
6173 void
6174 pango_layout_iter_get_line_yrange (PangoLayoutIter *iter,
6175                                    int             *y0,
6176                                    int             *y1)
6177 {
6178   Extents *ext;
6179   int half_spacing;
6180
6181   if (ITER_IS_INVALID (iter))
6182     return;
6183
6184   ext = iter->line_extents_link->data;
6185
6186   half_spacing = iter->layout->spacing / 2;
6187
6188   /* Note that if layout->spacing is odd, the remainder spacing goes
6189    * above the line (this is pretty arbitrary of course)
6190    */
6191
6192   if (y0)
6193     {
6194       /* No spacing above the first line */
6195
6196       if (iter->line_extents_link == iter->line_extents)
6197         *y0 = ext->logical_rect.y;
6198       else
6199         *y0 = ext->logical_rect.y - (iter->layout->spacing - half_spacing);
6200     }
6201
6202   if (y1)
6203     {
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;
6207       else
6208         *y1 = ext->logical_rect.y + ext->logical_rect.height + half_spacing;
6209     }
6210 }
6211
6212 /**
6213  * pango_layout_iter_get_baseline:
6214  * @iter: a #PangoLayoutIter
6215  *
6216  * Gets the Y position of the current line's baseline, in layout
6217  * coordinates (origin at top left of the entire layout).
6218  *
6219  * Return value: baseline of current line.
6220  **/
6221 int
6222 pango_layout_iter_get_baseline (PangoLayoutIter *iter)
6223 {
6224   Extents *ext;
6225
6226   if (ITER_IS_INVALID (iter))
6227     return 0;
6228
6229   ext = iter->line_extents_link->data;
6230
6231   return ext->baseline;
6232 }
6233
6234 /**
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
6239  *
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.
6243  *
6244  **/
6245 void
6246 pango_layout_iter_get_layout_extents  (PangoLayoutIter *iter,
6247                                        PangoRectangle  *ink_rect,
6248                                        PangoRectangle  *logical_rect)
6249 {
6250   if (ITER_IS_INVALID (iter))
6251     return;
6252
6253   pango_layout_get_extents (iter->layout, ink_rect, logical_rect);
6254 }