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