2 * m17n-fc.c: Generic shaper using the m17n library for FreeType-based backends
4 Copyright (C) 2004, 2005, 2006, 2007, 2008
5 National Institute of Advanced Industrial Science and Technology (AIST)
6 Registration Number H16PRO276
8 The library is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation; either version 2.1 of
11 the License, or (at your option) any later version.
13 The pango-m17n library is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with the pango-m17n library; if not, write to the
20 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21 MA 02111-1307, USA. */
28 #include <pango/pango-ot.h>
29 #include <pango/pango-engine.h>
30 #include <pango/pangofc-font.h>
35 #define G_LOG_DOMAIN "Pango-M17N-FC"
37 /**************************************/
38 /* Standard preamble for Pango module */
39 /**************************************/
41 /* No extra fields needed for these structures. */
42 typedef PangoEngineShape M17NEngineFc;
43 typedef PangoEngineShapeClass M17NEngineFcClass;
45 #define SCRIPT_ENGINE_NAME "M17NScriptEngineFc"
46 #define RENDER_TYPE PANGO_RENDER_TYPE_FC
48 static PangoEngineScriptInfo m17n_scripts[] = {
49 { PANGO_SCRIPT_ARABIC, "*" },
50 { PANGO_SCRIPT_ARMENIAN, "*" },
51 { PANGO_SCRIPT_BENGALI, "*" },
52 { PANGO_SCRIPT_BOPOMOFO, "*" },
53 { PANGO_SCRIPT_CHEROKEE, "*" },
54 { PANGO_SCRIPT_COPTIC, "*" },
55 { PANGO_SCRIPT_CYRILLIC, "*" },
56 { PANGO_SCRIPT_DESERET, "*" },
57 { PANGO_SCRIPT_DEVANAGARI, "*" },
58 { PANGO_SCRIPT_ETHIOPIC, "*" },
59 { PANGO_SCRIPT_GEORGIAN, "*" },
60 { PANGO_SCRIPT_GOTHIC, "*" },
61 { PANGO_SCRIPT_GREEK, "*" },
62 { PANGO_SCRIPT_GUJARATI, "*" },
63 { PANGO_SCRIPT_GURMUKHI, "*" },
64 { PANGO_SCRIPT_HAN, "*" },
65 { PANGO_SCRIPT_HANGUL, "*" },
66 { PANGO_SCRIPT_HEBREW, "*" },
67 { PANGO_SCRIPT_HIRAGANA, "*" },
68 { PANGO_SCRIPT_KANNADA, "*" },
69 { PANGO_SCRIPT_KATAKANA, "*" },
70 { PANGO_SCRIPT_KHMER, "*" },
71 { PANGO_SCRIPT_LAO, "*" },
72 { PANGO_SCRIPT_MALAYALAM, "*" },
73 { PANGO_SCRIPT_MYANMAR, "*" },
74 { PANGO_SCRIPT_OGHAM, "*" },
75 { PANGO_SCRIPT_OLD_ITALIC, "*" },
76 { PANGO_SCRIPT_ORIYA, "*" },
77 { PANGO_SCRIPT_RUNIC, "*" },
78 { PANGO_SCRIPT_SINHALA, "*" },
79 { PANGO_SCRIPT_SYRIAC, "*" },
80 { PANGO_SCRIPT_TAMIL, "*" },
81 { PANGO_SCRIPT_TELUGU, "*" },
82 { PANGO_SCRIPT_THAANA, "*" },
83 { PANGO_SCRIPT_THAI, "*" },
84 { PANGO_SCRIPT_TIBETAN, "*" },
85 { PANGO_SCRIPT_CANADIAN_ABORIGINAL, "*" },
86 { PANGO_SCRIPT_YI, "*" },
88 { PANGO_SCRIPT_BRAILLE, "*" },
89 { PANGO_SCRIPT_CYPRIOT, "*" },
90 { PANGO_SCRIPT_LIMBU, "*" },
91 { PANGO_SCRIPT_OSMANYA, "*" },
92 { PANGO_SCRIPT_SHAVIAN, "*" },
93 { PANGO_SCRIPT_LINEAR_B, "*" },
94 { PANGO_SCRIPT_TAI_LE, "*" },
95 { PANGO_SCRIPT_UGARITIC, "*" },
97 { PANGO_SCRIPT_NEW_TAI_LUE, "*" },
98 { PANGO_SCRIPT_BUGINESE, "*" },
99 { PANGO_SCRIPT_GLAGOLITIC, "*" },
100 { PANGO_SCRIPT_TIFINAGH, "*" },
101 { PANGO_SCRIPT_SYLOTI_NAGRI, "*" },
102 { PANGO_SCRIPT_OLD_PERSIAN, "*" },
103 { PANGO_SCRIPT_KHAROSHTHI, "*" },
104 { PANGO_SCRIPT_COMMON, "" }
107 static PangoEngineInfo script_engines[] = {
110 PANGO_ENGINE_TYPE_SHAPE,
112 m17n_scripts, G_N_ELEMENTS (m17n_scripts)
116 /***************************/
117 /* Interface with m17n-flt */
118 /***************************/
120 #include <m17n-flt.h>
121 #include <m17n-misc.h>
123 /* Return a pointer to the IDXth glyph in GSTRING. */
124 #define GREF(GSTRING, IDX) ((GSTRING)->glyphs + (IDX))
126 /* Convert Pango scale to 26.6 fractional pixel. */
127 #define PANGO_SCALE_TO_26_6(N) ((N) / (PANGO_SCALE) * (1<<6))
129 /* Convert 26.6 fractional pixel to Pango scale. */
130 #define PANGO_SCALE_FROM_26_6(N) ((N) * (PANGO_SCALE) / (1<<6))
132 /* Control printing of debug information. It is initialized from the
133 environment variable PANGO_M17N_FC_DEBUG. Currently, any positive
134 value turns on all debug messages. */
135 static int debug_level;
138 /* Structure containing information about OT features decoded from
142 /* Rulesets for GSUB (1st element) and GPOS (2nd element). */
143 PangoOTRuleset *ruleset[2];
147 /* Structure containing information about a font. */
151 PangoFcFont *pango_font;
153 /* Cache that maps MFLTOtfSpec->sym to FeatureInfo. */
154 MPlist *otf_spec_cache;
158 /* Prototypes for callback functions of FontInfo.font. */
160 static int get_glyph_id (MFLTFont *, MFLTGlyphString *, int, int);
161 static int get_metrics (MFLTFont *, MFLTGlyphString *, int, int);
162 static int check_otf (MFLTFont *, MFLTOtfSpec *);
163 static int drive_otf (MFLTFont *, MFLTOtfSpec *,
164 MFLTGlyphString *, int, int,
165 MFLTGlyphString *, MFLTGlyphAdjustment *);
168 /* This function works in two ways.
170 If INFO is NULL, it checks if the features specified in SPEC match
171 with the OpenType font in OT_INFO, and return 1 (match) or 0 (no
174 If INFO is non-NULL, it creates a PangoOTRuleset object from SPEC
175 and stores it in INFO. In this case, non-existing features in SPEC
178 Features in SPEC are stored in an array in this format:
179 P P ... [ 0xFFFFFFFF [ N N ... ] ]
181 Ps are positive features that should be in the font (INFO is
182 NULL), or should be applied in this order (INFO is non-NULL),
184 0xFFFFFFFF is a separater between Ps and Ns (INFO is NULL), or
185 is a wildcard meaning that all the other features (except for
186 Ns) must be applied (INFO is non-NULL),
188 Ns are negative features that should not be in the font (INFO
189 is NULL), or should not be applied (INFO is non-NULL). */
192 otf_check_features (PangoOTInfo *ot_info, PangoOTTableType type,
193 MFLTOtfSpec *spec, FeatureInfo *info)
195 unsigned int *features;
196 unsigned int langsys_tag;
197 guint script_idx, langsys_idx;
201 /* Used as an index to spec->features[] and info->ruleset[]. */
202 int for_gpos = type == PANGO_OT_TABLE_GPOS;
204 features = spec->features[for_gpos];
207 if (! pango_ot_info_find_script (ot_info, type, spec->script, &script_idx))
208 /* No feature for the specified script exist. If the first
209 feature in SPEC is a wildcard, the remaining features are all
210 negative, so we can return 1. */
211 return (features[0] == 0xFFFFFFFF);
212 langsys_tag = spec->langsys;
214 langsys_tag = PANGO_OT_DEFAULT_LANGUAGE;
215 if (! pango_ot_info_find_language (ot_info, type, script_idx,
216 langsys_tag, &langsys_idx, &index))
218 langsys_idx = PANGO_OT_DEFAULT_LANGUAGE;
222 if (info && index != 0xFFFF)
224 info->ruleset[for_gpos] = pango_ot_ruleset_new (ot_info);
225 pango_ot_ruleset_add_feature (info->ruleset[for_gpos], type,
226 index, PANGO_OT_ALL_GLYPHS);
229 for (i = 0; features[i] && features[i] != 0xFFFFFFFF; i++)
231 if (pango_ot_info_find_feature (ot_info, type, features[i],
232 script_idx, langsys_idx, &index))
236 if (! info->ruleset[for_gpos])
237 info->ruleset[for_gpos] = pango_ot_ruleset_new (ot_info);
238 pango_ot_ruleset_add_feature (info->ruleset[for_gpos], type,
239 index, PANGO_OT_ALL_GLYPHS);
249 /* No wildcard, no negative features. */
252 feature_list = pango_ot_info_list_features (ot_info, type, 0,
253 script_idx, langsys_idx);
255 /* No feature for the specified script/language exist. As the
256 remaining features are all negative, we can return 1. */
261 /* Check if the remaining features don't appear in
263 for (i++; features[i]; i++)
264 for (j = 0; feature_list[j]; j++)
265 if (features[i] == feature_list[j])
270 /* Add all features in FEATURE_LIST except for those appearing in
272 for (i = 0; feature_list[i]; i++)
274 guint tag = feature_list[i];
276 for (j = 0; features[j] && tag != features[j]; j++);
279 pango_ot_info_find_feature (ot_info, type, tag,
280 script_idx, langsys_idx, &index);
281 if (! info->ruleset[for_gpos])
282 info->ruleset[for_gpos] = pango_ot_ruleset_new (ot_info);
283 pango_ot_ruleset_add_feature (info->ruleset[for_gpos], type, index,
284 PANGO_OT_ALL_GLYPHS);
291 /* Return a FeatureInfo object (newly created if necessary) for the
292 font in FONT_INFO and for the OpenType feature specification in
296 get_feature_info (FontInfo *font_info, MFLTOtfSpec *spec)
298 FeatureInfo *feature_info;
299 PangoOTInfo *ot_info;
301 if (! spec->features[0] && ! spec->features[1])
303 if (font_info->otf_spec_cache)
305 feature_info = mplist_get (font_info->otf_spec_cache, spec->sym);
310 font_info->otf_spec_cache = mplist ();
311 feature_info = g_new0 (FeatureInfo, 1);
312 mplist_push (font_info->otf_spec_cache, spec->sym, feature_info);
313 ot_info = pango_ot_info_get (font_info->face);
314 otf_check_features (ot_info, PANGO_OT_TABLE_GSUB, spec, feature_info);
315 otf_check_features (ot_info, PANGO_OT_TABLE_GPOS, spec, feature_info);
321 /* Free the object pointed by FONT_INFO. */
324 free_font_info (FontInfo *font_info)
326 if (font_info->otf_spec_cache)
330 for (p = font_info->otf_spec_cache; mplist_key (p) != Mnil;
333 FeatureInfo *feature_info = mplist_value (p);
335 if (feature_info->ruleset[0])
336 g_object_unref (feature_info->ruleset[0]);
337 if (feature_info->ruleset[1])
338 g_object_unref (feature_info->ruleset[1]);
340 m17n_object_unref (font_info->otf_spec_cache);
342 pango_fc_font_unlock_face (font_info->pango_font);
347 /* Return a FontInfo object corresponding to FONT. It is newly
348 created if necessary. */
351 get_font_info (PangoFont *font)
354 static GQuark info_id = 0;
356 if (G_UNLIKELY (! info_id))
357 info_id = g_quark_from_string ("FontInfo");
358 font_info = g_object_get_qdata (G_OBJECT (font), info_id);
359 if (G_UNLIKELY (! font_info))
361 PangoFontDescription *desc;
362 const char *family_name;
366 font_info = g_new0 (FontInfo, 1);
367 font_info->pango_font = PANGO_FC_FONT (font);
368 font_info->face = pango_fc_font_lock_face (font_info->pango_font);
369 desc = pango_font_describe_with_absolute_size (font);
370 family_name = pango_font_description_get_family (desc);
373 len = strlen (family_name);
374 buf = g_alloca (len + 1);
375 for (i = 0; i < len + 1; i++)
376 buf[i] = tolower (family_name[i]);
377 font_info->font.family = msymbol (buf);
379 font_info->font.x_ppem = font_info->font.y_ppem
380 = PANGO_SCALE_TO_26_6 (pango_font_description_get_size (desc));
381 pango_font_description_free (desc);
382 font_info->font.check_otf = check_otf;
383 font_info->font.get_glyph_id = get_glyph_id;
384 font_info->font.get_metrics = get_metrics;
385 font_info->font.drive_otf = drive_otf;
386 g_object_set_qdata_full (G_OBJECT (font), info_id,
387 font_info, (GDestroyNotify) free_font_info);
392 /* Four callback functions for MFLTFont. */
395 get_glyph_id (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
397 FontInfo *font_info = (FontInfo *) font;
398 MFLTGlyph *g = gstring->glyphs + from, *g_end = g + (to - from);
401 for (; g < g_end; g++)
405 g->code = pango_fc_font_get_glyph (font_info->pango_font, g->code);
414 get_metrics (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
416 FontInfo *font_info = (FontInfo *) font;
417 MFLTGlyph *g = gstring->glyphs + from, *g_end = g + (to - from);
420 for (; g < g_end; g++)
424 PangoRectangle inc, logical;
427 g->code = pango_fc_font_get_glyph (font_info->pango_font, g->code);
428 pango_font_get_glyph_extents (PANGO_FONT (font_info->pango_font),
429 g->code, &inc, &logical);
430 g->lbearing = PANGO_SCALE_TO_26_6 (inc.x);
431 g->rbearing = PANGO_SCALE_TO_26_6 (inc.x + inc.width);
432 g->xadv = PANGO_SCALE_TO_26_6 (logical.width);
434 g->ascent = - PANGO_SCALE_TO_26_6 (inc.y);
435 g->descent = PANGO_SCALE_TO_26_6 (inc.height + inc.y);
444 check_otf (MFLTFont *font, MFLTOtfSpec *spec)
446 FontInfo *font_info = (FontInfo *) font;
447 PangoOTInfo *ot_info = pango_ot_info_get (font_info->face);
451 if (! otf_check_features (ot_info, PANGO_OT_TABLE_GSUB, spec, NULL)
452 || ! otf_check_features (ot_info, PANGO_OT_TABLE_GPOS, spec, NULL))
458 drive_otf (MFLTFont *font, MFLTOtfSpec *spec,
459 MFLTGlyphString *in, int from, int to,
460 MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment)
462 FontInfo *font_info = (FontInfo *) font;
463 PangoOTBuffer *buffer;
464 FeatureInfo *feature_info;
465 PangoGlyphString *glyphs;
468 buffer = pango_ot_buffer_new (font_info->pango_font);
469 for (i = from; i < to; i++)
470 pango_ot_buffer_add_glyph (buffer, in->glyphs[i].code, 0, i);
471 feature_info = get_feature_info (font_info, spec);
474 if (feature_info->ruleset[0])
475 pango_ot_ruleset_substitute (feature_info->ruleset[0], buffer);
476 if (feature_info->ruleset[1])
477 pango_ot_ruleset_position (feature_info->ruleset[1], buffer);
478 glyphs = pango_glyph_string_new ();
479 pango_ot_buffer_output (buffer, glyphs);
480 for (i = 0; i < glyphs->num_glyphs; i++)
482 PangoGlyphInfo *glyph_info = glyphs->glyphs + i;
485 out->glyphs[out->used] = in->glyphs[glyphs->log_clusters[i]];
486 g = out->glyphs + out->used++;
487 if (g->code != glyph_info->glyph)
490 g->code = glyph_info->glyph;
493 g->xoff = PANGO_SCALE_TO_26_6 (glyph_info->geometry.x_offset);
494 g->yoff = PANGO_SCALE_TO_26_6 (glyph_info->geometry.y_offset);
495 g->xadv = PANGO_SCALE_TO_26_6 (glyph_info->geometry.width);
497 adjustment[i].set = 0;
499 pango_ot_buffer_destroy (buffer);
500 pango_glyph_string_free (glyphs);
504 pango_ot_buffer_destroy (buffer);
505 for (i = from; i < to; i++)
506 out->glyphs[out->used++] = in->glyphs[i];
511 /* Shaper function. */
513 /* Shape TEXT of length GSTRING->used by a font in FONT_INFO, and
514 store the result in GLYPHS. GSTRING->glyphs is not yet allocated
515 but GSTRING->allocated tells how many glyphs to allocate. */
518 shape_text (const char *text, int *offsets, MFLTGlyphString *gstring,
519 FontInfo *font_info, PangoGlyphString *glyphs)
525 gstring->glyphs = g_newa (MFLTGlyph, gstring->allocated);
526 for (i = 0; i < gstring->used; i++)
527 gstring->glyphs[i].c = g_utf8_get_char (text + offsets[i]);
528 i = mflt_run (gstring, 0, gstring->used, (MFLTFont *) font_info, NULL);
532 pango_glyph_string_set_size (glyphs, gstring->used);
534 for (i = 0, g = glyphs->glyphs, mg = gstring->glyphs; i < gstring->used;
538 g->geometry.x_offset = PANGO_SCALE_FROM_26_6 (mg->xoff);
539 g->geometry.y_offset = PANGO_SCALE_FROM_26_6 (mg->yoff);
540 g->geometry.width = PANGO_SCALE_FROM_26_6 (mg->xadv);
541 g->attr.is_cluster_start = (i == 0 || mg->from != mg[-1].from);
542 glyphs->log_clusters[i]
543 = (g->attr.is_cluster_start ? offsets[mg->from]
544 : glyphs->log_clusters[i - 1]);
550 m17n_fc_engine_shape (PangoEngineShape *engine,
554 const PangoAnalysis *analysis,
555 PangoGlyphString *glyphs)
558 MFLTGlyphString gstring;
560 const char *p, *pend;
563 g_return_if_fail (font != NULL);
564 g_return_if_fail (text != NULL);
565 g_return_if_fail (length >= 0);
566 g_return_if_fail (analysis != NULL);
568 font_info = get_font_info (font);
570 gstring.glyph_size = sizeof (MFLTGlyph);
572 gstring.r2l = analysis->level % 2;
574 offsets = g_newa (int, length);
575 for (i = 0, p = text, pend = text + length; p < pend;
576 i++, p = g_utf8_next_char (p))
577 offsets[i] = p - text;
580 /* Try at most three times, each time with larger GSTRING->glyphs. */
581 gstring.allocated = length;
582 for (i = 0; i < 3; i++, gstring.allocated += length)
584 int result = shape_text (text, offsets, &gstring, font_info, glyphs);
588 g_return_if_fail (result >= 0);
596 /* Check if FONT is suitable for the character WC. For the moment,
597 this is commented out because even if it returns
598 PANGO_COVERAGE_NONE, the other engine will use the font. In
599 addition, the caller doesn't handle PANGO_COVERAGE_APPROXIMATE and
600 PANGO_COVERAGE_FALLBACK well. */
602 static PangoCoverageLevel
603 m17n_fc_engine_covers (PangoEngineShape *engine,
605 PangoLanguage *language,
608 FontInfo *font_info = get_font_info (font);
609 MFLT *flt = mflt_find (wc, (MFLTFont *) font_info);
610 const char *lang = pango_language_to_string (language);
615 g_debug (" %04X (%s/%s): no FLT\n", wc, lang,
616 msymbol_name (font_info->font.family));
617 return PANGO_COVERAGE_NONE;
620 g_debug (" %04X (%s/%s): FLT %s found\n", wc, lang,
621 msymbol_name (font_info->font.family), mflt_name (flt));
622 return PANGO_COVERAGE_EXACT;
628 m17n_engine_fc_class_init (PangoEngineShapeClass *class)
630 class->script_shape = m17n_fc_engine_shape;
632 class->covers = m17n_fc_engine_covers;
636 PANGO_ENGINE_SHAPE_DEFINE_TYPE (M17NEngineFc, m17n_engine_fc,
637 m17n_engine_fc_class_init, NULL)
640 PANGO_MODULE_ENTRY(init) (GTypeModule *module)
642 m17n_engine_fc_register_type (module);
644 if (getenv ("PANGO_M17N_DEBUG") != NULL)
649 PANGO_MODULE_ENTRY(exit) (void)
655 PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines,
658 *engines = script_engines;
659 *n_engines = G_N_ELEMENTS (script_engines);
663 PANGO_MODULE_ENTRY(create) (const char *id)
665 return (!strcmp (id, SCRIPT_ENGINE_NAME)
666 ? g_object_new (m17n_engine_fc_type, NULL)