2 * m17n-fc.c: Generic shaper using the m17n library for FreeType-based backends
4 Copyright (C) 2004, 2005, 2006
5 National Institute of Advanced Industrial Science and Technology (AIST)
6 Registration Number H16PRO276
8 This file is part of the pango-m17n library.
10 The pango-m17n library is free software; you can redistribute it
11 and/or modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
15 The pango-m17n library is distributed in the hope that it will be
16 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
17 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with the pango-m17n library; if not, write to the
22 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 MA 02111-1307, USA. */
28 #include <pango/pango-engine.h>
29 #include <pango/pango-utils.h>
30 #include <pango/pangofc-font.h>
33 #include <m17n-misc.h>
38 #define G_LOG_DOMAIN "Pango-M17N-FC"
40 /* Control printing of debug information. It is initialized from the
41 environment variable PANGO_M17N_FC_DEBUG. Currently, any positive
42 value turns on all debug messages. */
43 static int debug_level;
45 /* Commonly used frame on nulldevice. */
48 /* Commonly used "generic" fontset. */
49 static MFontset *fontset;
51 /* Initialize this module. The main task is to open a frame on
57 const char *envvar = g_getenv ("PANGO_M17N_FC_DEBUG");
61 debug_level = envvar ? atoi (envvar) : 0;
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 (face);
72 m17n_object_unref (param);
75 /* Finalize this module by freeing frame. */
80 m17n_object_unref (frame);
81 m17n_object_unref (fontset);
84 /* No extra fields needed for these structures. */
85 typedef PangoEngineShape M17NEngineFc;
86 typedef PangoEngineShapeClass M17NEngineFcClass;
88 #define SCRIPT_ENGINE_NAME "M17NScriptEngineFc"
89 #define RENDER_TYPE PANGO_RENDER_TYPE_FC
91 static PangoEngineScriptInfo m17n_scripts[] = {
92 { PANGO_SCRIPT_ARABIC, "*" },
93 { PANGO_SCRIPT_ARMENIAN, "*" },
94 { PANGO_SCRIPT_BENGALI, "*" },
95 { PANGO_SCRIPT_BOPOMOFO, "*" },
96 { PANGO_SCRIPT_CHEROKEE, "*" },
97 { PANGO_SCRIPT_COPTIC, "*" },
98 { PANGO_SCRIPT_CYRILLIC, "*" },
99 { PANGO_SCRIPT_DESERET, "*" },
100 { PANGO_SCRIPT_DEVANAGARI, "*" },
101 { PANGO_SCRIPT_ETHIOPIC, "*" },
102 { PANGO_SCRIPT_GEORGIAN, "*" },
103 { PANGO_SCRIPT_GOTHIC, "*" },
104 { PANGO_SCRIPT_GREEK, "*" },
105 { PANGO_SCRIPT_GUJARATI, "*" },
106 { PANGO_SCRIPT_GURMUKHI, "*" },
107 { PANGO_SCRIPT_HAN, "*" },
108 { PANGO_SCRIPT_HANGUL, "*" },
109 { PANGO_SCRIPT_HEBREW, "*" },
110 { PANGO_SCRIPT_HIRAGANA, "*" },
111 { PANGO_SCRIPT_KANNADA, "*" },
112 { PANGO_SCRIPT_KATAKANA, "*" },
113 { PANGO_SCRIPT_KHMER, "*" },
114 { PANGO_SCRIPT_LAO, "*" },
115 { PANGO_SCRIPT_MALAYALAM, "*" },
116 { PANGO_SCRIPT_MYANMAR, "*" },
117 { PANGO_SCRIPT_OGHAM, "*" },
118 { PANGO_SCRIPT_OLD_ITALIC, "*" },
119 { PANGO_SCRIPT_ORIYA, "*" },
120 { PANGO_SCRIPT_RUNIC, "*" },
121 { PANGO_SCRIPT_SINHALA, "*" },
122 { PANGO_SCRIPT_SYRIAC, "*" },
123 { PANGO_SCRIPT_TAMIL, "*" },
124 { PANGO_SCRIPT_TELUGU, "*" },
125 { PANGO_SCRIPT_THAANA, "*" },
126 { PANGO_SCRIPT_THAI, "*" },
127 { PANGO_SCRIPT_TIBETAN, "*" },
128 { PANGO_SCRIPT_CANADIAN_ABORIGINAL, "*" },
129 { PANGO_SCRIPT_YI, "*" },
131 #ifdef PANGO_SCRIPT_BRAILLE
132 { PANGO_SCRIPT_BRAILLE, "*" },
133 { PANGO_SCRIPT_CYPRIOT, "*" },
134 { PANGO_SCRIPT_LIMBU, "*" },
135 { PANGO_SCRIPT_OSMANYA, "*" },
136 { PANGO_SCRIPT_SHAVIAN, "*" },
137 { PANGO_SCRIPT_LINEAR_B, "*" },
138 { PANGO_SCRIPT_TAI_LE, "*" },
139 { PANGO_SCRIPT_UGARITIC, "*" },
140 #endif /* PANGO_SCRIPT_BRAILLE */
142 #ifdef PANGO_SCRIPT_NEW_TAI_LUE
143 { PANGO_SCRIPT_NEW_TAI_LUE, "*" },
144 { PANGO_SCRIPT_BUGINESE, "*" },
145 { PANGO_SCRIPT_GLAGOLITIC, "*" },
146 { PANGO_SCRIPT_TIFINAGH, "*" },
147 { PANGO_SCRIPT_SYLOTI_NAGRI, "*" },
148 { PANGO_SCRIPT_OLD_PERSIAN, "*" },
149 { PANGO_SCRIPT_KHAROSHTHI, "*" },
150 #endif /* PANGO_SCRIPT_NEW_TAI_LUE */
152 { PANGO_SCRIPT_UNKNOWN, "*" },
154 { PANGO_SCRIPT_COMMON, "" }
157 static PangoEngineInfo script_engines[] = {
160 PANGO_ENGINE_TYPE_SHAPE,
162 m17n_scripts, G_N_ELEMENTS (m17n_scripts)
167 m17n_fc_get_family (PangoFont *pango_font)
169 PangoFcFont *pango_fc_font = PANGO_FC_FONT (pango_font);
172 if (FcPatternGetString (pango_fc_font->font_pattern, FC_FAMILY, 0, &family)
174 return (char *) family;
178 /* Convert PANGO_LANGUAGE to m17n-lib's language symbol. If m17n-lib
179 doesn't know about PANGO_LANGUAGE, return Mnil. */
182 m17n_fc_get_language (PangoLanguage *pango_language)
184 static PangoLanguage *cached_pango_language;
185 static MSymbol cached_language;
188 if (cached_pango_language == pango_language)
190 return cached_language;
195 const char *lang = pango_language_to_string (pango_language);
197 language = msymbol (lang);
198 if (! msymbol_get (language, Mlanguage))
200 if (strlen (lang) <= 2)
204 /* Remove region part (e.g. "zh_CN" -> "zh"). */
207 shortlang[0] = lang[0], shortlang[1] = lang[1], shortlang[2] = 0;
208 language = msymbol (shortlang);
209 if (! msymbol_get (language, Mlanguage))
216 cached_pango_language = pango_language;
217 cached_language = language;
222 /* Return a list of FONT-SPECs specified in the current fontset for
223 displaying SCRIPT in LANGUAGE. Callers must unref the returned
227 m17n_fc_lookup_fontset (MSymbol script, MSymbol language)
229 static MSymbol cached_script, cached_language;
230 static MPlist *cached_plist;
233 if (cached_script == script && cached_language == language)
235 m17n_object_ref (cached_plist);
238 plist = mfontset_lookup (fontset, script, language, Mnil);
240 if (mplist_key (plist) == Mnil)
242 /* List up languages that have entries for SCRIPT. */
243 MPlist *ll = mfontset_lookup (fontset, script, Mt, Mnil);
247 /* No entry for SCRIPT. Return fallback FONT-SPECs. */
248 m17n_object_unref (plist);
249 plist = mfontset_lookup (fontset, Mnil, Mnil, Mnil);
253 if (mplist_length (ll) == 1)
255 /* If there's just one language, return FONT-SPECs for it. */
256 m17n_object_unref (plist);
257 language = mplist_key (ll);
260 plist = mfontset_lookup (fontset, script, mplist_key (ll), Mnil);
263 /* Sumup FONT-SPECs for all languages. */
264 for (; mplist_key (ll) != Mnil; ll = mplist_next (ll))
268 language = mplist_key (ll);
271 for (pl = mfontset_lookup (fontset, script, language, Mnil);
272 mplist_key (pl) != Mnil; pl = mplist_next (pl))
273 mplist_add (plist, mplist_key (pl), mplist_value (pl));
277 cached_language = language;
278 cached_script = script;
279 cached_plist = plist;
280 m17n_object_ref (plist);
287 PangoFcFont *pango_fc_font;
292 m17n_fc_free_font_info (gpointer data)
294 M17NFontInfo *font_info = data;
298 mfont_close (font_info->font);
299 pango_fc_font_unlock_face (font_info->pango_fc_font);
305 m17n_fc_get_font (PangoFont *pango_font)
307 GQuark info_id = g_quark_from_string ("m17n-font-info");
308 M17NFontInfo *font_info;
310 font_info = g_object_get_qdata (G_OBJECT (pango_font), info_id);
313 PangoFcFont *pango_fc_font = PANGO_FC_FONT (pango_font);
314 FT_Face ft_face = pango_fc_font_lock_face (pango_fc_font);
316 font_info = g_new (M17NFontInfo, 1);
317 font_info->pango_fc_font = pango_fc_font;
318 font_info->font = mfont_encapsulate (frame, Mfreetype, ft_face);
319 g_object_set_qdata_full (G_OBJECT (pango_font), info_id, font_info,
320 (GDestroyNotify) m17n_fc_free_font_info);
322 return font_info->font;
326 /* Shaper function. */
329 m17n_fc_engine_shape (PangoEngineShape *engine,
330 PangoFont *pango_font,
333 const PangoAnalysis *analysis,
334 PangoGlyphString *glyphs)
336 /* Symbols for character property `category'. */
337 static MSymbol MZs, MCf;
338 static MDrawControl control;
342 MDrawGlyph *m_glyphs;
349 g_return_if_fail (pango_font != NULL);
350 g_return_if_fail (text != NULL);
351 g_return_if_fail (length >= 0);
352 g_return_if_fail (analysis != NULL);
356 MZs = msymbol ("Zs");
357 MCf = msymbol ("Cf");
358 memset (&control, 0, sizeof control);
361 language = m17n_fc_get_language (analysis->language);
363 mt = mtext_from_data (text, length, MTEXT_FORMAT_UTF_8);
364 nchars = mtext_len (mt);
367 g_debug ("shape \"U+%04X\"", mtext_ref_char (mt, 0));
370 offsets = alloca (sizeof (int) * nchars);
371 g_return_if_fail (offsets != NULL);
372 for (i = 0, p = text; i < nchars; i++, p = g_utf8_next_char (p))
373 offsets[i] = p - text;
375 font = m17n_fc_get_font (pango_font);
376 g_return_if_fail (font != NULL);
378 mtext_put_prop (mt, 0, nchars, Mfont, font);
380 if (language != Mnil)
381 mtext_put_prop (mt, 0, nchars, Mlanguage, language);
384 control.enable_bidi = 1;
385 m_glyphs = alloca (sizeof (MDrawGlyph) * nchars * 2);
386 g_return_if_fail (m_glyphs != NULL);
387 if (mdraw_glyph_list (frame, mt, 0, nchars, &control, m_glyphs,
388 nchars * 2, &nglyphs) < 0)
390 m_glyphs = alloca (sizeof (MDrawGlyph) * nglyphs);
391 g_return_if_fail (m_glyphs != NULL);
392 mdraw_glyph_list (frame, mt, 0, nchars, &control, m_glyphs,
396 pango_glyph_string_set_size (glyphs, nglyphs);
398 for (i = 0, g = glyphs->glyphs; i < nglyphs; i++, g++)
400 if (m_glyphs[i].glyph_code >= 0)
402 g->glyph = m_glyphs[i].glyph_code;
403 g->geometry.x_offset = m_glyphs[i].x_off * PANGO_SCALE;
404 g->geometry.y_offset = m_glyphs[i].y_off * PANGO_SCALE;
405 g->geometry.width = m_glyphs[i].x_advance * PANGO_SCALE;
409 int c = mtext_ref_char (mt, m_glyphs[i].from);
410 MSymbol category = mchar_get_prop (c, Mcategory);
411 PangoRectangle logical_rect;
416 g->geometry.width = m_glyphs[i].x_advance * PANGO_SCALE;
418 else if (category == MCf)
421 g->geometry.width = 0;
425 g->glyph = (pango_fc_font_get_unknown_glyph
426 (PANGO_FC_FONT (pango_font), c));
427 pango_font_get_glyph_extents (pango_font, g->glyph, NULL,
429 g->geometry.width = logical_rect.width;
431 g->geometry.x_offset = 0;
432 g->geometry.y_offset = 0;
434 g->attr.is_cluster_start
436 || m_glyphs[i - 1].from != m_glyphs[i].from);
437 glyphs->log_clusters[i] = offsets[m_glyphs[i].from];
440 m17n_object_unref (mt);
443 /* Check if PANGO_FONT is suitable for the character WC. */
445 static PangoCoverageLevel
446 m17n_fc_engine_covers (PangoEngineShape *engine,
447 PangoFont *pango_font,
448 PangoLanguage *pango_language,
451 static gunichar last_wc = 0;
452 /* Symbols for character property `script'. */
453 static MSymbol Mlatin, Mcommon, Minherited;
454 PangoCoverage *coverage;
455 PangoCoverageLevel result;
456 MSymbol script, language;
457 MPlist *spec_list, *p;
462 Mlatin = msymbol ("latin");
463 Mcommon = msymbol ("common");
464 Minherited = msymbol ("inherited");
468 return PANGO_COVERAGE_EXACT;
470 if (debug_level > 0 && wc != last_wc)
472 g_debug ("covers for U+%04X", wc);
476 coverage = pango_font_get_coverage (pango_font, pango_language);
477 result = pango_coverage_get (coverage, wc);
478 pango_coverage_unref (coverage);
480 if (result == PANGO_COVERAGE_NONE)
483 g_debug (" %s none", m17n_fc_get_family (pango_font));
487 language = m17n_fc_get_language (pango_language);
492 script = mchar_get_prop ((int) wc, Mscript);
493 if (script == Mcommon || script == Minherited)
494 script = mchar_get_prop ((int) wc, msymbol ("block"));
497 font = m17n_fc_get_font (pango_font);
498 spec_list = m17n_fc_lookup_fontset (script, language);
500 result = PANGO_COVERAGE_NONE;
501 for (p = spec_list; mplist_key (p) != Mnil; p = mplist_next (p))
502 if (mfont_match_p (font, mplist_value (p)))
504 result = PANGO_COVERAGE_EXACT;
507 m17n_object_unref (spec_list);
509 g_debug (" (%s,%s): %s %s",
510 (script ? msymbol_name (script) : ""),
511 (language ? msymbol_name (language) : ""),
512 m17n_fc_get_family (pango_font),
513 result == PANGO_COVERAGE_EXACT ? "exact" : "none");
520 m17n_engine_fc_class_init (PangoEngineShapeClass *class)
522 class->script_shape = m17n_fc_engine_shape;
523 class->covers = m17n_fc_engine_covers;
526 PANGO_ENGINE_SHAPE_DEFINE_TYPE (M17NEngineFc, m17n_engine_fc,
527 m17n_engine_fc_class_init, NULL)
530 PANGO_MODULE_ENTRY(init) (GTypeModule *module)
532 m17n_engine_fc_register_type (module);
539 PANGO_MODULE_ENTRY(exit) (void)
546 PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines,
549 *engines = script_engines;
550 *n_engines = G_N_ELEMENTS (script_engines);
554 PANGO_MODULE_ENTRY(create) (const char *id)
556 return (!strcmp (id, SCRIPT_ENGINE_NAME)
557 ? g_object_new (m17n_engine_fc_type, NULL)