0ff05baae6d184968e15d60c92370327f4cbcbb0
[m17n/m17n-pango.git] / m17n-fc.c
1 /* Pango
2  * m17n-fc.c: Generic shaper using the m17n library for FreeType-based backends
3
4    Copyright (C) 2004, 2005
5      National Institute of Advanced Industrial Science and Technology (AIST)
6      Registration Number H15PRO112
7
8    This file is part of the m17n library.
9
10    The m17n library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public License
12    as published by the Free Software Foundation; either version 2.1 of
13    the License, or (at your option) any later version.
14
15    The m17n 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    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with the m17n library; if not, write to the Free
22    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23    02111-1307, USA.  */
24
25 #include <config.h>
26
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <pango/pango-engine.h>
31 #include <pango/pango-utils.h>
32 #include <pango/pangofc-font.h>
33
34 #include <m17n-gui.h>
35 #include <m17n-misc.h>
36
37 #ifdef G_LOG_DOMAIN
38 #undef G_LOG_DOMAIN
39 #endif
40 #define G_LOG_DOMAIN "M17N-Pango-FC"
41
42 /* Control printing of debug information.  It is initialized from the
43    environment variable PANGO_M17N_FC_DEBUG.  Currently, any positive
44    value turns on all debug messages.  */
45 static int debug_level;
46
47 /* Commonly used frame on nulldevice.  */
48 static MFrame *frame;
49
50 /* Initialize this module.  The main task is to open a frame on
51    nulldevice.  */
52
53 static void
54 init_module ()
55 {
56   const char *envvar = g_getenv ("PANGO_M17N_FC_DEBUG");
57   MPlist *param;
58   MFace *face;
59   MFontset *fontset;
60
61   debug_level = envvar ? atoi (envvar) : 0;
62
63   param = mplist ();
64   face = mface ();
65   fontset = (mdatabase_find (Mfontset, msymbol ("generic"), Mnil, Mnil)
66              ? mfontset ("generic") : mfontset (NULL));
67   mface_put_prop (face, Mfontset, fontset);
68   mplist_put (param, Mface, face);
69   mplist_put (param, Mdevice, Mnil);
70   frame = mframe (param);
71   m17n_object_unref (fontset);
72   m17n_object_unref (face);
73   m17n_object_unref (param);
74 }
75
76 /* Finalize this module by freeing frame.  */
77
78 static void
79 fini_module ()
80 {
81   m17n_object_unref (frame);
82 }
83
84 /* No extra fields needed for these structures.  */
85 typedef PangoEngineShape      M17NEngineFc;
86 typedef PangoEngineShapeClass M17NEngineFcClass;
87
88 #define SCRIPT_ENGINE_NAME "M17NScriptEngineFc"
89 #define RENDER_TYPE PANGO_RENDER_TYPE_FC
90
91 static PangoEngineScriptInfo m17n_scripts[] = {
92   { PANGO_SCRIPT_ARABIC,   "*" },
93   { PANGO_SCRIPT_BENGALI,  "*" },
94   { PANGO_SCRIPT_DEVANAGARI, "*" },
95   { PANGO_SCRIPT_ETHIOPIC, "*" },
96   { PANGO_SCRIPT_GEORGIAN, "*" },
97   { PANGO_SCRIPT_GUJARATI, "*" },
98   { PANGO_SCRIPT_GURMUKHI, "*" },
99   { PANGO_SCRIPT_HEBREW,   "*" },
100   { PANGO_SCRIPT_KANNADA,  "*" },
101   { PANGO_SCRIPT_KHMER,    "*" },
102   { PANGO_SCRIPT_LAO,      "*" },
103   { PANGO_SCRIPT_MALAYALAM, "*" },
104   { PANGO_SCRIPT_MYANMAR,  "*" },
105   { PANGO_SCRIPT_ORIYA,     "*" },
106   { PANGO_SCRIPT_SINHALA,   "*" },
107   { PANGO_SCRIPT_SYRIAC,    "*" },
108   { PANGO_SCRIPT_TAMIL,     "*" },
109   { PANGO_SCRIPT_TELUGU,    "*" },
110   { PANGO_SCRIPT_THAANA,    "*" },
111   { PANGO_SCRIPT_THAI,      "*" },
112   { PANGO_SCRIPT_TIBETAN,   "*" },
113 };
114
115 static PangoEngineInfo script_engines[] = {
116   {
117     SCRIPT_ENGINE_NAME,
118     PANGO_ENGINE_TYPE_SHAPE,
119     RENDER_TYPE,
120     m17n_scripts, G_N_ELEMENTS (m17n_scripts)
121   }
122 };
123
124
125 /* Convert PANGO_LANGUAGE to m17n-lib's language symbol.  If m17n-lib
126    doesn't know about PANGO_LANGUAGE, return Mnil.  */
127
128 static MSymbol
129 m17n_fc_get_language (PangoLanguage *pango_language)
130 {
131   MSymbol language;
132
133   if (pango_language)
134     {
135       const char *lang = pango_language_to_string (pango_language);
136
137       language = msymbol (lang);
138       if (! msymbol_get (language, Mlanguage))
139         {
140           if (strlen (lang) <= 2)
141             language = Mnil;
142           else
143             {
144               /* Remove region part (e.g. "zh_CN" -> "zh").  */
145               char shortlang[3];
146           
147               shortlang[0] = lang[0], shortlang[1] = lang[1], shortlang[2] = 0;
148               language = msymbol (shortlang);
149               if (! msymbol_get (language, Mlanguage))
150                 language = Mnil;
151             }
152         }
153     }
154   else
155     language = Mnil;
156   return language;
157 }
158
159 /* Shaper function.  */
160
161 static void 
162 m17n_fc_engine_shape (PangoEngineShape *engine,
163                       PangoFont        *pango_font,
164                       const char       *text,
165                       gint              length,
166                       PangoAnalysis    *analysis,
167                       PangoGlyphString *glyphs)
168 {
169   /* Symbols for character property `category'.  */
170   static MSymbol MZs, MCf;
171   static MDrawControl control;
172   MSymbol language;
173   PangoGlyphInfo *g;
174   MText *mt;
175   MDrawGlyph *m_glyphs;
176   int nchars, nglyphs;
177   PangoFcFont *fc_font;
178   char *fontname;
179   MFont *font;
180   int i;
181   const char *p;
182   int *offsets;
183
184   g_return_if_fail (pango_font != NULL);
185   g_return_if_fail (text != NULL);
186   g_return_if_fail (length >= 0);
187   g_return_if_fail (analysis != NULL);
188
189   if (debug_level > 0)
190     {
191       char *msg = alloca (length + 1);
192
193       if (msg)
194         {
195           memcpy (msg, text, length);
196           msg[length] = '\0';
197         }
198       else
199         msg = "...";
200       g_debug ("shape \"%s\"", msg);
201     }
202
203   if (! MZs)
204     {
205       MZs = msymbol ("Zs");
206       MCf = msymbol ("Cf");
207     }
208
209   language = m17n_fc_get_language (analysis->language);
210
211   mt = mtext_from_data (text, length, MTEXT_FORMAT_UTF_8);
212   nchars = mtext_len (mt);
213   offsets = alloca (sizeof (int) * nchars);
214   g_return_if_fail (offsets != NULL);
215   for (i = 0, p = text; i < nchars; i++, p = g_utf8_next_char (p))
216     offsets[i] = p - text;
217
218   fc_font = PANGO_FC_FONT (pango_font);
219   fontname = (char *) FcNameUnparse (fc_font->font_pattern);
220   font = mfont_parse_name (fontname, Mfontconfig);
221   free (fontname);
222   {
223     FcChar8 *filename;
224     int size;
225
226     if (FcPatternGetString (fc_font->font_pattern, FC_FILE, 0, &filename)
227         == FcResultMatch)
228       mfont_put_prop (font, Mfontfile, msymbol ((char *) filename));
229     size = (int) mfont_get_prop (font, Msize);
230     if (size)
231       mfont_put_prop (font, Msize, (void *) (size * 256));
232     if (debug_level > 0)
233       g_debug ("  by %s", (char *) filename);
234   }
235
236   mtext_put_prop (mt, 0, nchars, Mfont, font);
237   if (language != Mnil)
238     mtext_put_prop (mt, 0, nchars, Mlanguage, language);
239
240   control.enable_bidi = 1;
241   m_glyphs = alloca (sizeof (MDrawGlyph) * nchars * 2);
242   g_return_if_fail (m_glyphs != NULL);
243   if (mdraw_glyph_list (frame, mt, 0, nchars, &control, m_glyphs,
244                         nchars * 2, &nglyphs) < 0)
245     {
246       m_glyphs = alloca (sizeof (MDrawGlyph) * nglyphs);
247       g_return_if_fail (m_glyphs != NULL);
248       mdraw_glyph_list (frame, mt, 0, nchars, &control, m_glyphs,
249                         nglyphs, &nglyphs);
250     }
251
252   pango_glyph_string_set_size (glyphs, nglyphs);
253
254   for (i = 0, g = glyphs->glyphs; i < nglyphs; i++, g++)
255     {
256       if (m_glyphs[i].glyph_code >= 0)
257         {
258           g->glyph = m_glyphs[i].glyph_code;
259           g->geometry.x_offset = m_glyphs[i].x_off * PANGO_SCALE / 256;
260           g->geometry.y_offset = m_glyphs[i].y_off * PANGO_SCALE / 256;
261           g->geometry.width = m_glyphs[i].x_advance * PANGO_SCALE / 256;
262         }
263       else
264         {
265           int c = mtext_ref_char (mt, m_glyphs[i].from);
266           MSymbol category = mchar_get_prop (c, Mcategory);
267           PangoRectangle logical_rect;
268
269           if (category == MZs)
270             {
271               g->glyph = 0;
272               g->geometry.width = m_glyphs[i].x_advance * PANGO_SCALE / 256;
273             }
274           else if (category == MCf)
275             {
276               g->glyph = 0;
277               g->geometry.width = 0;
278             }
279           else
280             {
281               g->glyph = pango_fc_font_get_unknown_glyph (fc_font, c);
282               pango_font_get_glyph_extents (pango_font, g->glyph, NULL,
283                                             &logical_rect);
284               g->geometry.width = logical_rect.width;
285             }
286           g->geometry.x_offset = 0;
287           g->geometry.y_offset = 0;
288         }
289       g->attr.is_cluster_start
290         = (i == 0
291            || m_glyphs[i - 1].from != m_glyphs[i].from);
292       glyphs->log_clusters[i] = offsets[m_glyphs[i].from];
293     }
294
295   m17n_object_unref (mt);
296   free (font);
297 }
298
299 /* Check if PANGO_FONT is suitable for the character WC.  */
300
301 static PangoCoverageLevel
302 m17n_fc_engine_covers (PangoEngineShape *engine,
303                        PangoFont        *pango_font,
304                        PangoLanguage    *pango_language,
305                        gunichar          wc)
306 {
307   static gunichar last_wc = 0;
308   /* Symbols for character property `script'.  */
309   static MSymbol Mlatin, Mcommon, Minherited;
310   PangoCoverage *coverage;
311   PangoCoverageLevel result;
312   PangoFcFont *fc_font;
313   char *fontname;
314   MFont *font;
315   MSymbol script, language;
316   int check_result;
317
318   if (! Mlatin)
319     {
320       Mlatin = msymbol ("latin");
321       Mcommon = msymbol ("common");
322       Minherited = msymbol ("inherited");
323     }
324
325   if (debug_level > 0 && wc != last_wc)
326     {
327       g_debug ("covers for U+%04X", wc);
328       last_wc = wc;
329     }
330
331   coverage = pango_font_get_coverage (pango_font, pango_language);
332   result = pango_coverage_get (coverage, wc);
333   pango_coverage_unref (coverage);
334
335   if (result == PANGO_COVERAGE_NONE)
336     {
337       if (debug_level > 0)
338         {
339           fc_font = PANGO_FC_FONT (pango_font);
340           fontname = (char *) FcNameUnparse (fc_font->font_pattern);
341           font = mfont_parse_name (fontname, Mfontconfig);
342           g_debug ("  %s none", 
343                    msymbol_name (mfont_get_prop (font, Mfamily)));
344           free (fontname);
345           free (font);
346         }
347       return result;
348     }
349
350   fc_font = PANGO_FC_FONT (pango_font);
351   fontname = (char *) FcNameUnparse (fc_font->font_pattern);
352   font = mfont_parse_name (fontname, Mfontconfig);
353
354   if (wc < 0x100)
355     script = Mlatin;
356   else
357     {
358       script = mchar_get_prop ((int) wc, Mscript);
359       if (script == Mcommon || script == Minherited)
360         script = mchar_get_prop ((int) wc, msymbol ("block"));
361     }
362   language = m17n_fc_get_language (pango_language);
363
364   check_result = mfont_check (frame, NULL, script, language, font);
365   if (debug_level > 0)
366     {
367       char *result_text = (check_result == 2 ? "exact"
368                            : check_result == 1 ? "approximate"
369                            : "fallback");
370
371       g_debug ("  (%s,%s): %s %s",
372                (script ? msymbol_name (script) : ""),
373                (language ? msymbol_name (language) : ""),
374                msymbol_name (mfont_get_prop (font, Mfamily)),
375                result_text);
376     }      
377
378   free (fontname);
379   free (font);
380   return (check_result == 2 ? PANGO_COVERAGE_EXACT
381           : check_result == 1 ? PANGO_COVERAGE_APPROXIMATE
382           : PANGO_COVERAGE_FALLBACK);
383 }
384
385 static void
386 m17n_engine_fc_class_init (PangoEngineShapeClass *class)
387 {
388   class->script_shape = m17n_fc_engine_shape;
389   class->covers = m17n_fc_engine_covers;
390 }
391
392 PANGO_ENGINE_SHAPE_DEFINE_TYPE (M17NEngineFc, m17n_engine_fc,
393                                 m17n_engine_fc_class_init, NULL)
394
395 void 
396 PANGO_MODULE_ENTRY(init) (GTypeModule *module)
397 {
398   m17n_engine_fc_register_type (module);
399
400   M17N_INIT ();
401   init_module ();
402 }
403
404 void 
405 PANGO_MODULE_ENTRY(exit) (void)
406 {
407   fini_module ();
408   M17N_FINI ();
409 }
410
411 void 
412 PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines,
413                           int              *n_engines)
414 {
415   *engines = script_engines;
416   *n_engines = G_N_ELEMENTS (script_engines);
417 }
418
419 PangoEngine *
420 PANGO_MODULE_ENTRY(create) (const char *id)
421 {
422   return (!strcmp (id, SCRIPT_ENGINE_NAME)
423           ? g_object_new (m17n_engine_fc_type, NULL)
424           : NULL);
425 }