(otf_check_features): Check the return value of
[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, 2006, 2007, 2008
5      National Institute of Advanced Industrial Science and Technology (AIST)
6      Registration Number H16PRO276
7
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.
12
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.
17
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.  */
22
23 #include <config.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27
28 #include <pango/pango-ot.h>
29 #include <pango/pango-engine.h>
30 #include <pango/pangofc-font.h>
31
32 #ifdef G_LOG_DOMAIN
33 #undef G_LOG_DOMAIN
34 #endif
35 #define G_LOG_DOMAIN "Pango-M17N-FC"
36
37 /**************************************/
38 /* Standard preamble for Pango module */
39 /**************************************/
40
41 /* No extra fields needed for these structures.  */
42 typedef PangoEngineShape      M17NEngineFc;
43 typedef PangoEngineShapeClass M17NEngineFcClass;
44
45 #define SCRIPT_ENGINE_NAME "M17NScriptEngineFc"
46 #define RENDER_TYPE PANGO_RENDER_TYPE_FC
47
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,       "*" },
87
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, "*" },
96
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,   "" }
105 };
106
107 static PangoEngineInfo script_engines[] = {
108   {
109     SCRIPT_ENGINE_NAME,
110     PANGO_ENGINE_TYPE_SHAPE,
111     RENDER_TYPE,
112     m17n_scripts, G_N_ELEMENTS (m17n_scripts)
113   }
114 };
115
116 /***************************/
117 /* Interface with m17n-flt */
118 /***************************/
119
120 #include <m17n-flt.h>
121 #include <m17n-misc.h>
122
123 /* Return a pointer to the IDXth glyph in GSTRING.  */
124 #define GREF(GSTRING, IDX) ((GSTRING)->glyphs + (IDX))
125
126 /* Convert Pango scale to 26.6 fractional pixel.  */
127 #define PANGO_SCALE_TO_26_6(N) ((N) / (PANGO_SCALE) * (1<<6))
128
129 /* Convert 26.6 fractional pixel to Pango scale.  */
130 #define PANGO_SCALE_FROM_26_6(N) ((N) * (PANGO_SCALE) / (1<<6))
131
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;
136
137
138 /* Structure containing information about OT features decoded from
139    MFLTOtfSpec.  */
140
141 typedef struct {
142   /* Rulesets for GSUB (1st element) and GPOS (2nd element).  */
143   PangoOTRuleset *ruleset[2];
144 } FeatureInfo;
145
146
147 /* Structure containing information about a font.  */
148
149 typedef struct {
150   MFLTFont font;
151   PangoFcFont *pango_font;
152   FT_Face face;
153   /* Cache that maps MFLTOtfSpec->sym to FeatureInfo.  */
154   MPlist *otf_spec_cache;
155 } FontInfo;
156
157
158 /* Prototypes for callback functions of FontInfo.font.  */
159
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 *);
166
167
168 /* This function works in two ways.
169
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
172    match).
173
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
176    are just ignored.
177
178    Features in SPEC are stored in an array in this format:
179         P P ... [ 0xFFFFFFFF [ N N ... ] ]
180    where
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),
183
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),
187
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).  */
190
191 static int
192 otf_check_features (PangoOTInfo *ot_info, PangoOTTableType type, 
193                     MFLTOtfSpec *spec, FeatureInfo *info)
194 {
195   unsigned int *features;
196   unsigned int langsys_tag;
197   guint script_idx, langsys_idx;
198   guint index;
199   guint *feature_list;
200   int i, j;
201   /* Used as an index to spec->features[] and info->ruleset[].  */
202   int for_gpos = type == PANGO_OT_TABLE_GPOS;
203
204   features = spec->features[for_gpos];
205   if (! features)
206     return 1;
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;
213   if (! langsys_tag)
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))
217     {
218       langsys_idx = PANGO_OT_DEFAULT_LANGUAGE;
219       index = 0xFFFF;
220     }
221
222   if (info && index != 0xFFFF)
223     {
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);
227     }
228
229   for (i = 0; features[i] && features[i] != 0xFFFFFFFF; i++)
230     {
231       if (pango_ot_info_find_feature (ot_info, type, features[i],
232                                       script_idx, langsys_idx, &index))
233         {
234           if (info)
235             {
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);
240             }
241         }
242       else
243         {
244           if (! info)
245             return 0;
246         }
247     }
248   if (! features[i])
249     /* No wildcard, no negative features.  */
250     return 1;
251
252   feature_list = pango_ot_info_list_features (ot_info, type, 0,
253                                               script_idx, langsys_idx);
254   if (! feature_list)
255     /* No feature for the specified script/language exist.  As the
256        remaining features are all negative, we can return 1.  */
257     return 1;
258
259   if (! info)
260     {
261       /* Check if the remaining features don't appear in
262          FEATURE_LIST.  */
263       for (i++; features[i]; i++)
264         for (j = 0; feature_list[j]; j++)
265           if (features[i] == feature_list[j])
266             return 0;
267       return 1;
268     }
269
270   /* Add all features in FEATURE_LIST except for those appearing in
271      FEATURES.  */
272   for (i = 0; feature_list[i]; i++)
273     {
274       guint tag = feature_list[i];
275
276       for (j = 0; features[j] && tag != features[j]; j++);
277       if (! features[j])
278         {
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);
285         }
286     }
287   return 1;
288 }
289
290
291 /* Return a FeatureInfo object (newly created if necessary) for the
292    font in FONT_INFO and for the OpenType feature specification in
293    SPEC.  */
294
295 static FeatureInfo *
296 get_feature_info (FontInfo *font_info, MFLTOtfSpec *spec)
297 {
298   FeatureInfo *feature_info;
299   PangoOTInfo *ot_info; 
300
301   if (! spec->features[0] && ! spec->features[1])
302     return NULL;
303   if (font_info->otf_spec_cache)
304     {
305       feature_info = mplist_get (font_info->otf_spec_cache, spec->sym);
306       if (feature_info)
307         return feature_info;
308     }
309   else
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);
316
317   return feature_info;
318 }
319
320
321 /* Free the object pointed by FONT_INFO.  */
322
323 static void
324 free_font_info (FontInfo *font_info)
325 {
326   if (font_info->otf_spec_cache)
327     {
328       MPlist *p;
329
330       for (p = font_info->otf_spec_cache; mplist_key (p) != Mnil;
331            p = mplist_next (p))
332         {
333           FeatureInfo *feature_info = mplist_value (p);
334
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]);
339         }
340       m17n_object_unref (font_info->otf_spec_cache);
341     }
342   pango_fc_font_unlock_face (font_info->pango_font);
343   g_free (font_info);
344 }
345
346
347 /* Return a FontInfo object corresponding to FONT.  It is newly
348    created if necessary.  */
349
350 static FontInfo *
351 get_font_info  (PangoFont *font)
352 {
353   FontInfo *font_info;
354   static GQuark info_id = 0;
355   
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))
360     {
361       PangoFontDescription *desc;
362       const char *family_name;
363       char *buf;
364       int len, i;
365
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);
371       if (family_name)
372         {
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);
378         }
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);
388     }
389   return font_info;
390 }
391
392 /* Four callback functions for MFLTFont.  */
393
394 static int
395 get_glyph_id (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
396 {
397   FontInfo *font_info = (FontInfo *) font;
398   MFLTGlyph *g = gstring->glyphs + from, *g_end = g + (to - from);
399   int err = 0;
400
401   for (; g < g_end; g++)
402     {
403       if (! g->encoded)
404         {
405           g->code = pango_fc_font_get_glyph (font_info->pango_font, g->code);
406           g->encoded = 1;
407           err |= (! g->code);
408         }
409     }
410   return err;
411 }
412
413 static int
414 get_metrics (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
415 {
416   FontInfo *font_info = (FontInfo *) font;
417   MFLTGlyph *g = gstring->glyphs + from, *g_end = g + (to - from);
418   int err = 0;
419
420   for (; g < g_end; g++)
421     {
422       if (! g->measured)
423         {
424           PangoRectangle inc, logical;
425
426           if (! g->encoded)
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);
433           g->yadv = 0;
434           g->ascent = - PANGO_SCALE_TO_26_6 (inc.y);
435           g->descent = PANGO_SCALE_TO_26_6 (inc.height + inc.y);
436           g->measured = 1;
437           err |= (! g->code);
438         }
439     }
440   return err;
441 }
442
443 static int
444 check_otf (MFLTFont *font, MFLTOtfSpec *spec)
445 {
446   FontInfo *font_info = (FontInfo *) font;
447   PangoOTInfo *ot_info = pango_ot_info_get (font_info->face);
448
449   if (! ot_info)
450     return 0;
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))
453     return 0;
454   return 1;
455 }
456
457 static int
458 drive_otf (MFLTFont *font, MFLTOtfSpec *spec,
459            MFLTGlyphString *in, int from, int to,
460            MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment)
461 {
462   FontInfo *font_info = (FontInfo *) font;
463   PangoOTBuffer *buffer;
464   FeatureInfo *feature_info;
465   PangoGlyphString *glyphs;
466   int i;
467
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);
472   if (! feature_info)
473     goto simple_copy;
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++)
481     {
482       PangoGlyphInfo *glyph_info = glyphs->glyphs + i;
483       MFLTGlyph *g;
484
485       out->glyphs[out->used] = in->glyphs[glyphs->log_clusters[i]];
486       g = out->glyphs + out->used++;
487       if (g->code != glyph_info->glyph)
488         {
489           g->c = 0;
490           g->code = glyph_info->glyph;
491           g->measured = 0;
492         }
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);
496       g->yadv = 0;
497       adjustment[i].set = 0;
498     }
499   pango_ot_buffer_destroy (buffer);
500   pango_glyph_string_free (glyphs);
501   return to;
502
503  simple_copy:
504   pango_ot_buffer_destroy (buffer);
505   for (i = from; i < to; i++)
506     out->glyphs[out->used++] = in->glyphs[i];
507   return to;
508 }
509
510
511 /* Shaper function.  */
512
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.  */
516
517 static int
518 shape_text (const char *text, int *offsets, MFLTGlyphString *gstring,
519             FontInfo *font_info, PangoGlyphString *glyphs)
520 {
521   int i;
522   PangoGlyphInfo *g;
523   MFLTGlyph *mg;
524
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);
529   if (i < 0)
530     return i;
531
532   pango_glyph_string_set_size (glyphs, gstring->used);
533
534   for (i = 0, g = glyphs->glyphs, mg = gstring->glyphs; i < gstring->used;
535        i++, g++, mg++)
536     {
537       g->glyph = mg->code;
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]);
545     }
546   return 0;
547 }
548
549 static void 
550 m17n_fc_engine_shape (PangoEngineShape *engine,
551                       PangoFont        *font,
552                       const char       *text,
553                       int              length,
554                       const PangoAnalysis    *analysis,
555                       PangoGlyphString *glyphs)
556 {
557   FontInfo *font_info;
558   MFLTGlyphString gstring;
559   int i;
560   const char *p, *pend;
561   int *offsets;
562
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);
567
568   font_info = get_font_info (font);
569
570   gstring.glyph_size = sizeof (MFLTGlyph);
571   gstring.used = 0;
572   gstring.r2l = analysis->level % 2;
573
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;
578   gstring.used = i;
579
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)
583     {
584       int result = shape_text (text, offsets, &gstring, font_info, glyphs);
585         
586       if (result != -2)
587         {
588           g_return_if_fail (result >= 0);
589           break;
590         }
591     }
592 }
593
594 #if 0
595
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.  */
601
602 static PangoCoverageLevel
603 m17n_fc_engine_covers (PangoEngineShape *engine,
604                        PangoFont        *font,
605                        PangoLanguage    *language,
606                        gunichar          wc)
607 {
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);
611
612   if (! flt)
613     {
614       if (debug_level)
615         g_debug (" %04X (%s/%s): no FLT\n", wc, lang,
616                  msymbol_name (font_info->font.family));
617       return PANGO_COVERAGE_NONE;
618     }
619   if (debug_level)
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;
623 }
624
625 #endif
626
627 static void
628 m17n_engine_fc_class_init (PangoEngineShapeClass *class)
629 {
630   class->script_shape = m17n_fc_engine_shape;
631 #if 0
632   class->covers = m17n_fc_engine_covers;
633 #endif
634 }
635
636 PANGO_ENGINE_SHAPE_DEFINE_TYPE (M17NEngineFc, m17n_engine_fc,
637                                 m17n_engine_fc_class_init, NULL)
638
639 void 
640 PANGO_MODULE_ENTRY(init) (GTypeModule *module)
641 {
642   m17n_engine_fc_register_type (module);
643   M17N_INIT ();
644   if (getenv ("PANGO_M17N_DEBUG") != NULL)
645     debug_level = 1;
646 }
647
648 void 
649 PANGO_MODULE_ENTRY(exit) (void)
650 {
651   M17N_FINI ();
652 }
653
654 void 
655 PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines,
656                           int              *n_engines)
657 {
658   *engines = script_engines;
659   *n_engines = G_N_ELEMENTS (script_engines);
660 }
661
662 PangoEngine *
663 PANGO_MODULE_ENTRY(create) (const char *id)
664 {
665   return (!strcmp (id, SCRIPT_ENGINE_NAME)
666           ? g_object_new (m17n_engine_fc_type, NULL)
667           : NULL);
668 }