2 * shape.c: Convert characters into glyphs.
4 * Copyright (C) 1999 Red Hat Software
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.
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.
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.
24 #include "pango-impl-utils.h"
25 #include "pango-glyph.h"
26 #include "pango-engine-private.h"
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
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().
43 pango_shape (const gchar *text,
45 const PangoAnalysis *analysis,
46 PangoGlyphString *glyphs)
51 glyphs->num_glyphs = 0;
53 if (G_LIKELY (analysis->shape_engine && analysis->font))
55 _pango_engine_shape_shape (analysis->shape_engine, analysis->font,
56 text, length, analysis, glyphs);
58 if (G_UNLIKELY (glyphs->num_glyphs == 0))
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.
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
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.
73 GType engine_type = G_OBJECT_TYPE (analysis->shape_engine);
74 GQuark warned_quark = g_type_qname (engine_type);
76 if (!g_object_get_qdata (G_OBJECT (analysis->font), warned_quark))
78 PangoFontDescription *desc;
80 const char *engine_name;
82 desc = pango_font_describe (analysis->font);
83 font_name = pango_font_description_to_string (desc);
84 pango_font_description_free (desc);
86 if (!g_object_get_data (G_OBJECT (analysis->shape_engine), font_name))
88 engine_name = g_type_name (engine_type);
90 engine_name = "(unknown)";
92 g_warning ("shaping failure, expect ugly output. shape-engine='%s', font='%s', text='%.*s'",
95 length == -1 ? (gint) strlen (text) : length, text);
97 g_object_set_data_full (G_OBJECT (analysis->shape_engine), font_name,
98 GINT_TO_POINTER (1), NULL);
103 g_object_set_qdata_full (G_OBJECT (analysis->font), warned_quark,
104 GINT_TO_POINTER (1), NULL);
109 glyphs->num_glyphs = 0;
111 if (G_UNLIKELY (!glyphs->num_glyphs))
113 PangoEngineShape *fallback_engine = _pango_get_fallback_shaper ();
115 _pango_engine_shape_shape (fallback_engine, analysis->font,
116 text, length, analysis, glyphs);
117 if (G_UNLIKELY (!glyphs->num_glyphs))
121 /* make sure last_cluster is invalid */
122 last_cluster = glyphs->log_clusters[0] - 1;
123 for (i = 0; i < glyphs->num_glyphs; i++)
125 /* Set glyphs[i].attr.is_cluster_start based on log_clusters[] */
126 if (glyphs->log_clusters[i] != last_cluster)
128 glyphs->glyphs[i].attr.is_cluster_start = TRUE;
129 last_cluster = glyphs->log_clusters[i];
132 glyphs->glyphs[i].attr.is_cluster_start = FALSE;
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.
139 if (glyphs->glyphs[i].geometry.width < 0)
141 glyphs->glyphs[i].geometry.width = -glyphs->glyphs[i].geometry.width;
142 glyphs->glyphs[i].geometry.x_offset += glyphs->glyphs[i].geometry.width;
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]))
150 /* Warn once per shaper */
151 static GQuark warned_quark = 0;
154 warned_quark = g_quark_from_static_string ("pango-shape-warned");
156 if (analysis->shape_engine && !g_object_get_qdata (G_OBJECT (analysis->shape_engine), warned_quark))
158 GType engine_type = G_OBJECT_TYPE (analysis->shape_engine);
159 const char *engine_name = g_type_name (engine_type);
161 engine_name = "(unknown)";
163 g_warning ("Expected RTL run but shape-engine='%s' returned LTR. Fixing.", engine_name);
165 g_object_set_qdata_full (G_OBJECT (analysis->shape_engine), warned_quark,
166 GINT_TO_POINTER (1), NULL);
169 /* *Fix* it so we don't crash later */
170 pango_glyph_string_reverse_range (glyphs, 0, glyphs->num_glyphs);