Git init
[external/pango1.0.git] / pango / glyphstring.c
1 /* Pango
2  * glyphstring.c:
3  *
4  * Copyright (C) 1999 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 <glib.h>
24 #include "pango-glyph.h"
25 #include "pango-font.h"
26 #include "pango-impl-utils.h"
27
28 /**
29  * pango_glyph_string_new:
30  *
31  * Create a new #PangoGlyphString.
32  *
33  * Return value: the newly allocated #PangoGlyphString, which
34  *               should be freed with pango_glyph_string_free().
35  */
36 PangoGlyphString *
37 pango_glyph_string_new (void)
38 {
39   PangoGlyphString *string = g_slice_new (PangoGlyphString);
40
41   string->num_glyphs = 0;
42   string->space = 0;
43   string->glyphs = NULL;
44   string->log_clusters = NULL;
45
46   return string;
47 }
48
49 /**
50  * pango_glyph_string_set_size:
51  * @string:    a #PangoGlyphString.
52  * @new_len:   the new length of the string.
53  *
54  * Resize a glyph string to the given length.
55  */
56 void
57 pango_glyph_string_set_size (PangoGlyphString *string, gint new_len)
58 {
59   g_return_if_fail (new_len >= 0);
60
61   while (new_len > string->space)
62     {
63       if (string->space == 0)
64         {
65           string->space = 4;
66         }
67       else
68         {
69           const guint max_space =
70             MIN (G_MAXINT, G_MAXSIZE / MAX (sizeof(PangoGlyphInfo), sizeof(gint)));
71
72           guint more_space = (guint)string->space * 2;
73
74           if (more_space > max_space)
75             {
76               more_space = max_space;
77
78               if ((guint)new_len > max_space)
79                 {
80                   g_error ("%s: failed to allocate glyph string of length %i\n",
81                            G_STRLOC, new_len);
82                 }
83             }
84
85           string->space = more_space;
86         }
87     }
88
89   string->glyphs = g_realloc (string->glyphs, string->space * sizeof (PangoGlyphInfo));
90   string->log_clusters = g_realloc (string->log_clusters, string->space * sizeof (gint));
91   string->num_glyphs = new_len;
92 }
93
94 GType
95 pango_glyph_string_get_type (void)
96 {
97   static GType our_type = 0;
98
99   if (G_UNLIKELY (our_type == 0))
100     our_type = g_boxed_type_register_static (I_("PangoGlyphString"),
101                                              (GBoxedCopyFunc)pango_glyph_string_copy,
102                                              (GBoxedFreeFunc)pango_glyph_string_free);
103
104   return our_type;
105 }
106
107 /**
108  * pango_glyph_string_copy:
109  * @string: a #PangoGlyphString, may be %NULL
110  *
111  * Copy a glyph string and associated storage.
112  *
113  * Return value: the newly allocated #PangoGlyphString, which
114  *               should be freed with pango_glyph_string_free(),
115  *               or %NULL if @string was %NULL.
116  */
117 PangoGlyphString *
118 pango_glyph_string_copy (PangoGlyphString *string)
119 {
120   PangoGlyphString *new_string;
121
122   if (string == NULL)
123     return NULL;
124   
125   new_string = g_slice_new (PangoGlyphString);
126
127   *new_string = *string;
128
129   new_string->glyphs = g_memdup (string->glyphs,
130                                  string->space * sizeof (PangoGlyphInfo));
131   new_string->log_clusters = g_memdup (string->log_clusters,
132                                        string->space * sizeof (gint));
133
134   return new_string;
135 }
136
137 /**
138  * pango_glyph_string_free:
139  * @string: a #PangoGlyphString, may be %NULL
140  *
141  * Free a glyph string and associated storage.
142  */
143 void
144 pango_glyph_string_free (PangoGlyphString *string)
145 {
146   if (string == NULL)
147     return;
148
149   g_free (string->glyphs);
150   g_free (string->log_clusters);
151   g_slice_free (PangoGlyphString, string);
152 }
153
154 /**
155  * pango_glyph_string_extents_range:
156  * @glyphs:   a #PangoGlyphString
157  * @start:    start index
158  * @end:      end index (the range is the set of bytes with
159               indices such that start <= index < end)
160  * @font:     a #PangoFont
161  * @ink_rect: rectangle used to store the extents of the glyph string range as drawn
162  *            or %NULL to indicate that the result is not needed.
163  * @logical_rect: rectangle used to store the logical extents of the glyph string range
164  *            or %NULL to indicate that the result is not needed.
165  *
166  * Computes the extents of a sub-portion of a glyph string. The extents are
167  * relative to the start of the glyph string range (the origin of their
168  * coordinate system is at the start of the range, not at the start of the entire
169  * glyph string).
170  **/
171 void
172 pango_glyph_string_extents_range (PangoGlyphString *glyphs,
173                                   int               start,
174                                   int               end,
175                                   PangoFont        *font,
176                                   PangoRectangle   *ink_rect,
177                                   PangoRectangle   *logical_rect)
178 {
179   int x_pos = 0;
180   int i;
181
182   /* Note that the handling of empty rectangles for ink
183    * and logical rectangles is different. A zero-height ink
184    * rectangle makes no contribution to the overall ink rect,
185    * while a zero-height logical rect still reserves horizontal
186    * width. Also, we may return zero-width, positive height
187    * logical rectangles, while we'll never do that for the
188    * ink rect.
189    */
190   g_return_if_fail (start <= end);
191
192   if (G_UNLIKELY (!ink_rect && !logical_rect))
193     return;
194
195   if (ink_rect)
196     {
197       ink_rect->x = 0;
198       ink_rect->y = 0;
199       ink_rect->width = 0;
200       ink_rect->height = 0;
201     }
202
203   if (logical_rect)
204     {
205       logical_rect->x = 0;
206       logical_rect->y = 0;
207       logical_rect->width = 0;
208       logical_rect->height = 0;
209     }
210
211   for (i = start; i < end; i++)
212     {
213       PangoRectangle glyph_ink;
214       PangoRectangle glyph_logical;
215
216       PangoGlyphGeometry *geometry = &glyphs->glyphs[i].geometry;
217
218       pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph,
219                                     ink_rect ? &glyph_ink : NULL,
220                                     logical_rect ? &glyph_logical : NULL);
221
222       if (ink_rect && glyph_ink.width != 0 && glyph_ink.height != 0)
223         {
224           if (ink_rect->width == 0 || ink_rect->height == 0)
225             {
226               ink_rect->x = x_pos + glyph_ink.x + geometry->x_offset;
227               ink_rect->width = glyph_ink.width;
228               ink_rect->y = glyph_ink.y + geometry->y_offset;
229               ink_rect->height = glyph_ink.height;
230             }
231           else
232             {
233               int new_x, new_y;
234
235               new_x = MIN (ink_rect->x, x_pos + glyph_ink.x + geometry->x_offset);
236               ink_rect->width = MAX (ink_rect->x + ink_rect->width,
237                                      x_pos + glyph_ink.x + glyph_ink.width + geometry->x_offset) - new_x;
238               ink_rect->x = new_x;
239
240               new_y = MIN (ink_rect->y, glyph_ink.y + geometry->y_offset);
241               ink_rect->height = MAX (ink_rect->y + ink_rect->height,
242                                       glyph_ink.y + glyph_ink.height + geometry->y_offset) - new_y;
243               ink_rect->y = new_y;
244             }
245         }
246
247       if (logical_rect)
248         {
249           logical_rect->width += geometry->width;
250
251           if (i == start)
252             {
253               logical_rect->y = glyph_logical.y;
254               logical_rect->height = glyph_logical.height;
255             }
256           else
257             {
258               int new_y = MIN (logical_rect->y, glyph_logical.y);
259               logical_rect->height = MAX (logical_rect->y + logical_rect->height,
260                                           glyph_logical.y + glyph_logical.height) - new_y;
261               logical_rect->y = new_y;
262             }
263         }
264
265       x_pos += geometry->width;
266     }
267 }
268
269 /**
270  * pango_glyph_string_extents:
271  * @glyphs:   a #PangoGlyphString
272  * @font:     a #PangoFont
273  * @ink_rect: rectangle used to store the extents of the glyph string as drawn
274  *            or %NULL to indicate that the result is not needed.
275  * @logical_rect: rectangle used to store the logical extents of the glyph string
276  *            or %NULL to indicate that the result is not needed.
277  *
278  * Compute the logical and ink extents of a glyph string. See the documentation
279  * for pango_font_get_glyph_extents() for details about the interpretation
280  * of the rectangles.
281  */
282 void
283 pango_glyph_string_extents (PangoGlyphString *glyphs,
284                             PangoFont        *font,
285                             PangoRectangle   *ink_rect,
286                             PangoRectangle   *logical_rect)
287 {
288   pango_glyph_string_extents_range (glyphs, 0, glyphs->num_glyphs,
289                                     font, ink_rect, logical_rect);
290 }
291
292 /**
293  * pango_glyph_string_get_width:
294  * @glyphs:   a #PangoGlyphString
295  *
296  * Computes the logical width of the glyph string as can also be computed
297  * using pango_glyph_string_extents().  However, since this only computes the
298  * width, it's much faster.  This is in fact only a convenience function that
299  * computes the sum of geometry.width for each glyph in the @glyphs.
300  *
301  * Return value: the logical width of the glyph string.
302  *
303  * Since: 1.14
304  */
305 int
306 pango_glyph_string_get_width (PangoGlyphString *glyphs)
307 {
308   int i;
309   int width = 0;
310
311   for (i = 0; i < glyphs->num_glyphs; i++)
312     width += glyphs->glyphs[i].geometry.width;
313
314   return width;
315 }
316
317 /**
318  * pango_glyph_string_get_logical_widths:
319  * @glyphs: a #PangoGlyphString
320  * @text: the text corresponding to the glyphs
321  * @length: the length of @text, in bytes
322  * @embedding_level: the embedding level of the string
323  * @logical_widths: an array whose length is the number of characters in
324  *                  text (equal to g_utf8_strlen (text, length) unless
325  *                  text has NUL bytes)
326  *                  to be filled in with the resulting character widths.
327  *
328  * Given a #PangoGlyphString resulting from pango_shape() and the corresponding
329  * text, determine the screen width corresponding to each character. When
330  * multiple characters compose a single cluster, the width of the entire
331  * cluster is divided equally among the characters.
332  *
333  * See also pango_glyph_item_get_logical_widths().
334  **/
335 void
336 pango_glyph_string_get_logical_widths (PangoGlyphString *glyphs,
337                                        const char       *text,
338                                        int               length,
339                                        int               embedding_level,
340                                        int              *logical_widths)
341 {
342   /* Build a PangoGlyphItem and call the other API */
343   PangoItem item = {0, length, pango_utf8_strlen (text, length),
344                     {NULL, NULL, NULL,
345                      embedding_level, PANGO_GRAVITY_AUTO, 0,
346                      PANGO_SCRIPT_UNKNOWN, NULL,
347                      NULL}};
348   PangoGlyphItem glyph_item = {&item, glyphs};
349
350   return pango_glyph_item_get_logical_widths (&glyph_item, text, logical_widths);
351 }
352
353 /* The initial implementation here is script independent,
354  * but it might actually need to be virtualized into the
355  * rendering modules. Otherwise, we probably will end up
356  * enforcing unnatural cursor behavior for some languages.
357  *
358  * The only distinction that Uniscript makes is whether
359  * cursor positioning is allowed within clusters or not.
360  */
361
362 /**
363  * pango_glyph_string_index_to_x:
364  * @glyphs:    the glyphs return from pango_shape()
365  * @text:      the text for the run
366  * @length:    the number of bytes (not characters) in @text.
367  * @analysis:  the analysis information return from pango_itemize()
368  * @index_:    the byte index within @text
369  * @trailing:  whether we should compute the result for the beginning (%FALSE)
370  *             or end (%TRUE) of the character.
371  * @x_pos:     location to store result
372  *
373  * Converts from character position to x position. (X position
374  * is measured from the left edge of the run). Character positions
375  * are computed by dividing up each cluster into equal portions.
376  */
377
378 void
379 pango_glyph_string_index_to_x (PangoGlyphString *glyphs,
380                                char             *text,
381                                int               length,
382                                PangoAnalysis    *analysis,
383                                int               index,
384                                gboolean          trailing,
385                                int              *x_pos)
386 {
387   int i;
388   int start_xpos = 0;
389   int end_xpos = 0;
390   int width = 0;
391
392   int start_index = -1;
393   int end_index = -1;
394
395   int cluster_chars = 0;
396   int cluster_offset = 0;
397
398   char *p;
399
400   g_return_if_fail (glyphs != NULL);
401   g_return_if_fail (length >= 0);
402   g_return_if_fail (length == 0 || text != NULL);
403
404   if (!x_pos) /* Allow the user to do the useless */
405     return;
406
407   if (glyphs->num_glyphs == 0)
408     {
409       *x_pos = 0;
410       return;
411     }
412
413   /* Calculate the starting and ending character positions
414    * and x positions for the cluster
415    */
416   if (analysis->level % 2) /* Right to left */
417     {
418       for (i = glyphs->num_glyphs - 1; i >= 0; i--)
419         width += glyphs->glyphs[i].geometry.width;
420
421       for (i = glyphs->num_glyphs - 1; i >= 0; i--)
422         {
423           if (glyphs->log_clusters[i] > index)
424             {
425               end_index = glyphs->log_clusters[i];
426               end_xpos = width;
427               break;
428             }
429
430           if (glyphs->log_clusters[i] != start_index)
431             {
432               start_index = glyphs->log_clusters[i];
433               start_xpos = width;
434             }
435
436           width -= glyphs->glyphs[i].geometry.width;
437         }
438     }
439   else /* Left to right */
440     {
441       for (i = 0; i < glyphs->num_glyphs; i++)
442         {
443           if (glyphs->log_clusters[i] > index)
444             {
445               end_index = glyphs->log_clusters[i];
446               end_xpos = width;
447               break;
448             }
449
450           if (glyphs->log_clusters[i] != start_index)
451             {
452               start_index = glyphs->log_clusters[i];
453               start_xpos = width;
454             }
455
456           width += glyphs->glyphs[i].geometry.width;
457         }
458     }
459
460   if (end_index == -1)
461     {
462       end_index = length;
463       end_xpos = (analysis->level % 2) ? 0 : width;
464     }
465
466   /* Calculate offset of character within cluster */
467
468   p = text + start_index;
469   while (p < text + end_index)
470     {
471       if (p < text + index)
472         cluster_offset++;
473       cluster_chars++;
474       p = g_utf8_next_char (p);
475     }
476
477   if (trailing)
478     cluster_offset += 1;
479
480   if (G_UNLIKELY (!cluster_chars)) /* pedantic */
481     {
482       *x_pos = start_xpos;
483       return;
484     }
485
486   *x_pos = ((cluster_chars - cluster_offset) * start_xpos +
487             cluster_offset * end_xpos) / cluster_chars;
488 }
489
490 /**
491  * pango_glyph_string_x_to_index:
492  * @glyphs:    the glyphs returned from pango_shape()
493  * @text:      the text for the run
494  * @length:    the number of bytes (not characters) in text.
495  * @analysis:  the analysis information return from pango_itemize()
496  * @x_pos:     the x offset (in Pango units)
497  * @index_:    location to store calculated byte index within @text
498  * @trailing:  location to store a boolean indicating
499  *             whether the user clicked on the leading or trailing
500  *             edge of the character.
501  *
502  * Convert from x offset to character position. Character positions
503  * are computed by dividing up each cluster into equal portions.
504  * In scripts where positioning within a cluster is not allowed
505  * (such as Thai), the returned value may not be a valid cursor
506  * position; the caller must combine the result with the logical
507  * attributes for the text to compute the valid cursor position.
508  */
509 void
510 pango_glyph_string_x_to_index (PangoGlyphString *glyphs,
511                                char             *text,
512                                int               length,
513                                PangoAnalysis    *analysis,
514                                int               x_pos,
515                                int              *index,
516                                gboolean         *trailing)
517 {
518   int i;
519   int start_xpos = 0;
520   int end_xpos = 0;
521   int width = 0;
522
523   int start_index = -1;
524   int end_index = -1;
525
526   int cluster_chars = 0;
527   char *p;
528
529   gboolean found = FALSE;
530
531   /* Find the cluster containing the position */
532
533   width = 0;
534
535   if (analysis->level % 2) /* Right to left */
536     {
537       for (i = glyphs->num_glyphs - 1; i >= 0; i--)
538         width += glyphs->glyphs[i].geometry.width;
539
540       for (i = glyphs->num_glyphs - 1; i >= 0; i--)
541         {
542           if (glyphs->log_clusters[i] != start_index)
543             {
544               if (found)
545                 {
546                   end_index = glyphs->log_clusters[i];
547                   end_xpos = width;
548                   break;
549                 }
550               else
551                 {
552                   start_index = glyphs->log_clusters[i];
553                   start_xpos = width;
554                 }
555             }
556
557           width -= glyphs->glyphs[i].geometry.width;
558
559           if (width <= x_pos && x_pos < width + glyphs->glyphs[i].geometry.width)
560             found = TRUE;
561         }
562     }
563   else /* Left to right */
564     {
565       for (i = 0; i < glyphs->num_glyphs; i++)
566         {
567           if (glyphs->log_clusters[i] != start_index)
568             {
569               if (found)
570                 {
571                   end_index = glyphs->log_clusters[i];
572                   end_xpos = width;
573                   break;
574                 }
575               else
576                 {
577                   start_index = glyphs->log_clusters[i];
578                   start_xpos = width;
579                 }
580             }
581
582           if (width <= x_pos && x_pos < width + glyphs->glyphs[i].geometry.width)
583             found = TRUE;
584
585           width += glyphs->glyphs[i].geometry.width;
586         }
587     }
588
589   if (end_index == -1)
590     {
591       end_index = length;
592       end_xpos = (analysis->level % 2) ? 0 : width;
593     }
594
595   /* Calculate number of chars within cluster */
596   p = text + start_index;
597   while (p < text + end_index)
598     {
599       p = g_utf8_next_char (p);
600       cluster_chars++;
601     }
602
603   if (start_xpos == end_xpos)
604     {
605       if (index)
606         *index = start_index;
607       if (trailing)
608         *trailing = FALSE;
609     }
610   else
611     {
612       double cp = ((double)(x_pos - start_xpos) * cluster_chars) / (end_xpos - start_xpos);
613
614       /* LTR and right-to-left have to be handled separately
615        * here because of the edge condition when we are exactly
616        * at a pixel boundary; end_xpos goes with the next
617        * character for LTR, with the previous character for RTL.
618        */
619       if (start_xpos < end_xpos) /* Left-to-right */
620         {
621           if (index)
622             {
623               char *p = text + start_index;
624               int i = 0;
625
626               while (i + 1 <= cp)
627                 {
628                   p = g_utf8_next_char (p);
629                   i++;
630                 }
631
632               *index = (p - text);
633             }
634
635           if (trailing)
636             *trailing = (cp - (int)cp >= 0.5) ? TRUE : FALSE;
637         }
638       else /* Right-to-left */
639         {
640           if (index)
641             {
642               char *p = text + start_index;
643               int i = 0;
644
645               while (i + 1 < cp)
646                 {
647                   p = g_utf8_next_char (p);
648                   i++;
649                 }
650
651               *index = (p - text);
652             }
653
654           if (trailing)
655             {
656               double cp_flip = cluster_chars - cp;
657               *trailing = (cp_flip - (int)cp_flip >= 0.5) ? FALSE : TRUE;
658             }
659         }
660     }
661 }