Git init
[external/pango1.0.git] / modules / hebrew / hebrew-fc.c
1 /* Pango
2  * hebrew-fc.h: Hebrew shaper for FreeType-based backends
3  *
4  * Copyright (C) 2000, 2007 Red Hat Software
5  * Authors:
6  *   Owen Taylor <otaylor@redhat.com>
7  *   Dov Grobgeld <dov.grobgeld@weizmann.ac.il>
8  *   Behdad Esfahbod <behdad@behdad.org>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 #include "config.h"
27 #include <string.h>
28
29 #include <pango/pango-ot.h>
30 #include "pango-engine.h"
31 #include "pango-utils.h"
32 #include "pangofc-font.h"
33 #include "hebrew-shaper.h"
34
35 /* No extra fields needed */
36 typedef PangoEngineShape      HebrewEngineFc;
37 typedef PangoEngineShapeClass HebrewEngineFcClass ;
38
39 #define MAX_CLUSTER_CHRS        20
40
41 static PangoEngineScriptInfo hebrew_scripts[] = {
42   { PANGO_SCRIPT_HEBREW, "*" }
43 };
44
45 #define SCRIPT_ENGINE_NAME "HebrewScriptEngineFc"
46 #define RENDER_TYPE PANGO_RENDER_TYPE_FC
47
48 static PangoEngineInfo script_engines[] = {
49   {
50     SCRIPT_ENGINE_NAME,
51     PANGO_ENGINE_TYPE_SHAPE,
52     RENDER_TYPE,
53     hebrew_scripts, G_N_ELEMENTS(hebrew_scripts)
54   }
55 };
56
57 static void
58 get_cluster_glyphs(PangoFont      *font,
59                    gunichar       cluster[],
60                    gint           cluster_size,
61                    gboolean       do_mirror,
62                    /* output */
63                    gint           glyph_num[],
64                    PangoGlyph     glyph[],
65                    gint           widths[],
66                    PangoRectangle ink_rects[])
67 {
68   int i;
69   for (i=0; i<cluster_size; i++)
70     {
71       PangoRectangle logical_rect;
72       gunichar wc = cluster[i];
73       gunichar mirrored_ch;
74
75       if (do_mirror)
76         if (pango_get_mirror_char (wc, &mirrored_ch))
77           wc = mirrored_ch;
78
79       if (pango_is_zero_width (wc))
80         glyph_num[i] = PANGO_GLYPH_EMPTY;
81       else
82         {
83           glyph_num[i] = pango_fc_font_get_glyph ((PangoFcFont *)font, wc);
84
85           if (!glyph_num[i])
86             glyph_num[i] = PANGO_GET_UNKNOWN_GLYPH ( wc);
87         }
88
89       glyph[i] = glyph_num[i];
90
91       pango_font_get_glyph_extents (font,
92                                     glyph[i], &ink_rects[i], &logical_rect);
93
94       /* Assign the base char width to the last character in the cluster */
95       if (i==0)
96         {
97           widths[i] = 0;
98           widths[cluster_size-1] = logical_rect.width;
99         }
100       else if (i < cluster_size-1)
101         widths[i] = 0;
102     }
103 }
104
105 static void
106 add_glyph (PangoGlyphString *glyphs,
107            gint              cluster_start,
108            PangoGlyph        glyph,
109            gboolean          is_combining,
110            gint              width,
111            gint              x_offset,
112            gint              y_offset
113            )
114 {
115   gint index = glyphs->num_glyphs;
116
117   pango_glyph_string_set_size (glyphs, index + 1);
118
119   glyphs->glyphs[index].glyph = glyph;
120   glyphs->glyphs[index].attr.is_cluster_start = is_combining ? 0 : 1;
121
122   glyphs->log_clusters[index] = cluster_start;
123
124   glyphs->glyphs[index].geometry.x_offset = x_offset;
125   glyphs->glyphs[index].geometry.y_offset = y_offset;
126   glyphs->glyphs[index].geometry.width = width;
127 }
128
129 static void
130 add_cluster(PangoGlyphString *glyphs,
131             int              cluster_size,
132             int              cluster_start,
133             PangoGlyph       glyph[],
134             int              width[],
135             int              x_offset[],
136             int              y_offset[])
137 {
138   int i;
139
140   for (i=0; i<cluster_size; i++)
141     {
142       add_glyph (glyphs, cluster_start, glyph[i],
143                  i == 0 ? FALSE : TRUE, width[i], x_offset[i], y_offset[i]);
144     }
145 }
146
147 static void
148 fallback_shape (PangoEngineShape *engine G_GNUC_UNUSED,
149                 PangoFont        *font,
150                 const char       *text,
151                 gint              length,
152                 const PangoAnalysis *analysis,
153                 PangoGlyphString *glyphs)
154 {
155   const char *p;
156   const char *log_cluster;
157   gunichar cluster[MAX_CLUSTER_CHRS];
158   gint cluster_size;
159   gint glyph_num[MAX_CLUSTER_CHRS];
160   gint glyph_width[MAX_CLUSTER_CHRS], x_offset[MAX_CLUSTER_CHRS], y_offset[MAX_CLUSTER_CHRS];
161   PangoRectangle ink_rects[MAX_CLUSTER_CHRS];
162   PangoGlyph glyph[MAX_CLUSTER_CHRS];
163
164   pango_glyph_string_set_size (glyphs, 0);
165
166   p = text;
167   while (p < text + length)
168     {
169       log_cluster = p;
170       p = hebrew_shaper_get_next_cluster (p, text + length - p,
171                                           /* output */
172                                           cluster, &cluster_size);
173       get_cluster_glyphs(font,
174                          cluster,
175                          cluster_size,
176                          analysis->level % 2,
177                          /* output */
178                          glyph_num,
179                          glyph,
180                          glyph_width,
181                          ink_rects);
182
183       /* Kern the glyphs! */
184       hebrew_shaper_get_cluster_kerning(cluster,
185                                         cluster_size,
186                                         /* Input and output */
187                                         ink_rects,
188                                         glyph_width,
189                                         /* output */
190                                         x_offset,
191                                         y_offset);
192
193       add_cluster(glyphs,
194                   cluster_size,
195                   log_cluster - text,
196                   glyph,
197                   glyph_width,
198                   x_offset,
199                   y_offset);
200
201     }
202
203   if (analysis->level % 2)
204     hebrew_shaper_bidi_reorder(glyphs);
205 }
206
207 static const PangoOTFeatureMap gsub_features[] =
208 {
209   {"ccmp", PANGO_OT_ALL_GLYPHS},
210   {"locl", PANGO_OT_ALL_GLYPHS},
211   {"rlig", PANGO_OT_ALL_GLYPHS},
212   /* 'dlig' should be turned-on/off-able.  lets turn off for now. */
213   /* {"dlig", PANGO_OT_ALL_GLYPHS}, */
214 };
215
216 static const PangoOTFeatureMap gpos_features[] =
217 {
218   {"kern", PANGO_OT_ALL_GLYPHS},
219   {"mark", PANGO_OT_ALL_GLYPHS},
220   {"mkmk", PANGO_OT_ALL_GLYPHS}
221 };
222
223 static void
224 hebrew_engine_shape (PangoEngineShape *engine,
225                      PangoFont        *font,
226                      const char       *text,
227                      gint              length,
228                      const PangoAnalysis *analysis,
229                      PangoGlyphString *glyphs)
230 {
231   PangoFcFont *fc_font;
232   FT_Face face;
233   PangoOTRulesetDescription desc;
234   const PangoOTRuleset *ruleset;
235   PangoOTBuffer *buffer;
236   guint n_gpos_features = 0;
237   glong n_chars;
238   const char *p;
239   int cluster = 0;
240   int i;
241
242   g_return_if_fail (font != NULL);
243   g_return_if_fail (text != NULL);
244   g_return_if_fail (length >= 0);
245   g_return_if_fail (analysis != NULL);
246
247   fc_font = PANGO_FC_FONT (font);
248   face = pango_fc_font_lock_face (fc_font);
249   if (!face)
250     return;
251
252   desc.script = analysis->script;
253   desc.language = analysis->language;
254
255   desc.n_static_gsub_features = G_N_ELEMENTS (gsub_features);
256   desc.static_gsub_features = gsub_features;
257   desc.n_static_gpos_features = G_N_ELEMENTS (gpos_features);
258   desc.static_gpos_features = gpos_features;
259
260   /* TODO populate other_features from analysis->extra_attrs */
261   desc.n_other_features = 0;
262   desc.other_features = NULL;
263
264   ruleset = pango_ot_ruleset_get_for_description (pango_ot_info_get (face), &desc);
265
266   pango_ot_ruleset_get_feature_count (ruleset, NULL, &n_gpos_features);
267   if (n_gpos_features == 0)
268     {
269       fallback_shape (engine, font, text, length, analysis, glyphs);
270       goto out;
271     }
272
273   buffer = pango_ot_buffer_new (fc_font);
274   pango_ot_buffer_set_rtl (buffer, analysis->level % 2 != 0);
275
276   n_chars = g_utf8_strlen (text, length);
277
278   p = text;
279   for (i=0; i < n_chars; i++)
280     {
281       gunichar wc;
282       PangoGlyph glyph;
283
284       wc = g_utf8_get_char (p);
285
286       if (g_unichar_type (wc) != G_UNICODE_NON_SPACING_MARK)
287         cluster = p - text;
288
289       if (pango_is_zero_width (wc))
290         glyph = PANGO_GLYPH_EMPTY;
291       else
292         {
293           gunichar c = wc;
294
295           if (analysis->level % 2)
296             g_unichar_get_mirror_char (c, &c);
297
298           glyph = pango_fc_font_get_glyph (fc_font, c);
299         }
300
301       if (!glyph)
302         glyph = PANGO_GET_UNKNOWN_GLYPH (wc);
303
304       pango_ot_buffer_add_glyph (buffer, glyph, 0, cluster);
305
306       p = g_utf8_next_char (p);
307     }
308
309   pango_ot_ruleset_substitute (ruleset, buffer);
310   pango_ot_ruleset_position (ruleset, buffer);
311   pango_ot_buffer_output (buffer, glyphs);
312
313   pango_ot_buffer_destroy (buffer);
314
315  out:
316   pango_fc_font_unlock_face (fc_font);
317 }
318
319 static void
320 hebrew_engine_fc_class_init (PangoEngineShapeClass *class)
321 {
322   class->script_shape = hebrew_engine_shape;
323 }
324
325 PANGO_ENGINE_SHAPE_DEFINE_TYPE (HebrewEngineFc, hebrew_engine_fc,
326                                 hebrew_engine_fc_class_init, NULL)
327
328 void
329 PANGO_MODULE_ENTRY(init) (GTypeModule *module)
330 {
331   hebrew_engine_fc_register_type (module);
332 }
333
334 void
335 PANGO_MODULE_ENTRY(exit) (void)
336 {
337 }
338
339 void
340 PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines,
341                           int              *n_engines)
342 {
343   *engines = script_engines;
344   *n_engines = G_N_ELEMENTS (script_engines);
345 }
346
347 PangoEngine *
348 PANGO_MODULE_ENTRY(create) (const char *id)
349 {
350   if (!strcmp (id, SCRIPT_ENGINE_NAME))
351     return g_object_new (hebrew_engine_fc_type, NULL);
352   else
353     return NULL;
354 }