Git init
[external/pango1.0.git] / pango / shape.c
1 /* Pango
2  * shape.c: Convert characters into glyphs.
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
24 #include "pango-impl-utils.h"
25 #include "pango-glyph.h"
26 #include "pango-engine-private.h"
27
28 #include <string.h>
29
30 /**
31  * pango_shape:
32  * @text:      the text to process
33  * @length:    the length (in bytes) of @text
34  * @analysis:  #PangoAnalysis structure from pango_itemize()
35  * @glyphs:    glyph string in which to store results
36  *
37  * Given a segment of text and the corresponding
38  * #PangoAnalysis structure returned from pango_itemize(),
39  * convert the characters into glyphs. You may also pass
40  * in only a substring of the item from pango_itemize().
41  */
42 void
43 pango_shape (const gchar      *text,
44              gint              length,
45              const PangoAnalysis *analysis,
46              PangoGlyphString *glyphs)
47 {
48   int i;
49   int last_cluster;
50
51   glyphs->num_glyphs = 0;
52
53   if (G_LIKELY (analysis->shape_engine && analysis->font))
54     {
55       _pango_engine_shape_shape (analysis->shape_engine, analysis->font,
56                                  text, length, analysis, glyphs);
57
58       if (G_UNLIKELY (glyphs->num_glyphs == 0))
59         {
60           /* If a font has been correctly chosen, but no glyphs are output,
61            * there's probably something wrong with the shaper, or the font.
62            *
63            * Trying to be informative, we print out the font description,
64            * shaper name, and the text, but to not flood the terminal with
65            * zillions of the message, we set a flag to only err once per
66            * font/engine pair.
67            *
68            * To do the flag fast, we use the engine qname to qflag the font,
69            * but also the font description to flag the engine.  This is
70            * supposed to be fast to check, but also avoid writing out
71            * duplicate warnings when a new PangoFont is created.
72            */
73           GType engine_type = G_OBJECT_TYPE (analysis->shape_engine);
74           GQuark warned_quark = g_type_qname (engine_type);
75
76           if (!g_object_get_qdata (G_OBJECT (analysis->font), warned_quark))
77             {
78               PangoFontDescription *desc;
79               char *font_name;
80               const char *engine_name;
81
82               desc = pango_font_describe (analysis->font);
83               font_name = pango_font_description_to_string (desc);
84               pango_font_description_free (desc);
85
86               if (!g_object_get_data (G_OBJECT (analysis->shape_engine), font_name))
87                 {
88                   engine_name = g_type_name (engine_type);
89                   if (!engine_name)
90                     engine_name = "(unknown)";
91
92                   g_warning ("shaping failure, expect ugly output. shape-engine='%s', font='%s', text='%.*s'",
93                              engine_name,
94                              font_name,
95                              length == -1 ? (gint) strlen (text) : length, text);
96
97                   g_object_set_data_full (G_OBJECT (analysis->shape_engine), font_name,
98                                           GINT_TO_POINTER (1), NULL);
99                 }
100
101               g_free (font_name);
102
103               g_object_set_qdata_full (G_OBJECT (analysis->font), warned_quark,
104                                        GINT_TO_POINTER (1), NULL);
105             }
106         }
107     }
108   else
109     glyphs->num_glyphs = 0;
110
111   if (G_UNLIKELY (!glyphs->num_glyphs))
112     {
113       PangoEngineShape *fallback_engine = _pango_get_fallback_shaper ();
114
115       _pango_engine_shape_shape (fallback_engine, analysis->font,
116                                  text, length, analysis, glyphs);
117       if (G_UNLIKELY (!glyphs->num_glyphs))
118         return;
119     }
120
121   /* make sure last_cluster is invalid */
122   last_cluster = glyphs->log_clusters[0] - 1;
123   for (i = 0; i < glyphs->num_glyphs; i++)
124     {
125       /* Set glyphs[i].attr.is_cluster_start based on log_clusters[] */
126       if (glyphs->log_clusters[i] != last_cluster)
127         {
128           glyphs->glyphs[i].attr.is_cluster_start = TRUE;
129           last_cluster = glyphs->log_clusters[i];
130         }
131       else
132         glyphs->glyphs[i].attr.is_cluster_start = FALSE;
133
134
135       /* Shift glyph if width is negative, and negate width.
136        * This is useful for rotated font matrices and shouldn't
137        * harm in normal cases.
138        */
139       if (glyphs->glyphs[i].geometry.width < 0)
140         {
141           glyphs->glyphs[i].geometry.width = -glyphs->glyphs[i].geometry.width;
142           glyphs->glyphs[i].geometry.x_offset += glyphs->glyphs[i].geometry.width;
143         }
144     }
145
146   /* Make sure glyphstring direction conforms to analysis->level */
147   if (G_UNLIKELY ((analysis->level & 1) &&
148                   glyphs->log_clusters[0] < glyphs->log_clusters[glyphs->num_glyphs - 1]))
149     {
150       /* Warn once per shaper */
151       static GQuark warned_quark = 0;
152
153       if (!warned_quark)
154         warned_quark = g_quark_from_static_string ("pango-shape-warned");
155
156       if (analysis->shape_engine && !g_object_get_qdata (G_OBJECT (analysis->shape_engine), warned_quark))
157         {
158           GType engine_type = G_OBJECT_TYPE (analysis->shape_engine);
159           const char *engine_name = g_type_name (engine_type);
160           if (!engine_name)
161             engine_name = "(unknown)";
162
163           g_warning ("Expected RTL run but shape-engine='%s' returned LTR. Fixing.", engine_name);
164
165           g_object_set_qdata_full (G_OBJECT (analysis->shape_engine), warned_quark,
166                                    GINT_TO_POINTER (1), NULL);
167         }
168
169       /* *Fix* it so we don't crash later */
170       pango_glyph_string_reverse_range (glyphs, 0, glyphs->num_glyphs);
171     }
172 }