Upload tizen 2.0 beta source
[external/pango1.0.git] / pango / pango-glyph-item.c
1 /* Pango
2  * pango-glyph-item.c: Pair of PangoItem and a glyph string
3  *
4  * Copyright (C) 2002 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23 #include <string.h>
24
25 #include "pango-glyph-item.h"
26 #include "pango-impl-utils.h"
27
28 #define LTR(glyph_item) (((glyph_item)->item->analysis.level % 2) == 0)
29
30 /**
31  * pango_glyph_item_split:
32  * @orig: a #PangoItem
33  * @text: text to which positions in @orig apply
34  * @split_index: byte index of position to split item, relative to the start of the item
35  *
36  * Modifies @orig to cover only the text after @split_index, and
37  * returns a new item that covers the text before @split_index that
38  * used to be in @orig. You can think of @split_index as the length of
39  * the returned item. @split_index may not be 0, and it may not be
40  * greater than or equal to the length of @orig (that is, there must
41  * be at least one byte assigned to each item, you can't create a
42  * zero-length item).
43  *
44  * This function is similar in function to pango_item_split() (and uses
45  * it internally.)
46  *
47  * Return value: the newly allocated item representing text before
48  *               @split_index, which should be freed
49  *               with pango_glyph_item_free().
50  *
51  * Since: 1.2
52  **/
53 PangoGlyphItem *
54 pango_glyph_item_split (PangoGlyphItem *orig,
55                         const char     *text,
56                         int             split_index)
57 {
58   PangoGlyphItem *new;
59   int i;
60   int num_glyphs;
61   int num_remaining;
62   int split_offset;
63
64   g_return_val_if_fail (orig != NULL, NULL);
65   g_return_val_if_fail (orig->item->length > 0, NULL);
66   g_return_val_if_fail (split_index > 0, NULL);
67   g_return_val_if_fail (split_index < orig->item->length, NULL);
68
69   if (LTR (orig))
70     {
71       for (i = 0; i < orig->glyphs->num_glyphs; i++)
72         {
73           if (orig->glyphs->log_clusters[i] >= split_index)
74             break;
75         }
76
77       if (i == orig->glyphs->num_glyphs) /* No splitting necessary */
78         return NULL;
79
80       split_index = orig->glyphs->log_clusters[i];
81       num_glyphs = i;
82     }
83   else
84     {
85       for (i = orig->glyphs->num_glyphs - 1; i >= 0; i--)
86         {
87           if (orig->glyphs->log_clusters[i] >= split_index)
88             break;
89         }
90
91       if (i < 0) /* No splitting necessary */
92         return NULL;
93
94       split_index = orig->glyphs->log_clusters[i];
95       num_glyphs = orig->glyphs->num_glyphs - 1 - i;
96     }
97
98   num_remaining = orig->glyphs->num_glyphs - num_glyphs;
99
100   new = g_slice_new (PangoGlyphItem);
101   split_offset = g_utf8_pointer_to_offset (text + orig->item->offset,
102                                            text + orig->item->offset + split_index);
103   new->item = pango_item_split (orig->item, split_index, split_offset);
104
105   new->glyphs = pango_glyph_string_new ();
106   pango_glyph_string_set_size (new->glyphs, num_glyphs);
107
108   if (LTR (orig))
109     {
110       memcpy (new->glyphs->glyphs, orig->glyphs->glyphs, num_glyphs * sizeof (PangoGlyphInfo));
111       memcpy (new->glyphs->log_clusters, orig->glyphs->log_clusters, num_glyphs * sizeof (int));
112
113       memmove (orig->glyphs->glyphs, orig->glyphs->glyphs + num_glyphs,
114                num_remaining * sizeof (PangoGlyphInfo));
115       for (i = num_glyphs; i < orig->glyphs->num_glyphs; i++)
116         orig->glyphs->log_clusters[i - num_glyphs] = orig->glyphs->log_clusters[i] - split_index;
117     }
118   else
119     {
120       memcpy (new->glyphs->glyphs, orig->glyphs->glyphs + num_remaining, num_glyphs * sizeof (PangoGlyphInfo));
121       memcpy (new->glyphs->log_clusters, orig->glyphs->log_clusters + num_remaining, num_glyphs * sizeof (int));
122
123       for (i = 0; i < num_remaining; i++)
124         orig->glyphs->log_clusters[i] = orig->glyphs->log_clusters[i] - split_index;
125     }
126
127   pango_glyph_string_set_size (orig->glyphs, orig->glyphs->num_glyphs - num_glyphs);
128
129   return new;
130 }
131
132 /**
133  * pango_glyph_item_copy:
134  * @orig: a #PangoGlyphItem, may be %NULL
135  *
136  * Make a deep copy of an existing #PangoGlyphItem structure.
137  *
138  * Return value: the newly allocated #PangoGlyphItem, which should
139  *               be freed with pango_glyph_item_free(), or %NULL
140  *               if @orig was %NULL.
141  *
142  * Since: 1.20
143  **/
144 PangoGlyphItem *
145 pango_glyph_item_copy  (PangoGlyphItem *orig)
146 {
147   PangoGlyphItem *result;
148
149   if (orig == NULL)
150     return NULL;
151
152   result = g_slice_new (PangoGlyphItem);
153
154   result->item = pango_item_copy (orig->item);
155   result->glyphs = pango_glyph_string_copy (orig->glyphs);
156
157   return result;
158 }
159
160 /**
161  * pango_glyph_item_free:
162  * @glyph_item: a #PangoGlyphItem, may be %NULL
163  *
164  * Frees a #PangoGlyphItem and resources to which it points.
165  *
166  * Since: 1.6
167  **/
168 void
169 pango_glyph_item_free  (PangoGlyphItem *glyph_item)
170 {
171   if (glyph_item == NULL)
172     return;
173
174   if (glyph_item->item)
175     pango_item_free (glyph_item->item);
176   if (glyph_item->glyphs)
177     pango_glyph_string_free (glyph_item->glyphs);
178
179   g_slice_free (PangoGlyphItem, glyph_item);
180 }
181
182 GType
183 pango_glyph_item_get_type (void)
184 {
185   static GType our_type = 0;
186
187   if (G_UNLIKELY (our_type == 0))
188     our_type = g_boxed_type_register_static (I_("PangoGlyphItem"),
189                                              (GBoxedCopyFunc) pango_glyph_item_copy,
190                                              (GBoxedFreeFunc) pango_glyph_item_free);
191   return our_type;
192 }
193
194
195 /**
196  * pango_glyph_item_iter_copy:
197  * @orig: a #PangoGlyphItemIter, may be %NULL
198  *
199  * Make a shallow copy of an existing #PangoGlyphItemIter structure.
200  *
201  * Return value: the newly allocated #PangoGlyphItemIter, which should
202  *               be freed with pango_glyph_item_iter_free(), or %NULL
203  *               if @orig was %NULL.
204  *
205  * Since: 1.22
206  **/
207 PangoGlyphItemIter *
208 pango_glyph_item_iter_copy  (PangoGlyphItemIter *orig)
209 {
210   PangoGlyphItemIter *result;
211
212   if (orig == NULL)
213     return NULL;
214
215   result = g_slice_new (PangoGlyphItemIter);
216
217   *result = *orig;
218
219   return result;
220 }
221
222 /**
223  * pango_glyph_item_iter_free:
224  * @iter: a #PangoGlyphItemIter, may be %NULL
225  *
226  * Frees a #PangoGlyphItemIter created by pango_glyph_item_iter_copy().
227  *
228  * Since: 1.22
229  **/
230 void
231 pango_glyph_item_iter_free  (PangoGlyphItemIter *iter)
232 {
233   if (iter == NULL)
234     return;
235
236   g_slice_free (PangoGlyphItemIter, iter);
237 }
238
239 GType
240 pango_glyph_item_iter_get_type (void)
241 {
242   static GType our_type = 0;
243
244   if (G_UNLIKELY (our_type == 0))
245     our_type = g_boxed_type_register_static (I_("PangoGlyphItemIter"),
246                                              (GBoxedCopyFunc) pango_glyph_item_iter_copy,
247                                              (GBoxedFreeFunc) pango_glyph_item_iter_free);
248   return our_type;
249 }
250
251 /**
252  * pango_glyph_item_iter_next_cluster:
253  * @iter: a #PangoGlyphItemIter
254  *
255  * Advances the iterator to the next cluster in the glyph item.
256  * See #PangoGlyphItemIter for details of cluster orders.
257  *
258  * Return value: %TRUE if the iterator was advanced, %FALSE if we were already on the
259  *  last cluster.
260  *
261  * Since: 1.22
262  **/
263 gboolean
264 pango_glyph_item_iter_next_cluster (PangoGlyphItemIter *iter)
265 {
266   int glyph_index = iter->end_glyph;
267   PangoGlyphString *glyphs = iter->glyph_item->glyphs;
268   int cluster;
269   PangoItem *item = iter->glyph_item->item;
270
271   if (LTR (iter->glyph_item))
272     {
273       if (glyph_index == glyphs->num_glyphs)
274         return FALSE;
275     }
276   else
277     {
278       if (glyph_index < 0)
279         return FALSE;
280     }
281
282   iter->start_glyph = iter->end_glyph;
283   iter->start_index = iter->end_index;
284   iter->start_char = iter->end_char;
285
286   if (LTR (iter->glyph_item))
287     {
288       cluster = glyphs->log_clusters[glyph_index];
289       while (TRUE)
290         {
291           glyph_index++;
292
293           if (glyph_index == glyphs->num_glyphs)
294             {
295               iter->end_index = item->offset + item->length;
296               iter->end_char = item->num_chars;
297               break;
298             }
299
300           if (glyphs->log_clusters[glyph_index] != cluster)
301             {
302               iter->end_index = item->offset + glyphs->log_clusters[glyph_index];
303               iter->end_char += pango_utf8_strlen (iter->text + iter->start_index,
304                                                iter->end_index - iter->start_index);
305               break;
306             }
307         }
308     }
309   else                  /* RTL */
310     {
311       cluster = glyphs->log_clusters[glyph_index];
312       while (TRUE)
313         {
314           glyph_index--;
315
316           if (glyph_index < 0)
317             {
318               iter->end_index = item->offset + item->length;
319               iter->end_char = item->num_chars;
320               break;
321             }
322
323           if (glyphs->log_clusters[glyph_index] != cluster)
324             {
325               iter->end_index = item->offset + glyphs->log_clusters[glyph_index];
326               iter->end_char += pango_utf8_strlen (iter->text + iter->start_index,
327                                                iter->end_index - iter->start_index);
328               break;
329             }
330         }
331     }
332
333   iter->end_glyph = glyph_index;
334   return TRUE;
335 }
336
337 /**
338  * pango_glyph_item_iter_prev_cluster:
339  * @iter: a #PangoGlyphItemIter
340  *
341  * Moves the iterator to the preceding cluster in the glyph item.
342  * See #PangoGlyphItemIter for details of cluster orders.
343  *
344  * Return value: %TRUE if the iterator was moved, %FALSE if we were already on the
345  *  first cluster.
346  *
347  * Since: 1.22
348  **/
349 gboolean
350 pango_glyph_item_iter_prev_cluster (PangoGlyphItemIter *iter)
351 {
352   int glyph_index = iter->start_glyph;
353   PangoGlyphString *glyphs = iter->glyph_item->glyphs;
354   int cluster;
355   PangoItem *item = iter->glyph_item->item;
356
357   if (LTR (iter->glyph_item))
358     {
359       if (glyph_index == 0)
360         return FALSE;
361     }
362   else
363     {
364       if (glyph_index == glyphs->num_glyphs - 1)
365         return FALSE;
366
367     }
368
369   iter->end_glyph = iter->start_glyph;
370   iter->end_index = iter->start_index;
371   iter->end_char = iter->start_char;
372
373   if (LTR (iter->glyph_item))
374     {
375       cluster = glyphs->log_clusters[glyph_index - 1];
376       while (TRUE)
377         {
378           glyph_index--;
379
380           if (glyph_index == 0)
381             {
382               iter->start_index = item->offset;
383               iter->start_char = 0;
384               break;
385             }
386
387           if (glyphs->log_clusters[glyph_index] != cluster)
388             {
389               glyph_index++;
390               iter->start_index = item->offset + glyphs->log_clusters[glyph_index];
391               iter->start_char -= pango_utf8_strlen (iter->text + iter->start_index,
392                                                  iter->end_index - iter->start_index);
393               break;
394             }
395         }
396     }
397   else                  /* RTL */
398     {
399       cluster = glyphs->log_clusters[glyph_index + 1];
400       while (TRUE)
401         {
402           glyph_index++;
403
404           if (glyph_index == glyphs->num_glyphs - 1)
405             {
406               iter->start_index = item->offset;
407               iter->start_char = 0;
408               break;
409             }
410
411           if (glyphs->log_clusters[glyph_index] != cluster)
412             {
413               glyph_index--;
414               iter->start_index = item->offset + glyphs->log_clusters[glyph_index];
415               iter->start_char -= pango_utf8_strlen (iter->text + iter->start_index,
416                                                  iter->end_index - iter->start_index);
417               break;
418             }
419         }
420     }
421
422   iter->start_glyph = glyph_index;
423   return TRUE;
424 }
425
426 /**
427  * pango_glyph_item_iter_init_start:
428  * @iter: a #PangoGlyphItemIter
429  * @glyph_item: the glyph item to iterate over
430  * @text: text corresponding to the glyph item
431  *
432  * Initializes a #PangoGlyphItemIter structure to point to the
433  * first cluster in a glyph item.
434  * See #PangoGlyphItemIter for details of cluster orders.
435  *
436  * Return value: %FALSE if there are no clusters in the glyph item
437  *
438  * Since: 1.22
439  **/
440 gboolean
441 pango_glyph_item_iter_init_start (PangoGlyphItemIter  *iter,
442                                   PangoGlyphItem      *glyph_item,
443                                   const char          *text)
444 {
445   iter->glyph_item = glyph_item;
446   iter->text = text;
447
448   if (LTR (glyph_item))
449     iter->end_glyph = 0;
450   else
451     iter->end_glyph = glyph_item->glyphs->num_glyphs - 1;
452
453   iter->end_index = glyph_item->item->offset;
454   iter->end_char = 0;
455
456   iter->start_glyph = iter->end_glyph;
457   iter->start_index = iter->end_index;
458   iter->start_char = iter->end_char;
459
460   /* Advance onto the first cluster of the glyph item */
461   return pango_glyph_item_iter_next_cluster (iter);
462 }
463
464 /**
465  * pango_glyph_item_iter_init_end:
466  * @iter: a #PangoGlyphItemIter
467  * @glyph_item: the glyph item to iterate over
468  * @text: text corresponding to the glyph item
469  *
470  * Initializes a #PangoGlyphItemIter structure to point to the
471  * last cluster in a glyph item.
472  * See #PangoGlyphItemIter for details of cluster orders.
473  *
474  * Return value: %FALSE if there are no clusters in the glyph item
475  *
476  * Since: 1.22
477  **/
478 gboolean
479 pango_glyph_item_iter_init_end (PangoGlyphItemIter  *iter,
480                                 PangoGlyphItem      *glyph_item,
481                                 const char          *text)
482 {
483   iter->glyph_item = glyph_item;
484   iter->text = text;
485
486   if (LTR (glyph_item))
487     iter->start_glyph = glyph_item->glyphs->num_glyphs;
488   else
489     iter->start_glyph = -1;
490
491   iter->start_index = glyph_item->item->offset + glyph_item->item->length;
492   iter->start_char = glyph_item->item->num_chars;
493
494   iter->end_glyph = iter->start_glyph;
495   iter->end_index = iter->start_index;
496   iter->end_char = iter->start_char;
497
498   /* Advance onto the first cluster of the glyph item */
499   return pango_glyph_item_iter_prev_cluster (iter);
500 }
501
502 typedef struct
503 {
504   PangoGlyphItemIter iter;
505
506   GSList *segment_attrs;
507 } ApplyAttrsState;
508
509 /* Tack @attrs onto the attributes of glyph_item
510  */
511 static void
512 append_attrs (PangoGlyphItem *glyph_item,
513               GSList         *attrs)
514 {
515   glyph_item->item->analysis.extra_attrs =
516     g_slist_concat (glyph_item->item->analysis.extra_attrs, attrs);
517 }
518
519 /* Make a deep copy of a #GSList of PangoAttribute
520  */
521 static GSList *
522 attr_slist_copy (GSList *attrs)
523 {
524   GSList *tmp_list;
525   GSList *new_attrs;
526
527   new_attrs = g_slist_copy (attrs);
528
529   for (tmp_list = new_attrs; tmp_list; tmp_list = tmp_list->next)
530     tmp_list->data = pango_attribute_copy (tmp_list->data);
531
532   return new_attrs;
533 }
534
535 /* Split the glyph item at the start of the current cluster
536  */
537 static PangoGlyphItem *
538 split_before_cluster_start (ApplyAttrsState *state)
539 {
540   PangoGlyphItem *split_item;
541   int split_len = state->iter.start_index - state->iter.glyph_item->item->offset;
542
543   split_item = pango_glyph_item_split (state->iter.glyph_item, state->iter.text, split_len);
544   append_attrs (split_item, state->segment_attrs);
545
546   /* Adjust iteration to account for the split
547    */
548   if (LTR (state->iter.glyph_item))
549     {
550       state->iter.start_glyph -= split_item->glyphs->num_glyphs;
551       state->iter.end_glyph -= split_item->glyphs->num_glyphs;
552     }
553
554   state->iter.start_char -= split_item->item->num_chars;
555   state->iter.end_char -= split_item->item->num_chars;
556
557   return split_item;
558 }
559
560 /**
561  * pango_glyph_item_apply_attrs:
562  * @glyph_item: a shaped item
563  * @text: text that @list applies to
564  * @list: a #PangoAttrList
565  *
566  * Splits a shaped item (PangoGlyphItem) into multiple items based
567  * on an attribute list. The idea is that if you have attributes
568  * that don't affect shaping, such as color or underline, to avoid
569  * affecting shaping, you filter them out (pango_attr_list_filter()),
570  * apply the shaping process and then reapply them to the result using
571  * this function.
572  *
573  * All attributes that start or end inside a cluster are applied
574  * to that cluster; for instance, if half of a cluster is underlined
575  * and the other-half strikethrough, then the cluster will end
576  * up with both underline and strikethrough attributes. In these
577  * cases, it may happen that item->extra_attrs for some of the
578  * result items can have multiple attributes of the same type.
579  *
580  * This function takes ownership of @glyph_item; it will be reused
581  * as one of the elements in the list.
582  *
583  * Return value: a list of glyph items resulting from splitting
584  *   @glyph_item. Free the elements using pango_glyph_item_free(),
585  *   the list using g_slist_free().
586  *
587  * Since: 1.2
588  **/
589 GSList *
590 pango_glyph_item_apply_attrs (PangoGlyphItem   *glyph_item,
591                               const char       *text,
592                               PangoAttrList    *list)
593 {
594   PangoAttrIterator *iter = pango_attr_list_get_iterator (list);
595   GSList *result = NULL;
596   ApplyAttrsState state;
597   gboolean start_new_segment = FALSE;
598   gboolean have_cluster;
599   int range_start, range_end;
600
601   /* This routine works by iterating through the item cluster by
602    * cluster; we accumulate the attributes that we need to
603    * add to the next output item, and decide when to split
604    * off an output item based on two criteria:
605    *
606    * A) If start_index < attribute_start < end_index
607    *    (attribute starts within cluster) then we need
608    *    to split between the last cluster and this cluster.
609    * B) If start_index < attribute_end <= end_index,
610    *    (attribute ends within cluster) then we need to
611    *    split between this cluster and the next one.
612    */
613
614   /* Advance the attr iterator to the start of the item
615    */
616   do
617     {
618       pango_attr_iterator_range (iter, &range_start, &range_end);
619       if (range_end > glyph_item->item->offset)
620         break;
621     }
622   while (pango_attr_iterator_next (iter));
623
624   state.segment_attrs = pango_attr_iterator_get_attrs (iter);
625
626   /* Short circuit the case when we don't actually need to
627    * split the item
628    */
629   if (range_start <= glyph_item->item->offset &&
630       range_end >= glyph_item->item->offset + glyph_item->item->length)
631     goto out;
632
633   for (have_cluster = pango_glyph_item_iter_init_start (&state.iter, glyph_item, text);
634        have_cluster;
635        have_cluster = pango_glyph_item_iter_next_cluster (&state.iter))
636     {
637       gboolean have_next;
638
639       /* [range_start,range_end] is the first range that intersects
640        * the current cluster.
641        */
642
643       /* Split item into two, if this cluster isn't a continuation
644        * of the last cluster
645        */
646       if (start_new_segment)
647         {
648           result = g_slist_prepend (result,
649                                     split_before_cluster_start (&state));
650           state.segment_attrs = pango_attr_iterator_get_attrs (iter);
651         }
652
653       start_new_segment = FALSE;
654
655       /* Loop over all ranges that intersect this cluster; exiting
656        * leaving [range_start,range_end] being the first range that
657        * intersects the next cluster.
658        */
659       do
660         {
661           if (range_end > state.iter.end_index) /* Range intersects next cluster */
662             break;
663
664           /* Since ranges end in this cluster, the next cluster goes into a
665            * separate segment
666            */
667           start_new_segment = TRUE;
668
669           have_next = pango_attr_iterator_next (iter);
670           pango_attr_iterator_range (iter, &range_start, &range_end);
671
672           if (range_start >= state.iter.end_index) /* New range doesn't intersect this cluster */
673             {
674               /* No gap between ranges, so previous range must of ended
675                * at cluster boundary.
676                */
677               g_assert (range_start == state.iter.end_index && start_new_segment);
678               break;
679             }
680
681           /* If any ranges start *inside* this cluster, then we need
682            * to split the previous cluster into a separate segment
683            */
684           if (range_start > state.iter.start_index &&
685               state.iter.start_index != glyph_item->item->offset)
686             {
687               GSList *new_attrs = attr_slist_copy (state.segment_attrs);
688               result = g_slist_prepend (result,
689                                         split_before_cluster_start (&state));
690               state.segment_attrs = new_attrs;
691             }
692
693           state.segment_attrs = g_slist_concat (state.segment_attrs,
694                                                 pango_attr_iterator_get_attrs (iter));
695         }
696       while (have_next);
697     }
698
699  out:
700   /* What's left in glyph_item is the remaining portion
701    */
702   append_attrs (glyph_item, state.segment_attrs);
703   result = g_slist_prepend (result, glyph_item);
704
705   if (LTR (glyph_item))
706     result = g_slist_reverse (result);
707
708   pango_attr_iterator_destroy (iter);
709
710   return result;
711 }
712
713 /**
714  * pango_glyph_item_letter_space:
715  * @glyph_item: a #PangoGlyphItem
716  * @text: text that @glyph_item corresponds to
717  *   (glyph_item->item->offset is an offset from the
718  *    start of @text)
719  * @log_attrs: logical attributes for the item (the
720  *   first logical attribute refers to the position
721  *   before the first character in the item)
722  * @letter_spacing: amount of letter spacing to add
723  *   in Pango units. May be negative, though too large
724  *   negative values will give ugly results.
725  *
726  * Adds spacing between the graphemes of @glyph_item to
727  * give the effect of typographic letter spacing.
728  *
729  * Since: 1.6
730  **/
731 void
732 pango_glyph_item_letter_space (PangoGlyphItem *glyph_item,
733                                const char     *text,
734                                PangoLogAttr   *log_attrs,
735                                int             letter_spacing)
736 {
737   PangoGlyphItemIter iter;
738   PangoGlyphInfo *glyphs = glyph_item->glyphs->glyphs;
739   gboolean have_cluster;
740   int space_left, space_right;
741
742   space_left = letter_spacing / 2;
743
744   /* hinting */
745   if ((letter_spacing & (PANGO_SCALE - 1)) == 0)
746     {
747       space_left = PANGO_UNITS_ROUND (space_left);
748     }
749
750   space_right = letter_spacing - space_left;
751
752   for (have_cluster = pango_glyph_item_iter_init_start (&iter, glyph_item, text);
753        have_cluster;
754        have_cluster = pango_glyph_item_iter_next_cluster (&iter))
755     {
756       if (!log_attrs[iter.start_char].is_cursor_position)
757         continue;
758
759       if (iter.start_glyph < iter.end_glyph) /* LTR */
760         {
761           if (iter.start_char > 0)
762             {
763               glyphs[iter.start_glyph].geometry.width    += space_left ;
764               glyphs[iter.start_glyph].geometry.x_offset += space_left ;
765             }
766           if (iter.end_char < glyph_item->item->num_chars)
767             {
768               glyphs[iter.end_glyph-1].geometry.width    += space_right;
769             }
770         }
771       else                                       /* RTL */
772         {
773           if (iter.start_char > 0)
774             {
775               glyphs[iter.start_glyph].geometry.width    += space_right;
776             }
777           if (iter.end_char < glyph_item->item->num_chars)
778             {
779               glyphs[iter.end_glyph+1].geometry.x_offset += space_left ;
780               glyphs[iter.end_glyph+1].geometry.width    += space_left ;
781             }
782         }
783     }
784 }
785
786 /**
787  * pango_glyph_item_get_logical_widths:
788  * @glyph_item: a #PangoGlyphItem
789  * @text: text that @glyph_item corresponds to
790  *   (glyph_item->item->offset is an offset from the
791  *    start of @text)
792  * @logical_widths: an array whose length is the number of characters in
793  *                  glyph_item (equal to glyph_item->item->num_chars)
794  *                  to be filled in with the resulting character widths.
795  *
796  * Given a #PangoGlyphItem and the corresponding
797  * text, determine the screen width corresponding to each character. When
798  * multiple characters compose a single cluster, the width of the entire
799  * cluster is divided equally among the characters.
800  *
801  * See also pango_glyph_string_get_logical_widths().
802  *
803  * Since: 1.26
804  **/
805 void
806 pango_glyph_item_get_logical_widths (PangoGlyphItem *glyph_item,
807                                      const char     *text,
808                                      int            *logical_widths)
809 {
810   PangoGlyphItemIter iter;
811   gboolean has_cluster;
812   int dir;
813
814   dir = glyph_item->item->analysis.level % 2 == 0 ? +1 : -1;
815   for (has_cluster = pango_glyph_item_iter_init_start (&iter, glyph_item, text);
816        has_cluster;
817        has_cluster = pango_glyph_item_iter_next_cluster (&iter))
818     {
819       int glyph_index, char_index, num_chars, cluster_width = 0, char_width;
820
821       for (glyph_index  = iter.start_glyph;
822            glyph_index != iter.end_glyph;
823            glyph_index += dir)
824         {
825           cluster_width += glyph_item->glyphs->glyphs[glyph_index].geometry.width;
826         }
827
828       num_chars = iter.end_char - iter.start_char;
829       if (num_chars) /* pedantic */
830         {
831           char_width = cluster_width / num_chars;
832
833           for (char_index = iter.start_char;
834                char_index < iter.end_char;
835                char_index++)
836             {
837               logical_widths[char_index] = char_width;
838             }
839
840           /* add any residues to the first char */
841           logical_widths[iter.start_char] += cluster_width - (char_width * num_chars);
842         }
843     }
844 }