4cf4d25d1eb52dce846e1a344181d8eaced32e56
[m17n/m17n-lib.git] / src / m17n-flt.c
1 /* m17n-flt.c -- Font Layout Table sub-module.
2    Copyright (C) 2003, 2004, 2007
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    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 m17n library; if not, write to the Free
20    Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21    02111-1307, USA.  */
22
23 /***en
24     @addtogroup m17nFLT
25     @brief FLT support for a window system.
26
27     This section defines the m17n FLT API concerning character
28     layouting facility using FLT (Font Layout Table).  The format of
29     FLT is described in @ref mdbFLT.  */
30
31 /***ja
32     @addtogroup m17nFLT
33     @brief ¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¤Î¤¿¤á¤Î FLT ¥µ¥Ý¡¼¥È.
34
35     ¤³¤Î¥»¥¯¥·¥ç¥ó¤Ç¤Ï¡¢FLT (Font Layout Table)
36     ¤òÍѤ¤¤¿Ê¸»ú¥ì¥¤¥¢¥¦¥Èµ¡Ç½¤Ë´Ø¤¹¤ë m17n FLT API ¤òÄêµÁ¤¹¤ë¡£
37     FLT ¤Î·Á¼°¤Ï @ref mdbFLT ¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤ë¡£  */
38
39 /*=*/
40
41 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
42 /*** @addtogroup m17nInternal
43      @{ */
44
45 #include "config.h"
46
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include <sys/types.h>
52 #include <regex.h>
53
54 #include "m17n-core.h"
55 #include "m17n-flt.h"
56 #include "m17n-misc.h"
57 #include "internal.h"
58 #include "mtext.h"
59 #include "symbol.h"
60 #include "plist.h"
61 #include "database.h"
62 #include "internal-flt.h"
63
64 /* Font Layouter */
65
66 /* Font Layout Table (FLT)
67
68 Predefined terms: SYMBOL, INTEGER, STRING
69
70 FLT ::= '(' STAGE + ')'
71
72 STAGE ::= CATEGORY-TABLE ? FONT-LAYOUT-RULE
73
74 ;; Each STAGE consumes a source (code sequence) and produces another
75 ;; code sequence that is given to the next STAGE as a source.  The
76 ;; source given to the first stage is a sequence of character codes
77 ;; that are assigned category codes by CATEGORY-TABLE.  The output of
78 ;; the last stage is a glyph code sequence given to the renderer.
79
80 CATEGORY-TABLE ::=
81         '(' 'category' CATEGORY-SPEC + ')'
82 CATEGORY-SPEC ::=
83         '(' CODE [ CODE ] CATEGORY ')'
84 CODE ::= INTEGER
85 CATEGORY ::= INTEGER
86 ;; ASCII character codes of alphabet ('A' .. 'Z' 'a' .. 'z').
87 ;; Ex: CATEGORY-TABLE
88 ;; (category
89 ;;   (0x0900 0x097F     ?E)     ; All Devanagari characters
90 ;;   (0x093C            ?N))    ; DEVANAGARI-LETTER NUKTA
91 ;;      Assign the category 'E' to all Devanagari characters but 0x093C,
92 ;;      assign the category 'N' to 0x093C.
93
94 FONT-LAYOUT-RULE ::=
95         '(' 'generator' RULE MACRO-DEF * ')'
96
97 RULE ::= COMMAND | REGEXP-RULE | MATCH-RULE | MAP-RULE
98          | COND-STRUCT | MACRO-NAME
99
100 COMMAND ::=
101         DIRECT-CODE | COMBINING | PREDEFIND-COMMAND | OTF-COMMAND
102
103 DIRECT-CODE ::= INTEGER
104 ;; Always succeed.  Produce the code.  Consume no source.
105
106 PREDEFIND-COMMAND ::=
107         '=' | '*' | '<' | '>' | '|'
108
109 ;; '=': Succeed when the current run contains at least one code.
110 ;; Consume the first code in the current run, and produce it as is.
111
112 ;; '*': If the the previous command succeeded, repeat it until it
113 ;; fails.  
114
115 ;; '<': Produce a special code that indicates the start of grapheme
116 ;; cluster.  Succeed always, consume nothing.
117
118 ;; '>': Produce a special code that indicates the end of grapheme
119 ;; cluster.  Succeed always, consume nothing.
120
121 ;; '|': Produce a special code whose category is ' '.  Succeed always,
122 ;; consume nothing.
123
124 OTF-COMMAND ::=
125         'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]]
126 ;; Run the Open Type Layout Table on the current run.  Succeed always,
127 ;; consume nothing.
128
129 SCRIPT ::= OTF-TAG
130 ;;      OTF's ScriptTag name (four letters) listed at:
131 ;;      <http://www.microsoft.om/typograph/otspec/scripttags.htm>
132 LANGSYS ::= OTF-TAG
133 ;;      OTF's Language System name (four letters) listed at:
134 ;;      <http://www.microsoft.om/typograph/otspec/languagetags.htm>
135
136 GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
137 GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
138 FEATURE ::= OTF-TAG
139 ;;      OTF's Feature name (four letters) listed at:
140 ;;      <http://www.microsoft.om/typograph/otspec/???.htm>
141
142 OTF-TAG ::= PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR
143
144 ;; Ex. OTF-COMMAND
145 ;; 'otf:deva'
146 ;;      Run all features in the default langsys of 'deva' script.
147 ;; 'otf:deva::nukt:haln'
148 ;;      Run all GSUB features, run 'nukt' and 'haln' GPOS features.
149 ;; 'otf:deva:: :'
150 ;;      Run all GSUB features, run no GPOS features.
151
152 REGEXP-RULE ::=
153         '(' REGEXP RULE * ')'
154
155 ;; Succeed if REGXP matches the head of source.  Run RULEs while
156 ;; limiting the source to the matching part.  Consume that part.
157
158 REGEXP ::= STRING
159 ;; Must be composed only from ASCII characters.  'A' - 'Z', 'a' - 'z'
160 ;; correspond to CATEGORY.
161
162 ;; Ex: REGEXP-RULE
163 ;; ("VA?"
164 ;;   < | vowel * | >)
165
166 MATCH-RULE ::=
167         '(' MATCH-IDX RULE * ')'
168
169 ;; Succeed if the previous REGEXP-RULE found a matching part for
170 ;; MATCH-IDX.  Run RULEs while limiting the source to the matching
171 ;; part.  If MATCH-IDX is zero, consume the whole part, else consume
172 ;; nothing.
173
174 MATCH-IDX ::= INTEGER
175 ;; Must be 0..20.
176
177 ;; Ex. MATCH-RULE
178 ;; (2 consonant *)
179
180 MAP-RULE ::=
181         '(' ( SOURCE-SEQ | SOURCE-RANGE ) RULE * ')'
182
183 ;; Succeed if the source matches SOURCE-SEQ or SOURCE-RANGE.  Run
184 ;; RULEs while limiting the source to the matching part.  Consume that
185 ;; part.
186
187 SOURCE-SEQ ::=
188         '(' CODE + ')'
189 SOURCE-RANGE ::=
190         '(' 'range' CODE CODE ')'
191 ;; Ex. MAP-RULE
192 ;; ((0x0915 0x094D)             0x43)
193 ;;      If the source code sequence is 0x0915 0x094D, produce 0x43.
194 ;; ((range 0x0F40 0x0F6A)       0x2221)
195 ;;      If the first source code CODE is in the range 0x0F40..0x0F6A, 
196 ;;      produce (0x2221 + (CODE - 0x0F40)).
197
198 COND-STRUCT ::=
199         '(' 'cond' RULE + ')'
200
201 ;; Try each rule in sequence until one succeeds.  Succeed if one
202 ;; succeeds.  Consume nothing.
203
204 ;; Ex. COND-STRUCT
205 ;; (cond
206 ;;  ((0x0915 0x094D)            0x43)
207 ;;  ((range 0x0F40 0x0F6A)      0x2221)
208 ;;  = )
209
210 COMBINING ::= 'V''H''O''V''H'
211 V ::= ( 't' | 'c' | 'b' | 'B' )
212 H ::= ( 'l' | 'c' | 'r' )
213 O ::= ( '.' | XOFF | YOFF | XOFF YOFF )
214 XOFF ::= '<'INTEGER | '>'INTEGER 
215 YOFF ::= '+'INTEGER | '-'INTEGER
216 ;; INTEGER must be integer 0..127
217
218 ;; VH pair indicates 12 reference points of a glyph as below:
219 ;;
220 ;;   0----1----2 <---- ascent    0:tl (top-left)
221 ;;   |         |                 1:tc (top-center)
222 ;;   |         |                 2:tr (top-right)
223 ;;   |         |                 3:Bl (base-left)
224 ;;   9   10   11 <---- center    4:Bc (base-center)
225 ;;   |         |                 5:Br (base-right)
226 ;; --3----4----5-- <-- baseline  6:bl (bottom-left)
227 ;;   |         |                 7:bc (bottom-center)
228 ;;   6----7----8 <---- descent   8:br (bottom-right)
229 ;;                               9:cl (center-left)
230 ;;   |    |    |                10:cc (center-center)
231 ;; left center right            11:cr (center-right)
232 ;;
233 ;; Ex. COMBINING
234 ;; 'tc.bc':
235 ;;      Align top-left point of the previous glyph and bottom-center
236 ;;      point of the current glyph.
237 ;; 'Bl<20-10Br'
238 ;;      Align 20% left and 10% below of base-left point of the previous
239 ;;      glyph and base-right point of the current glyph.
240
241 MACRO-DEF ::=
242         '(' MACRO-NAME RULE + ')'
243 MACRO-NAME ::= SYMBOL
244
245 */
246
247 static int mdebug_flag = MDEBUG_FLT;
248
249 MSymbol Mfont, Mlayouter, Mcombining;
250
251 static MSymbol Mgenerator, Mend;
252
253 static MPlist *flt_list;
254 static int flt_min_coverage, flt_max_coverage;
255
256 enum GlyphInfoMask
257 {
258   CombiningCodeMask = 0xFFFFFFF,
259   LeftPaddingMask = 1 << 28,
260   RightPaddingMask = 1 << 29
261 };
262
263 #define SET_GLYPH_INFO(g, mask, ctx, info)                      \
264   ((g)->internal = (((g)->internal & ~(mask)) | (info)),        \
265    (ctx)->check_mask |= (mask))
266
267 #define GET_COMBINING_CODE(g) ((g)->internal & CombiningCodeMask)
268 #define SET_COMBINING_CODE(g, ctx, code)        \
269   SET_GLYPH_INFO (g, CombiningCodeMask, ctx, code)
270 #define GET_LEFT_PADDING(g) ((g)->internal & LeftPaddingMask)
271 #define SET_LEFT_PADDING(g, ctx, flag)  \
272   SET_GLYPH_INFO (g, LeftPaddingMask, ctx, flag)
273 #define GET_RIGHT_PADDING(g) ((g)->internal & RightPaddingMask)
274 #define SET_RIGHT_PADDING(g, ctx, flag) \
275   SET_GLYPH_INFO (g, RightPaddingMask, ctx, flag)
276 #define GET_ENCODED(g) ((g)->encoded)
277 #define SET_ENCODED(g, flag) ((g)->encoded = (flag))
278 #define GET_MEASURED(g) ((g)->measured)
279 #define SET_MEASURED(g, flag) ((g)->measured = (flag))
280
281 #define GINIT(gstring, n)                                       \
282   do {                                                          \
283     if (! (gstring)->glyph_size)                                \
284       (gstring)->glyph_size = sizeof (MFLTGlyph);               \
285     (gstring)->glyphs = alloca ((gstring)->glyph_size * (n));   \
286     (gstring)->allocated = (n);                                 \
287     (gstring)->used = 0;                                        \
288   } while (0)
289
290 #define GALLOCA (gstring)       \
291   ((MFLTGlyph *) alloca ((gstring)->glyph_size))
292
293 #define GREF(gstring, idx)      \
294   ((MFLTGlyph *) ((char *) ((gstring)->glyphs) + (gstring)->glyph_size * (idx)))
295
296 #define PREV(gstring, g)        \
297   ((MFLTGlyph *) ((char *) (g) - (gstring)->glyph_size))
298
299 #define NEXT(gstring, g)        \
300   ((MFLTGlyph *) ((char *) (g) + (gstring)->glyph_size))
301
302 #define GCPY(src, src_idx, n, tgt, tgt_idx)                             \
303   do {                                                                  \
304     memcpy ((char *) ((tgt)->glyphs) + (tgt)->glyph_size * (tgt_idx),   \
305             (char *) ((src)->glyphs) + (src)->glyph_size * (src_idx),   \
306             (src)->glyph_size * (n));                                   \
307   } while (0)
308
309 #define GDUP(ctx, idx)                          \
310   do {                                          \
311     MFLTGlyphString *src = (ctx)->in;           \
312     MFLTGlyphString *tgt = (ctx)->out;          \
313     if (tgt->allocated <= tgt->used)            \
314       return -2;                                \
315     GCPY (src, (idx), 1, tgt, tgt->used);       \
316     tgt->used++;                                \
317   } while (0)
318
319 static int
320 GREPLACE (MFLTGlyphString *src, int src_from, int src_to,
321           MFLTGlyphString *tgt, int tgt_from, int tgt_to)
322 {
323   int src_len = src_to - src_from;
324   int tgt_len = tgt_to - tgt_from;
325   int inc = src_len - tgt_len;
326
327   if (tgt->allocated < tgt->used + inc)
328     return -2;
329   if (inc != 0 && tgt_to < tgt->used)
330     memmove ((char *) tgt->glyphs + tgt->glyph_size * (tgt_from + src_len),
331              (char *) tgt->glyphs + tgt->glyph_size * tgt_to,
332              tgt->glyph_size * (tgt->used - tgt_to));
333   if (src_len)
334     memcpy ((char *) tgt->glyphs + tgt->glyph_size * tgt_from,
335             (char *) src->glyphs + src->glyph_size * src_from,
336             src->glyph_size * src_len);
337   tgt->used += inc;
338   return 0;
339 }
340
341
342 /* Command ID:
343                  0 ...          : direct code
344                    -1           : invalid
345              -0x0F .. -2        : builtin commands
346         -0x100000F .. -0x10     : combining code
347                   ... -0x1000010: index to FontLayoutStage->cmds
348  */
349
350 #define INVALID_CMD_ID -1
351 #define CMD_ID_OFFSET_BUILTIN   -3
352 #define CMD_ID_OFFSET_COMBINING -0x10
353 #define CMD_ID_OFFSET_INDEX     -0x1000010
354
355 /* Builtin commands. */
356 #define CMD_ID_COPY             -3 /* '=' */
357 #define CMD_ID_REPEAT           -4 /* '*' */
358 #define CMD_ID_CLUSTER_BEGIN    -5 /* '<' */
359 #define CMD_ID_CLUSTER_END      -6 /* '>' */
360 #define CMD_ID_SEPARATOR        -7 /* '|' */
361 #define CMD_ID_LEFT_PADDING     -8 /* '[' */
362 #define CMD_ID_RIGHT_PADDING    -9 /* ']' */
363
364 #define CMD_ID_TO_COMBINING_CODE(id) (CMD_ID_OFFSET_COMBINING - (id))
365 #define COMBINING_CODE_TO_CMD_ID(code) (CMD_ID_OFFSET_COMBINING - (code))
366
367 #define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id))
368 #define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx))
369
370 static MSymbol Mcond, Mrange, Mfont_facility;
371
372 #define GLYPH_CODE_P(code)      \
373   ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX)
374
375 #define GLYPH_CODE_INDEX(code) ((code) - GLYPH_CODE_MIN)
376
377 #define UPDATE_CLUSTER_RANGE(ctx, g)            \
378   do {                                          \
379     if ((ctx)->cluster_begin_idx >= 0)          \
380       {                                         \
381         if (ctx->cluster_begin_pos > (g)->from) \
382           ctx->cluster_begin_pos = (g)->from;   \
383         if (ctx->cluster_end_pos < (g)->to)     \
384           ctx->cluster_end_pos = (g)->to;       \
385       }                                         \
386   } while (0)
387
388 enum FontLayoutCmdRuleSrcType
389   {
390     SRC_REGEX,
391     SRC_INDEX,
392     SRC_SEQ,
393     SRC_RANGE,
394     SRC_HAS_GLYPH,
395     SRC_OTF_SPEC
396   };
397
398 typedef struct
399 {
400   enum FontLayoutCmdRuleSrcType src_type;
401   union {
402     struct {
403       char *pattern;
404       regex_t preg;
405     } re;
406     int match_idx;
407     struct {
408       int n_codes;
409       int *codes;
410     } seq;
411     struct {
412       int from, to;
413     } range;
414     int supported_glyph;
415     MFLTOtfSpec otf_spec;
416   } src;
417
418   int n_cmds;
419   int *cmd_ids;
420 } FontLayoutCmdRule;
421
422 typedef struct
423 {
424   /* Beginning and end indices of series of SEQ commands.  */
425   int seq_beg, seq_end;
426   /* Range of the first character appears in the above series.  */
427   int seq_from, seq_to;
428
429   int n_cmds;
430   int *cmd_ids;
431 } FontLayoutCmdCond;
432
433 enum FontLayoutCmdType
434   {
435     FontLayoutCmdTypeRule,
436     FontLayoutCmdTypeCond,
437     FontLayoutCmdTypeOTF,
438     FontLayoutCmdTypeMAX
439   };
440
441 typedef struct
442 {
443   enum FontLayoutCmdType type;
444   union {
445     FontLayoutCmdRule rule;
446     FontLayoutCmdCond cond;
447     MFLTOtfSpec otf;
448   } body;
449 } FontLayoutCmd;
450
451 typedef struct 
452 {
453   MCharTable *category;
454   int size, inc, used;
455   FontLayoutCmd *cmds;
456 } FontLayoutStage;
457
458 struct _MFLT
459 {
460   MSymbol name;
461   MSymbol family;
462   MSymbol registry;
463   MFLTOtfSpec otf;
464   MDatabase *mdb;
465   MCharTable *coverage;
466   MPlist *stages;
467 };
468
469 /* Font layout table loader */
470
471 static int parse_otf_command (MSymbol symbol, MFLTOtfSpec *spec);
472
473 /* Load a category table from PLIST.  PLIST has this form:
474       PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
475 */
476
477 static MCharTable *
478 load_category_table (MPlist *plist)
479 {
480   MCharTable *table;
481
482   table = mchartable (Minteger, (void *) 0);
483
484   MPLIST_DO (plist, plist)
485     {
486       MPlist *elt;
487       int from, to, category_code;
488
489       if (! MPLIST_PLIST (plist))
490         MERROR (MERROR_FONT, NULL);
491       elt = MPLIST_PLIST (plist);
492       if (! MPLIST_INTEGER_P (elt))
493         MERROR (MERROR_FONT, NULL);
494       from = MPLIST_INTEGER (elt);
495       elt = MPLIST_NEXT (elt);
496       if (! MPLIST_INTEGER_P (elt))
497         MERROR (MERROR_FONT, NULL);
498       to = MPLIST_INTEGER (elt);
499       elt = MPLIST_NEXT (elt);
500       if (MPLIST_TAIL_P (elt))
501         {
502           category_code = to;
503           to = from;
504         }
505       else
506         {
507           if (! MPLIST_INTEGER_P (elt))
508             MERROR (MERROR_FONT, NULL);
509           category_code = MPLIST_INTEGER (elt);
510         }
511       if (! isalnum (category_code))
512         MERROR (MERROR_FONT, NULL);
513
514       if (from == to)
515         mchartable_set (table, from, (void *) category_code);
516       else
517         mchartable_set_range (table, from, to, (void *) category_code);
518     }
519
520   return table;
521 }
522
523 static unsigned int
524 gen_otf_tag (char *p)
525 {
526   unsigned int tag = 0;
527   int i;
528
529   for (i = 0; i < 4 && *p; i++, p++)
530     tag = (tag << 8) | *p;
531   for (; i < 4; i++)
532     tag = (tag << 8) | 0x20;
533   return tag;
534 }
535
536 static char *
537 otf_count_features (char *p, char *end, char stopper, int *count)
538 {
539   int negative = 0;
540
541   *count = 0;
542   if (*p != stopper && *p != '\0')
543     while (1)
544       {
545         (*count)++;
546         if (*p == '*')
547           {
548             p++;
549             if (*p == stopper || *p == '\0')
550               break;
551             return NULL;
552           }
553         if (*p == '~')
554           {
555             if (negative++ == 0)
556               (*count)++;
557             p += 5;
558           }
559         else 
560           p += 4;
561         if (p > end)
562           return NULL;
563         if (*p == stopper || *p == '\0')
564           break;
565         if (*p != ',')
566           return NULL;
567         p++;
568         if (! *p)
569           return NULL;
570       }
571   return p;
572 }
573
574 static void
575 otf_store_features (char *p, char *end, unsigned *buf)
576 {
577   int negative = 0;
578   int i;
579
580   for (i = 0; p < end;)
581     {
582       if (*p == '*')
583         buf[i++] = 0xFFFFFFFF, p += 2, negative = 1;
584       else if (*p == '~')
585         {
586           if (negative++ == 0)
587             buf[i++] = 0xFFFFFFFF;
588           buf[i++] = gen_otf_tag (p + 1), p += 6;
589         }
590       else
591         buf[i++] = gen_otf_tag (p), p += 5;
592     }
593   buf[i] = 0;
594 }
595
596 static int
597 parse_otf_command (MSymbol symbol, MFLTOtfSpec *spec)
598 {
599   char *str = MSYMBOL_NAME (symbol);
600   char *end = str + MSYMBOL_NAMELEN (symbol);
601   unsigned int script, langsys;
602   char *gsub, *gpos;
603   int gsub_count = 0, gpos_count = 0;
604   char *p;
605
606   memset (spec, 0, sizeof (MFLTOtfSpec));
607
608   spec->sym = symbol;
609   str += 5;                     /* skip the heading ":otf=" */
610   script = gen_otf_tag (str);
611   str += 4;
612   if (*str == '/')
613     {
614       langsys = gen_otf_tag (str);
615       str += 4;
616     }
617   else
618     langsys = 0;
619   gsub = str;
620   if (*str != '=')
621     /* Apply all GSUB features.  */
622       gsub_count = 1;
623   else
624     {
625       p = str + 1;
626       str = otf_count_features (p, end, '+', &gsub_count);
627       if (! str)
628         MERROR (MERROR_FLT, -1);
629     }
630   gpos = str;
631   if (*str != '+')
632     /* Apply all GPOS features.  */
633     gpos_count = 1;
634   else
635     {
636       p = str + 1;
637       str = otf_count_features (p, end, '\0', &gpos_count);
638       if (! str)
639         MERROR (MERROR_FLT, -1);
640     }
641
642   spec->script = script;
643   spec->langsys = langsys;
644   if (gsub_count > 0)
645     {
646       spec->features[0] = malloc (sizeof (int) * (gsub_count + 1));
647       if (! spec->features[0])
648         return -2;
649       if (*gsub == '=')
650         otf_store_features (gsub + 1, gpos, spec->features[0]);
651       else
652         spec->features[0][0] = 0xFFFFFFFF, spec->features[0][1] = 0;
653     }
654   if (gpos_count > 0)
655     {
656       spec->features[1] = malloc (sizeof (int) * (gpos_count + 1));
657       if (! spec->features[1])
658         {
659           if (spec->features[0])
660             free (spec->features[0]);
661           return -2;
662         }
663       if (*gpos == '+')
664         otf_store_features (gpos + 1, str, spec->features[1]);
665       else
666         spec->features[1][0] = 0xFFFFFFFF, spec->features[1][1] = 0;
667     }
668   return 0;
669 }
670
671
672 /* Parse OTF command name NAME and store the result in CMD.
673    NAME has this form:
674         :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
675    where GSUB-FEATURES and GPOS-FEATURES have this form:
676         [FEATURE[,FEATURE]*] | ' '  */
677
678 static int
679 load_otf_command (FontLayoutCmd *cmd, MSymbol sym)
680 {
681   char *name = MSYMBOL_NAME (sym);
682   int result;
683
684   if (name[0] != ':')
685     {
686       /* This is old format of "otf:...".  Change it to ":otf=...".  */
687       char *str = alloca (MSYMBOL_NAMELEN (sym) + 2);
688
689       sprintf (str, ":otf=");
690       strcat (str, name + 4);
691       sym = msymbol (str);
692     }
693
694   result = parse_otf_command (sym, &cmd->body.otf);
695   if (result == -2)
696     return result;
697   cmd->type = FontLayoutCmdTypeOTF;
698   return 0;
699 }
700
701
702 /* Read a decimal number from STR preceded by one of "+-><".  '+' and
703    '>' means a plus sign, '-' and '<' means a minus sign.  If the
704    number is greater than 127, limit it to 127.  */
705
706 static int
707 read_decimal_number (char **str)
708 {
709   char *p = *str;
710   int sign = (*p == '-' || *p == '<') ? -1 : 1;
711   int n = 0;
712
713   p++;
714   while (*p >= '0' && *p <= '9')
715     n = n * 10 + *p++ - '0';
716   *str = p;
717   if (n == 0)
718     n = 5;
719   return (n < 127 ? n * sign : 127 * sign);
720 }
721
722
723 /* Read a horizontal and vertical combining positions from STR, and
724    store them in the place pointed by X and Y.  The horizontal
725    position left, center, and right are represented by 0, 1, and 2
726    respectively.  The vertical position top, center, bottom, and base
727    are represented by 0, 1, 2, and 3 respectively.  If successfully
728    read, return 0, else return -1.  */
729
730 static int
731 read_combining_position (char *str, int *x, int *y)
732 {
733   int c = *str++;
734   int i;
735
736   /* Vertical position comes first.  */
737   for (i = 0; i < 4; i++)
738     if (c == "tcbB"[i])
739       {
740         *y = i;
741         break;
742       }
743   if (i == 4)
744     return -1;
745   c = *str;
746   /* Then comse horizontal position.  */
747   for (i = 0; i < 3; i++)
748     if (c == "lcr"[i])
749       {
750         *x = i;
751         return 0;
752       }
753   return -1;
754 }
755
756
757 /* Return a combining code corresponding to SYM.  */
758
759 static int
760 get_combining_command (MSymbol sym)
761 {
762   char *str = msymbol_name (sym);
763   int base_x, base_y, add_x, add_y, off_x, off_y;
764   int c;
765
766   if (read_combining_position (str, &base_x, &base_y) < 0)
767     return 0;
768   str += 2;
769   c = *str;
770   if (c == '.')
771     {
772       off_x = off_y = 128;
773       str++;
774     }
775   else
776     {
777       if (c == '+' || c == '-')
778         {
779           off_y = read_decimal_number (&str) + 128;
780           c = *str;
781         }
782       else
783         off_y = 128;
784       if (c == '<' || c == '>')
785         off_x = read_decimal_number (&str) + 128;
786       else
787         off_x = 128;
788     }
789   if (read_combining_position (str, &add_x, &add_y) < 0)
790     return 0;
791
792   c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
793   return (COMBINING_CODE_TO_CMD_ID (c));
794 }
795
796
797 /* Load a command from PLIST into STAGE, and return that
798    identification number.  If ID is not INVALID_CMD_ID, that means we
799    are loading a top level command or a macro.  In that case, use ID
800    as the identification number of the command.  Otherwise, generate a
801    new id number for the command.  MACROS is a list of raw macros.  */
802
803 static int
804 load_command (FontLayoutStage *stage, MPlist *plist,
805               MPlist *macros, int id)
806 {
807   int i;
808   int result;
809
810   if (MPLIST_INTEGER_P (plist))
811     {
812       int code = MPLIST_INTEGER (plist);
813
814       if (code < 0)
815         MERROR (MERROR_DRAW, INVALID_CMD_ID);
816       return code;
817     }
818   else if (MPLIST_PLIST_P (plist))
819     {
820       /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
821                    | ( ( INTEGER INTEGER ) ... )
822                    | ( ( range INTEGER INTEGER ) ... )
823                    | ( ( font-facilty [ INTEGER ] ) ... )
824                    | ( ( font-facilty OTF-SPEC ) ... )  */
825       MPlist *elt = MPLIST_PLIST (plist);
826       int len = MPLIST_LENGTH (elt) - 1;
827       FontLayoutCmd *cmd;
828
829       if (id == INVALID_CMD_ID)
830         {
831           FontLayoutCmd dummy;
832           id = INDEX_TO_CMD_ID (stage->used);
833           MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
834         }
835       cmd = stage->cmds + CMD_ID_TO_INDEX (id);
836
837       if (MPLIST_SYMBOL_P (elt))
838         {
839           FontLayoutCmdCond *cond;
840
841           if (MPLIST_SYMBOL (elt) != Mcond)
842             MERROR (MERROR_DRAW, INVALID_CMD_ID);
843           elt = MPLIST_NEXT (elt);
844           cmd->type = FontLayoutCmdTypeCond;
845           cond = &cmd->body.cond;
846           cond->seq_beg = cond->seq_end = -1;
847           cond->seq_from = cond->seq_to = 0;
848           cond->n_cmds = len;
849           MTABLE_CALLOC (cond->cmd_ids, len, MERROR_DRAW);
850           for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
851             {
852               int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
853
854               if (this_id == INVALID_CMD_ID || this_id == -2)
855                 MERROR (MERROR_DRAW, this_id);
856               /* The above load_command may relocate stage->cmds.  */
857               cmd = stage->cmds + CMD_ID_TO_INDEX (id);
858               cond = &cmd->body.cond;
859               cond->cmd_ids[i] = this_id;
860               if (this_id <= CMD_ID_OFFSET_INDEX)
861                 {
862                   FontLayoutCmd *this_cmd
863                     = stage->cmds + CMD_ID_TO_INDEX (this_id);
864
865                   if (this_cmd->type == FontLayoutCmdTypeRule
866                       && this_cmd->body.rule.src_type == SRC_SEQ)
867                     {
868                       int first_char = this_cmd->body.rule.src.seq.codes[0];
869
870                       if (cond->seq_beg < 0)
871                         {
872                           /* The first SEQ command.  */
873                           cond->seq_beg = i;
874                           cond->seq_from = cond->seq_to = first_char;
875                         }
876                       else if (cond->seq_end < 0)
877                         {
878                           /* The following SEQ command.  */
879                           if (cond->seq_from > first_char)
880                             cond->seq_from = first_char;
881                           else if (cond->seq_to < first_char)
882                             cond->seq_to = first_char;
883                         }
884                     }
885                   else
886                     {
887                       if (cond->seq_beg >= 0 && cond->seq_end < 0)
888                         /* The previous one is the last SEQ command.  */
889                         cond->seq_end = i;
890                     }
891                 }
892               else
893                 {
894                   if (cond->seq_beg >= 0 && cond->seq_end < 0)
895                     /* The previous one is the last SEQ command.  */
896                     cond->seq_end = i;
897                 }
898             }
899           if (cond->seq_beg >= 0 && cond->seq_end < 0)
900             /* The previous one is the last SEQ command.  */
901             cond->seq_end = i;
902         }
903       else
904         {
905           cmd->type = FontLayoutCmdTypeRule;
906           if (MPLIST_MTEXT_P (elt))
907             {
908               MText *mt = MPLIST_MTEXT (elt);
909               char *str = (char *) MTEXT_DATA (mt);
910
911               if (str[0] != '^')
912                 {
913                   mtext_ins_char (mt, 0, '^', 1);
914                   str = (char *) MTEXT_DATA (mt);
915                 }
916               if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
917                 MERROR (MERROR_FONT, INVALID_CMD_ID);
918               cmd->body.rule.src_type = SRC_REGEX;
919               cmd->body.rule.src.re.pattern = strdup (str);
920             }
921           else if (MPLIST_INTEGER_P (elt))
922             {
923               cmd->body.rule.src_type = SRC_INDEX;
924               cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
925             }
926           else if (MPLIST_PLIST_P (elt))
927             {
928               MPlist *pl = MPLIST_PLIST (elt);
929               int size = MPLIST_LENGTH (pl);
930
931               if (MPLIST_INTEGER_P (pl))
932                 {
933                   int i;
934
935                   cmd->body.rule.src_type = SRC_SEQ;
936                   cmd->body.rule.src.seq.n_codes = size;
937                   MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
938                                  MERROR_FONT);
939                   for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
940                     {
941                       if (! MPLIST_INTEGER_P (pl))
942                         MERROR (MERROR_DRAW, INVALID_CMD_ID);
943                       cmd->body.rule.src.seq.codes[i]
944                         = (unsigned) MPLIST_INTEGER (pl);
945                     }
946                 }
947               else if (MPLIST_SYMBOL_P (pl) && size == 3)
948                 {
949                   if (MPLIST_SYMBOL (pl) != Mrange)
950                     MERROR (MERROR_FLT, INVALID_CMD_ID);
951                   cmd->body.rule.src_type = SRC_RANGE;
952                   pl = MPLIST_NEXT (pl);
953                   if (! MPLIST_INTEGER_P (pl))
954                     MERROR (MERROR_DRAW, INVALID_CMD_ID);
955                   cmd->body.rule.src.range.from
956                     = (unsigned) MPLIST_INTEGER (pl);
957                   pl = MPLIST_NEXT (pl);
958                   if (! MPLIST_INTEGER_P (pl))
959                     MERROR (MERROR_DRAW, INVALID_CMD_ID);
960                   cmd->body.rule.src.range.to
961                     = (unsigned) MPLIST_INTEGER (pl);
962                 }
963               else if (MPLIST_SYMBOL_P (pl) && size <= 2)
964                 {
965                   if (MPLIST_SYMBOL (pl) != Mfont_facility)
966                     MERROR (MERROR_FLT, INVALID_CMD_ID);
967                   pl = MPLIST_NEXT (pl);
968                   if (MPLIST_SYMBOL_P (pl))
969                     {
970                       MSymbol sym = MPLIST_SYMBOL (pl);
971                       char *otf_spec = MSYMBOL_NAME (sym);
972
973                       if (otf_spec[0] == ':' && otf_spec[1] == 'o'
974                           && otf_spec[2] == 't' && otf_spec[3] == 'f')
975                         parse_otf_command (sym, &cmd->body.rule.src.otf_spec);
976                       else
977                         MERROR (MERROR_FLT, INVALID_CMD_ID);
978                       cmd->body.rule.src_type = SRC_OTF_SPEC;
979                     }
980                   else
981                     {
982                       cmd->body.rule.src_type = SRC_HAS_GLYPH;
983                       if (MPLIST_INTEGER_P (pl))
984                         cmd->body.rule.src.supported_glyph
985                           = MPLIST_INTEGER (pl);
986                       else
987                         cmd->body.rule.src.supported_glyph = -1;
988                     }
989                 }
990               else
991                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
992             }
993           else
994             MERROR (MERROR_DRAW, INVALID_CMD_ID);
995
996           elt = MPLIST_NEXT (elt);
997           cmd->body.rule.n_cmds = len;
998           MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
999           for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
1000             {
1001               int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
1002
1003               if (this_id == INVALID_CMD_ID || this_id == -2)
1004                 MERROR (MERROR_DRAW, this_id);
1005               /* The above load_command may relocate stage->cmds.  */
1006               cmd = stage->cmds + CMD_ID_TO_INDEX (id);
1007               cmd->body.rule.cmd_ids[i] = this_id;
1008             }
1009         }
1010     }
1011   else if (MPLIST_SYMBOL_P (plist))
1012     {
1013       MPlist *elt;
1014       MSymbol sym = MPLIST_SYMBOL (plist);
1015       char *name = msymbol_name (sym);
1016       int len = strlen (name);
1017       FontLayoutCmd cmd;
1018
1019       if (len > 4
1020           && ((name[0] == 'o' && name[1] == 't'
1021                && name[2] == 'f' && name[3] == ':')
1022               || (name[0] == ':' && name[1] == 'o' && name[2] == 't'
1023                   && name[3] == 'f' && name[4] == '=')))
1024         {
1025           result = load_otf_command (&cmd, sym);
1026           if (result < 0)
1027             return result;
1028           if (id == INVALID_CMD_ID)
1029             {
1030               id = INDEX_TO_CMD_ID (stage->used);
1031               MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
1032             }
1033           else
1034             stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
1035           return id;
1036         }
1037
1038       if (len == 1)
1039         {
1040           if (*name == '=')
1041             return CMD_ID_COPY;
1042           else if (*name == '*')
1043             return CMD_ID_REPEAT;
1044           else if (*name == '<')
1045             return CMD_ID_CLUSTER_BEGIN;
1046           else if (*name == '>')
1047             return CMD_ID_CLUSTER_END;
1048           else if (*name == '|')
1049             return CMD_ID_SEPARATOR;
1050           else if (*name == '[')
1051             return CMD_ID_LEFT_PADDING;
1052           else if (*name == ']')
1053             return CMD_ID_RIGHT_PADDING;
1054           else
1055             id = 0;
1056         }
1057       else
1058         {
1059           id = get_combining_command (sym);
1060           if (id)
1061             return id;
1062         }
1063
1064       i = 1;
1065       MPLIST_DO (elt, macros)
1066         {
1067           if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
1068             {
1069               id = INDEX_TO_CMD_ID (i);
1070               if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
1071                 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
1072                                    macros, id);
1073               return id;
1074             }
1075           i++;
1076         }
1077       MERROR (MERROR_DRAW, INVALID_CMD_ID);
1078     }
1079   else
1080     MERROR (MERROR_DRAW, INVALID_CMD_ID);
1081
1082   return id;
1083 }
1084
1085 static void
1086 free_flt_command (FontLayoutCmd *cmd)
1087 {
1088   if (cmd->type == FontLayoutCmdTypeRule)
1089     {
1090       FontLayoutCmdRule *rule = &cmd->body.rule;
1091
1092       if (rule->src_type == SRC_REGEX)
1093         {
1094           free (rule->src.re.pattern);
1095           regfree (&rule->src.re.preg);
1096         }
1097       else if (rule->src_type == SRC_SEQ)
1098         free (rule->src.seq.codes);
1099       free (rule->cmd_ids);
1100     }
1101   else if (cmd->type == FontLayoutCmdTypeCond)
1102     free (cmd->body.cond.cmd_ids);
1103   else if (cmd->type == FontLayoutCmdTypeOTF)
1104     {
1105       if (cmd->body.otf.features[0])
1106         free (cmd->body.otf.features[0]);
1107       if (cmd->body.otf.features[1])
1108         free (cmd->body.otf.features[1]);
1109     }
1110 }
1111
1112 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
1113    and return it.  PLIST has this form:
1114       PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
1115 */
1116
1117 static FontLayoutStage *
1118 load_generator (MPlist *plist)
1119 {
1120   FontLayoutStage *stage;
1121   MPlist *elt, *pl;
1122   FontLayoutCmd dummy;
1123   int result;
1124
1125   MSTRUCT_CALLOC (stage, MERROR_DRAW);
1126   MLIST_INIT1 (stage, cmds, 32);
1127   dummy.type = FontLayoutCmdTypeMAX;
1128   MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
1129   MPLIST_DO (elt, MPLIST_NEXT (plist))
1130     {
1131       if (! MPLIST_PLIST_P (elt))
1132         MERROR (MERROR_FONT, NULL);
1133       pl = MPLIST_PLIST (elt);
1134       if (! MPLIST_SYMBOL_P (pl))
1135         MERROR (MERROR_FONT, NULL);
1136       MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
1137     }
1138
1139   /* Load the first command from PLIST into STAGE->cmds[0].  Macros
1140      called in the first command are also loaded from MPLIST_NEXT
1141      (PLIST) into STAGE->cmds[n].  */
1142   result = load_command (stage, plist, MPLIST_NEXT (plist),
1143                          INDEX_TO_CMD_ID (0));
1144   if (result == INVALID_CMD_ID || result == -2)
1145     {
1146       MLIST_FREE1 (stage, cmds);
1147       free (stage);
1148       return NULL;
1149     }
1150
1151   return stage;
1152 }
1153
1154
1155 /* Load stages of the font layout table FLT.  */
1156
1157 static int
1158 load_flt (MFLT *flt, MPlist *key_list)
1159 {
1160   MPlist *top, *plist, *pl, *p;
1161   MCharTable *category = NULL;
1162   MSymbol sym;
1163
1164   if (key_list)
1165     top = (MPlist *) mdatabase__load_for_keys (flt->mdb, key_list);
1166   else
1167     top = (MPlist *) mdatabase_load (flt->mdb);
1168   if (! top)
1169     return -1;
1170   if (! MPLIST_PLIST_P (top))
1171     {
1172       M17N_OBJECT_UNREF (top);
1173       MERROR (MERROR_FLT, -1);
1174     }
1175
1176   if (key_list)
1177     {
1178       plist = mdatabase__props (flt->mdb);
1179       if (! plist)
1180         MERROR (MERROR_FLT, -1);
1181       MPLIST_DO (plist, plist)
1182         if (MPLIST_PLIST_P (plist))
1183           {
1184             pl = MPLIST_PLIST (plist);
1185             if (! MPLIST_SYMBOL_P (pl)
1186                 || MPLIST_SYMBOL (pl) != Mfont)
1187               continue;
1188             pl = MPLIST_NEXT (pl);
1189             if (! MPLIST_PLIST_P (pl))
1190               continue;
1191             p = MPLIST_PLIST (pl);
1192             if (! MPLIST_SYMBOL_P (p))
1193               continue;
1194             p = MPLIST_NEXT (p);
1195             if (! MPLIST_SYMBOL_P (p))
1196               continue;
1197             flt->family = MPLIST_SYMBOL (p);
1198             MPLIST_DO (p, MPLIST_NEXT (p))
1199               if (MPLIST_SYMBOL_P (p))
1200                 {
1201                   sym = MPLIST_SYMBOL (p);
1202                   if (MSYMBOL_NAME (sym)[0] != ':')
1203                     flt->registry = sym, sym = Mnil;
1204                   else
1205                     break;
1206                 }
1207             if (sym)
1208               {
1209                 char *otf_spec = MSYMBOL_NAME (sym);
1210
1211                 if (otf_spec[0] == ':' && otf_spec[1] == 'o'
1212                     && otf_spec[2] == 't' && otf_spec[3] == 'f')
1213                   parse_otf_command (sym, &flt->otf);
1214               }
1215             break;
1216           }
1217     }
1218   MPLIST_DO (plist, top)
1219     {
1220       if (MPLIST_SYMBOL_P (plist)
1221           && MPLIST_SYMBOL (plist) == Mend)
1222         {
1223           mplist_set (plist, Mnil, NULL);
1224           break;
1225         }
1226       if (! MPLIST_PLIST (plist))
1227         continue;
1228       pl = MPLIST_PLIST (plist);
1229       if (! MPLIST_SYMBOL_P (pl))
1230         continue;
1231       sym = MPLIST_SYMBOL (pl);
1232       pl = MPLIST_NEXT (pl);
1233       if (! pl)
1234         continue;
1235       if (sym == Mcategory)
1236         {
1237           if (category)
1238             M17N_OBJECT_UNREF (category);
1239           else if (flt->coverage)
1240             {
1241               category = flt->coverage;
1242               continue;
1243             }
1244           category = load_category_table (pl);
1245           if (! flt->coverage)
1246             {
1247               flt->coverage = category;
1248               M17N_OBJECT_REF (category);
1249             }
1250         }
1251       else if (sym == Mgenerator)
1252         {
1253           FontLayoutStage *stage;
1254
1255           if (! category)
1256             break;
1257           stage = load_generator (pl);
1258           if (! stage)
1259             break;
1260           stage->category = category;
1261           M17N_OBJECT_REF (category);
1262           if (! flt->stages)
1263             flt->stages = mplist ();
1264           mplist_add (flt->stages, Mt, stage);
1265         }
1266     }
1267   if (category)
1268     M17N_OBJECT_UNREF (category);
1269   if (! MPLIST_TAIL_P (plist))
1270     {
1271       M17N_OBJECT_UNREF (top);
1272       M17N_OBJECT_UNREF (flt->stages);
1273       MERROR (MERROR_FLT, -1);
1274     }
1275   M17N_OBJECT_UNREF (top);
1276   return 0;
1277 }
1278
1279
1280 static void
1281 free_flt_stage (FontLayoutStage *stage)
1282 {
1283   int i;
1284
1285   M17N_OBJECT_UNREF (stage->category);
1286   for (i = 0; i < stage->used; i++)
1287     free_flt_command (stage->cmds + i);
1288   MLIST_FREE1 (stage, cmds);
1289   free (stage);
1290 }
1291
1292 static void
1293 free_flt_list ()
1294 {
1295   if (flt_list)
1296     {
1297       MPlist *plist, *pl;
1298
1299       MPLIST_DO (plist, flt_list)
1300         {
1301           MFLT *flt = MPLIST_VAL (plist);
1302
1303           if (flt->coverage)
1304             M17N_OBJECT_UNREF (flt->coverage);
1305           if (flt->stages)
1306             {
1307               MPLIST_DO (pl, MPLIST_NEXT (flt->stages))
1308                 free_flt_stage (MPLIST_VAL (pl));
1309               M17N_OBJECT_UNREF (flt->stages);
1310             }
1311         }
1312       M17N_OBJECT_UNREF (flt_list);
1313     }
1314 }
1315
1316 static int
1317 list_flt ()
1318 {
1319   MPlist *plist, *key_list = NULL;
1320   MPlist *pl;
1321   int result = 0;
1322
1323   if (! (plist = mdatabase_list (Mfont, Mlayouter, Mnil, Mnil)))
1324     return -1;
1325   if (! (flt_list = mplist ()))
1326     goto err;
1327   if (! (key_list = mplist ()))
1328     goto err;
1329   if (! mplist_add (key_list, Mcategory, Mt))
1330     goto err;
1331
1332   MPLIST_DO (pl, plist)
1333     {
1334       MDatabase *mdb = MPLIST_VAL (pl);
1335       MSymbol *tags = mdatabase_tag (mdb);
1336       MFLT *flt;
1337
1338       if (! MSTRUCT_CALLOC_SAFE (flt))
1339         goto err;
1340       flt->name = tags[2];
1341       flt->mdb = mdb;
1342       if (load_flt (flt, key_list) < 0)
1343         free (flt);
1344       else
1345         {
1346           if (MPLIST_TAIL_P (flt_list))
1347             {
1348               flt_min_coverage = mchartable_min_char (flt->coverage);
1349               flt_max_coverage = mchartable_max_char (flt->coverage);
1350             }
1351           else
1352             {
1353               int c;
1354
1355               c = mchartable_min_char (flt->coverage);
1356               if (flt_min_coverage > c)
1357                 flt_min_coverage = c;
1358               c = mchartable_max_char (flt->coverage);
1359               if (flt_max_coverage < c)
1360                 flt_max_coverage = c;
1361             }
1362           if (! mplist_push (flt_list, flt->name, flt))
1363             goto err;
1364         }
1365     }
1366   goto end;
1367
1368  err:
1369   free_flt_list ();
1370   result = -1;
1371  end:
1372   M17N_OBJECT_UNREF (plist);  
1373   M17N_OBJECT_UNREF (key_list);
1374   return result;
1375 }
1376
1377 /* FLS (Font Layout Service) */
1378
1379 /* Structure to hold information about a context of FLS.  */
1380
1381 typedef struct
1382 {
1383   /* Pointer to the current stage.  */
1384   FontLayoutStage *stage;
1385
1386   /* Pointer to the font.  */
1387   MFLTFont *font;
1388
1389   /* Input and output glyph string.  */
1390   MFLTGlyphString *in, *out;
1391
1392   /* Encode each character or code of a glyph by the current category
1393      table into this array.  An element is a category letter used for
1394      a regular expression matching.  */
1395   char *encoded;
1396   int encoded_offset;
1397   int *match_indices;
1398   int code_offset;
1399   int cluster_begin_idx;
1400   int cluster_begin_pos;
1401   int cluster_end_pos;
1402   int combining_code;
1403   int left_padding;
1404   int check_mask;
1405 } FontLayoutContext;
1406
1407 static int run_command (int, int, int, int, FontLayoutContext *);
1408
1409 #define NMATCH 20
1410
1411 static int
1412 run_rule (int depth,
1413           FontLayoutCmdRule *rule, int from, int to, FontLayoutContext *ctx)
1414 {
1415   int *saved_match_indices = ctx->match_indices;
1416   int match_indices[NMATCH * 2];
1417   int consumed;
1418   int i;
1419   int orig_from = from;
1420
1421   if (rule->src_type == SRC_SEQ)
1422     {
1423       int len;
1424
1425       len = rule->src.seq.n_codes;
1426       if (len > (to - from))
1427         return 0;
1428       for (i = 0; i < len; i++)
1429         if (rule->src.seq.codes[i] != GREF (ctx->in, from + i)->code)
1430           break;
1431       if (i < len)
1432         return 0;
1433       to = from + len;
1434       if (MDEBUG_FLAG () > 2)
1435         MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "",
1436                        rule->src.seq.codes[0]);
1437     }
1438   else if (rule->src_type == SRC_RANGE)
1439     {
1440       int head;
1441
1442       if (from >= to)
1443         return 0;
1444       head = GREF (ctx->in, from)->code;
1445       if (head < rule->src.range.from || head > rule->src.range.to)
1446         return 0;
1447       ctx->code_offset = head - rule->src.range.from;
1448       to = from + 1;
1449       if (MDEBUG_FLAG () > 2)
1450         MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "",
1451                        rule->src.range.from, rule->src.range.to);
1452     }
1453   else if (rule->src_type == SRC_REGEX)
1454     {
1455       regmatch_t pmatch[NMATCH];
1456       char saved_code;
1457       int result;
1458
1459       if (from > to)
1460         return 0;
1461       saved_code = ctx->encoded[to - ctx->encoded_offset];
1462       ctx->encoded[to - ctx->encoded_offset] = '\0';
1463       result = regexec (&(rule->src.re.preg),
1464                         ctx->encoded + (from - ctx->encoded_offset),
1465                         NMATCH, pmatch, 0);
1466       if (result == 0 && pmatch[0].rm_so == 0)
1467         {
1468           if (MDEBUG_FLAG () > 2)
1469             MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "",
1470                            rule->src.re.pattern,
1471                            ctx->encoded + (from - ctx->encoded_offset),
1472                            pmatch[0].rm_eo);
1473           ctx->encoded[to - ctx->encoded_offset] = saved_code;
1474           for (i = 0; i < NMATCH; i++)
1475             {
1476               if (pmatch[i].rm_so < 0)
1477                 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1478               else
1479                 {
1480                   match_indices[i * 2] = from + pmatch[i].rm_so;
1481                   match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1482                 }
1483             }
1484           ctx->match_indices = match_indices;
1485           to = match_indices[1];
1486         }
1487       else
1488         {
1489           ctx->encoded[to - ctx->encoded_offset] = saved_code;
1490           return 0;
1491         }
1492     }
1493   else if (rule->src_type == SRC_INDEX)
1494     {
1495       if (rule->src.match_idx >= NMATCH)
1496         return 0;
1497       from = ctx->match_indices[rule->src.match_idx * 2];
1498       if (from < 0)
1499         return 0;
1500       to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1501       if (MDEBUG_FLAG () > 2)
1502         MDEBUG_PRINT3 ("\n [FLT] %*s(SUBPART %d", depth, "",
1503                        rule->src.match_idx);
1504     }
1505   else if (rule->src_type == SRC_HAS_GLYPH)
1506     {
1507       int encoded;
1508       unsigned code;
1509
1510       if (rule->src.supported_glyph < 0)
1511         {
1512           if (from >= to)
1513             return 0;
1514           code = GREF (ctx->in, from)->code;
1515           encoded = GREF (ctx->in, from)->encoded;
1516         }
1517       else
1518         {
1519           code = rule->src.supported_glyph;
1520           encoded = 0;
1521         }
1522       if (! encoded)
1523         {
1524           static MFLTGlyphString gstring;
1525
1526           if (! gstring.glyph_size)
1527             {
1528               gstring.glyph_size = ctx->in->glyph_size;
1529               gstring.glyphs = calloc (1, gstring.glyph_size);
1530               gstring.allocated = 1;
1531               gstring.used = 1;
1532             }
1533           gstring.glyphs[0].code = code;
1534           ctx->font->get_glyph_id (ctx->font, &gstring, 0, 1);
1535           if (gstring.glyphs[0].code == 0xFFFFFFFF
1536               || ! gstring.glyphs[0].encoded)
1537             return 0;
1538         }
1539       if (MDEBUG_FLAG () > 2)
1540         MDEBUG_PRINT3 ("\n [FLT] %*s(HAS-GLYPH %04X", depth, "", code);
1541     }
1542   else if (rule->src_type == SRC_OTF_SPEC)
1543     {
1544       MFLTOtfSpec *spec = &rule->src.otf_spec;
1545
1546       if (! ctx->font->check_otf)
1547         {
1548           if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
1549               || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
1550             return 0;
1551         }
1552       else if (! ctx->font->check_otf (ctx->font, spec))
1553         return 0;
1554     }
1555
1556   consumed = 0;
1557   depth++;
1558   for (i = 0; i < rule->n_cmds; i++)
1559     {
1560       int pos;
1561
1562       if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1563         {
1564           if (! consumed)
1565             continue;
1566           i--;
1567         }
1568       pos = run_command (depth, rule->cmd_ids[i], from, to, ctx);
1569       if (pos < 0)
1570         return pos;
1571       consumed = pos > from;
1572       if (consumed)
1573         from = pos;
1574     }
1575
1576   ctx->match_indices = saved_match_indices;
1577   if (MDEBUG_FLAG () > 2)
1578     MDEBUG_PRINT (")");
1579   return (rule->src_type == SRC_INDEX ? orig_from : to);
1580 }
1581
1582 static int
1583 run_cond (int depth,
1584           FontLayoutCmdCond *cond, int from, int to, FontLayoutContext *ctx)
1585 {
1586   int i, pos = 0;
1587
1588   if (MDEBUG_FLAG () > 2)
1589     MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1590   depth++;
1591   for (i = 0; i < cond->n_cmds; i++)
1592     {
1593       /* TODO: Write a code for optimization utilizaing the info
1594          cond->seq_XXX.  */
1595       if ((pos = run_command (depth, cond->cmd_ids[i], from, to, ctx))
1596           != 0)
1597         break;
1598     }
1599   if (pos < 0)
1600     return pos;
1601   if (MDEBUG_FLAG () > 2)
1602     MDEBUG_PRINT (")");
1603   return (pos);
1604 }
1605
1606 static int
1607 run_otf (int depth,
1608          MFLTOtfSpec *otf_spec, int from, int to, FontLayoutContext *ctx)
1609 {
1610   MFLTFont *font = ctx->font;
1611   int from_idx = ctx->out->used;
1612
1613   if (MDEBUG_FLAG () > 2)
1614     MDEBUG_PRINT3 ("\n [FLT] %*s%s", depth, "", MSYMBOL_NAME (otf_spec->sym));
1615
1616   font->get_glyph_id (font, ctx->in, from, to);
1617   if (! font->drive_otf)
1618     {
1619       if (ctx->out->used + (to - from) > ctx->out->allocated)
1620         return -2;
1621       font->get_metrics (font, ctx->in, from, to);
1622       GCPY (ctx->in, from, to - from, ctx->out, ctx->out->used);
1623       ctx->out->used += to - from;
1624     }
1625   else
1626     {
1627       MFLTGlyphAdjustment *adjustment;
1628       int out_len;
1629       int i;
1630
1631       adjustment = alloca ((sizeof *adjustment)
1632                            * (ctx->out->allocated - ctx->out->used));
1633       if (! adjustment)
1634         MERROR (MERROR_FLT, -1);
1635       memset (adjustment, 0,
1636               (sizeof *adjustment) * (ctx->out->allocated - ctx->out->used));
1637       to = font->drive_otf (font, otf_spec, ctx->in, from, to, ctx->out,
1638                             adjustment);
1639       if (to < 0)
1640         return to;
1641       out_len = ctx->out->used - from_idx;
1642       if (otf_spec->features[1])
1643         {
1644           MFLTGlyphAdjustment *a;
1645           MFLTGlyph *g;
1646           
1647           for (i = 0, a = adjustment; i < out_len; i++, a++)
1648             if (a->set)
1649               break;
1650           if (i < out_len)
1651             {
1652               font->get_metrics (font, ctx->out, from_idx, ctx->out->used);
1653               for (g = GREF (ctx->out, from_idx + i);
1654                    i < out_len; i++, a++, g = NEXT (ctx->out, g))
1655                 if (a->set)
1656                   {
1657                     if (a->advance_is_absolute)
1658                       {
1659                         g->xadv = a->xadv;
1660                         g->yadv = a->yadv;
1661                       }
1662                     else if (a->xadv || a->yadv)
1663                       {
1664                         g->xadv += a->xadv;
1665                         g->yadv += a->yadv;
1666                       }
1667                     if (a->xoff || a->yoff)
1668                       {
1669                         int j;
1670                         MFLTGlyph *gg = PREV (ctx->out, g);
1671                         MFLTGlyphAdjustment *aa = a;
1672
1673                         g->xoff = a->xoff;
1674                         g->yoff = a->yoff;
1675                         while (aa->back > 0)
1676                           {
1677                             for (j = 0; j < aa->back;
1678                                  j++, gg = PREV (ctx->out, gg))
1679                               g->xoff -= gg->xadv;
1680                             aa = aa - aa->back;
1681                             g->xoff += aa->xoff;
1682                             g->yoff += aa->yoff;
1683                           }
1684                       }
1685                     g->adjusted = 1;
1686                   }
1687             }
1688         }
1689     }
1690
1691   if (ctx->cluster_begin_idx >= 0)
1692     for (; from_idx < ctx->out->used; from_idx++)
1693       {
1694         MFLTGlyph *g = GREF (ctx->out, from_idx);
1695         UPDATE_CLUSTER_RANGE (ctx, g);
1696       }
1697   return to;
1698 }
1699
1700 static char work[16];
1701
1702 static char *
1703 dump_combining_code (int code)
1704 {
1705   char *vallign = "tcbB";
1706   char *hallign = "lcr";
1707   char *p;
1708   int off_x, off_y;
1709
1710   if (! code)
1711     return "none";
1712   work[0] = vallign[COMBINING_CODE_BASE_Y (code)];
1713   work[1] = hallign[COMBINING_CODE_BASE_X (code)];
1714   off_y = COMBINING_CODE_OFF_Y (code);
1715   off_x = COMBINING_CODE_OFF_X (code);
1716   if (off_y > 0)
1717     sprintf (work + 2, "+%d", off_y);
1718   else if (off_y < 0)
1719     sprintf (work + 2, "%d", off_y);
1720   else if (off_x == 0)
1721     sprintf (work + 2, ".");
1722   p = work + strlen (work);
1723   if (off_x > 0)
1724     sprintf (p, ">%d", off_x);
1725   else if (off_x < 0)
1726     sprintf (p, "<%d", -off_x);
1727   p += strlen (p);
1728   p[0] = vallign[COMBINING_CODE_ADD_Y (code)];
1729   p[1] = hallign[COMBINING_CODE_ADD_X (code)];
1730   p[2] = '\0';
1731   return work;
1732 }
1733
1734 static int
1735 run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
1736 {
1737   MFLTGlyph *g;
1738
1739   if (id >= 0)
1740     {
1741       int i;
1742
1743       /* Direct code (== ctx->code_offset + id) output.
1744          The source is not consumed.  */
1745       if (MDEBUG_FLAG () > 2)
1746         MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "",
1747                        ctx->code_offset + id);
1748       i = (from < to || from == 0) ? from : from - 1;
1749       GDUP (ctx, i);
1750       g = GREF (ctx->out, ctx->out->used - 1);
1751       g->c = g->code = ctx->code_offset + id;
1752       SET_ENCODED (g, 0);
1753       SET_MEASURED (g, 0);
1754       if (ctx->combining_code)
1755         SET_COMBINING_CODE (g, ctx, ctx->combining_code);
1756       if (ctx->left_padding)
1757         SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
1758       for (i = from; i < to; i++)
1759         {
1760           MFLTGlyph *tmp = GREF (ctx->in, i);
1761
1762           if (g->from > tmp->from)
1763             g->from = tmp->from;
1764           else if (g->to < tmp->to)
1765             g->to = tmp->to;
1766         }
1767       UPDATE_CLUSTER_RANGE (ctx, g);
1768       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1769       if (MDEBUG_FLAG () > 2)
1770         MDEBUG_PRINT (")");
1771       return (from);
1772     }
1773
1774   if (id <= CMD_ID_OFFSET_INDEX)
1775     {
1776       int idx = CMD_ID_TO_INDEX (id);
1777       FontLayoutCmd *cmd;
1778
1779       if (idx >= ctx->stage->used)
1780         MERROR (MERROR_DRAW, -1);
1781       cmd = ctx->stage->cmds + idx;
1782       if (cmd->type == FontLayoutCmdTypeRule)
1783         to = run_rule (depth, &cmd->body.rule, from, to, ctx);
1784       else if (cmd->type == FontLayoutCmdTypeCond)
1785         to = run_cond (depth, &cmd->body.cond, from, to, ctx);
1786       else if (cmd->type == FontLayoutCmdTypeOTF)
1787         to = run_otf (depth, &cmd->body.otf, from, to, ctx);
1788       return to;
1789     }
1790
1791   if (id <= CMD_ID_OFFSET_COMBINING)
1792     {
1793       ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1794       if (MDEBUG_FLAG () > 2)
1795         MDEBUG_PRINT3 ("\n [FLT] %*s(CMB %s)", depth, "",
1796                        dump_combining_code (ctx->combining_code));
1797       return from;
1798     }
1799
1800   switch (id)
1801     {
1802     case CMD_ID_COPY:
1803       {
1804         if (from >= to)
1805           return from;
1806         GDUP (ctx, from);
1807         g = GREF (ctx->out, ctx->out->used - 1);
1808         if (ctx->combining_code)
1809           SET_COMBINING_CODE (g, ctx, ctx->combining_code);
1810         if (ctx->left_padding)
1811           SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
1812         UPDATE_CLUSTER_RANGE (ctx, g);
1813         if (MDEBUG_FLAG () > 2)
1814           {
1815             if (g->c < 0)
1816               MDEBUG_PRINT2 ("\n [FLT] %*s(COPY |)", depth, "");
1817             else
1818               MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g->code);
1819           }
1820         ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1821         return (from + 1);
1822       }
1823
1824     case CMD_ID_CLUSTER_BEGIN:
1825       if (ctx->cluster_begin_idx < 0)
1826         {
1827           if (MDEBUG_FLAG () > 2)
1828             MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "",
1829                            GREF (ctx->in, from)->from);
1830           ctx->cluster_begin_idx = ctx->out->used;
1831           ctx->cluster_begin_pos = GREF (ctx->in, from)->from;
1832           ctx->cluster_end_pos = GREF (ctx->in, from)->to;
1833         }
1834       return from;
1835
1836     case CMD_ID_CLUSTER_END:
1837       if (ctx->cluster_begin_idx >= 0
1838           && ctx->cluster_begin_idx < ctx->out->used)
1839         {
1840           int i;
1841
1842           if (MDEBUG_FLAG () > 2)
1843             MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos + 1);
1844           for (i = ctx->cluster_begin_idx; i < ctx->out->used; i++)
1845             {
1846               GREF (ctx->out, i)->from = ctx->cluster_begin_pos;
1847               GREF (ctx->out, i)->to = ctx->cluster_end_pos;
1848             }
1849           ctx->cluster_begin_idx = -1;
1850         }
1851       return from;
1852
1853     case CMD_ID_SEPARATOR:
1854       {
1855         int i;
1856
1857         i = from < to ? from : from - 1;
1858         GDUP (ctx, i);
1859         g = GREF (ctx->out, ctx->out->used - 1);
1860         g->c = -1, g->code = 0;
1861         g->xadv = g->yadv = 0;
1862         SET_ENCODED (g, 0);
1863         SET_MEASURED (g, 0);
1864         return from;
1865       }
1866
1867     case CMD_ID_LEFT_PADDING:
1868       if (MDEBUG_FLAG () > 2)
1869         MDEBUG_PRINT2 ("\n [FLT] %*s[", depth, "");
1870       ctx->left_padding = 1;
1871       return from;
1872
1873     case CMD_ID_RIGHT_PADDING:
1874       if (ctx->out->used > 0)
1875         {
1876           if (MDEBUG_FLAG () > 2)
1877             MDEBUG_PRINT2 ("\n [FLT] %*s]", depth, "");
1878           g = GREF (ctx->out, ctx->out->used - 1);
1879           SET_RIGHT_PADDING (g, ctx, RightPaddingMask);
1880         }
1881       return from;
1882     }
1883
1884   MERROR (MERROR_DRAW, -1);
1885 }
1886
1887 static int
1888 run_stages (MFLTGlyphString *gstring, int from, int to,
1889             MFLT *flt, FontLayoutContext *ctx)
1890 {
1891   MFLTGlyphString buf, *temp;
1892   int stage_idx = 0;
1893   int orig_from = from, orig_to = to;
1894   int from_pos, to_pos, len;
1895   int i, j;
1896   MFLTGlyph *g;
1897   MPlist *stages = flt->stages;
1898
1899   from_pos = GREF (ctx->in, from)->from;
1900   to_pos = GREF (ctx->in, to - 1)->to;
1901   len = to_pos - from_pos;
1902
1903   buf = *(ctx->in);
1904   buf.glyphs = NULL;
1905   GINIT (ctx->out, ctx->out->allocated);
1906   ctx->encoded = alloca (ctx->out->allocated);
1907   if (! ctx->out->glyphs || ! ctx->encoded)
1908     return -1;
1909
1910   for (stage_idx = 0; 1; stage_idx++)
1911     {
1912       MCharTable *table;
1913       int result;
1914
1915       ctx->stage = (FontLayoutStage *) MPLIST_VAL (stages);
1916       table = ctx->stage->category;
1917       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1918       ctx->encoded_offset = from;
1919       for (i = from; i < to; i++)
1920         {
1921           MFLTGlyph *g = GREF (ctx->in, i);
1922           char enc = (GET_ENCODED (g)
1923                       ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
1924                       : g->code
1925                       ? (int) mchartable_lookup (table, g->code)
1926                       : ' ');
1927
1928           ctx->encoded[i - from] = enc;
1929           if (! enc && stage_idx == 0)
1930             {
1931               to = i;
1932               break;
1933             }
1934         }
1935       ctx->encoded[i - from] = '\0';
1936       ctx->match_indices[0] = from;
1937       ctx->match_indices[1] = to;
1938       for (i = 2; i < NMATCH; i++)
1939         ctx->match_indices[i] = -1;
1940
1941       if (MDEBUG_FLAG () > 2)
1942         {
1943           MDEBUG_PRINT2 ("\n [FLT]   (STAGE %d \"%s\"", stage_idx,
1944                          ctx->encoded);
1945           MDEBUG_PRINT (" (");
1946           for (i = from; i < to; i++)
1947             {
1948               g = GREF (ctx->in, i);
1949               if (g->c == -1)
1950                 MDEBUG_PRINT2 ("%*s|", (i > 0), "");
1951               else
1952                 MDEBUG_PRINT3 ("%*s%04X", (i > 0), "", GREF (ctx->in, i)->code);
1953             }
1954           MDEBUG_PRINT (")");
1955         }
1956       result = run_command (4, INDEX_TO_CMD_ID (0), from, to, ctx);
1957       if (MDEBUG_FLAG () > 2)
1958         MDEBUG_PRINT (")");
1959       if (result < 0)
1960         return result;
1961
1962       stages = MPLIST_NEXT (stages);
1963       /* If this is the last stage, break the loop. */
1964       if (MPLIST_TAIL_P (stages))
1965         break;
1966
1967       /* Otherwise, prepare for the next stage.   */
1968       temp = ctx->in;
1969       ctx->in = ctx->out;
1970       if (buf.glyphs)
1971         ctx->out = temp;
1972       else
1973         {
1974           GINIT (&buf, ctx->out->allocated);
1975           ctx->out = &buf;
1976         }
1977       ctx->out->used = 0;
1978
1979       from = 0;
1980       to = ctx->in->used;
1981     }
1982
1983   if (ctx->out->used > 0)
1984     {
1985       int *g_indices;
1986       int x_ppem = ctx->font->x_ppem << 6, y_ppem = ctx->font->y_ppem << 6;
1987
1988       /* Remove separator glyphs.  */
1989       for (i = 0; i < ctx->out->used;)
1990         {
1991           g = GREF (ctx->out, i);
1992           if (g->c < 0)
1993             GREPLACE (NULL, 0, 0, ctx->out, i, i + 1);
1994           else
1995             i++;
1996         }
1997
1998       /* Get actual glyph IDs of glyphs.  */
1999       ctx->font->get_glyph_id (ctx->font, ctx->out, 0, ctx->out->used);
2000
2001       /* Check if all characters in the range are covered by some
2002          glyph(s).  If not, change <from> and <to> of glyphs to cover
2003          uncovered characters.  */
2004       g_indices = alloca (sizeof (int) * len);
2005       if (! g_indices)
2006         return -1;
2007       for (i = 0; i < len; i++) g_indices[i] = -1;
2008       for (i = 0; i < ctx->out->used; i++)
2009         {
2010           int pos;
2011
2012           g = GREF (ctx->out, i);
2013           for (pos = g->from; pos <= g->to; pos++)
2014             if (g_indices[pos - orig_from] < 0)
2015               g_indices[pos - orig_from] = i;
2016         }
2017       for (i = 0; i < len; i++)
2018         if (g_indices[i] < 0)
2019           {
2020             if (i == 0)
2021               {
2022                 int this_from;
2023
2024                 for (i++; i < len && g_indices[i] < 0; i++);
2025                 j = g_indices[i];
2026                 g = GREF (ctx->out, j);
2027                 this_from = g->from;
2028                 do {
2029                   g->from = orig_from + i;
2030                 } while (++j < ctx->out->used
2031                          && (g = GREF (ctx->out, j))
2032                          && g->from == this_from);
2033               }
2034             else
2035               {
2036                 int this_to;
2037
2038                 j = g_indices[i - 1];
2039                 g = GREF (ctx->out, j);
2040                 this_to = g->to;
2041                 do {
2042                   g->to = orig_from + i + 1;
2043                 } while (--j >= 0
2044                          && (g = GREF (ctx->out, j))
2045                          && g->to == this_to);
2046               }
2047           }
2048
2049       ctx->font->get_metrics (ctx->font, ctx->out, 0, ctx->out->used);
2050
2051       /* Handle combining.  */
2052       if (ctx->check_mask & CombiningCodeMask)
2053         {
2054           MFLTGlyph *base = GREF (ctx->out, 0);
2055           int base_height = base->ascent + base->descent;
2056           int combining_code;
2057
2058           for (i = 1; i < ctx->out->used; i++)
2059             {
2060               if ((g = GREF (ctx->out, i))
2061                   && (combining_code = GET_COMBINING_CODE (g)))
2062                 {
2063                   int height = g->ascent + g->descent;
2064                   int base_x, base_y, add_x, add_y, off_x, off_y;
2065
2066                   if (base->from > g->from)
2067                     base->from = g->from;
2068                   else if (base->to < g->to)
2069                     base->to = g->to;
2070                 
2071                   base_x = COMBINING_CODE_BASE_X (combining_code);
2072                   base_y = COMBINING_CODE_BASE_Y (combining_code);
2073                   add_x = COMBINING_CODE_ADD_X (combining_code);
2074                   add_y = COMBINING_CODE_ADD_Y (combining_code);
2075                   off_x = COMBINING_CODE_OFF_X (combining_code);
2076                   off_y = COMBINING_CODE_OFF_Y (combining_code);
2077
2078                   g->xoff = ((base->xadv * base_x - g->xadv * add_x) / 2
2079                              + x_ppem * off_x / 100 - base->xadv);
2080                   if (base_y < 3)
2081                     g->yoff = base_height * base_y / 2 - base->ascent;
2082                   else
2083                     g->yoff = 0;
2084                   if (add_y < 3)
2085                     g->yoff -= height * add_y / 2 - g->ascent;
2086                   g->yoff -= y_ppem * off_y / 100;
2087                   if (base->lbearing > base->xadv + g->lbearing + g->xoff)
2088                     base->lbearing = base->xadv + g->lbearing + g->xoff;
2089                   if (base->rbearing < base->xadv + g->xadv + g->xoff)
2090                     base->rbearing = base->xadv + g->xadv + g->xoff;
2091                   if (base->ascent < g->ascent - g->yoff)
2092                     base->ascent = g->ascent - g->yoff;
2093                   if (base->descent < g->descent - g->yoff)
2094                     base->descent = g->descent - g->yoff;
2095                   g->xadv = g->yadv = 0;
2096                   if (GET_RIGHT_PADDING (g))
2097                     SET_RIGHT_PADDING (base, ctx, RightPaddingMask);
2098                   g->adjusted = 1;
2099                 }
2100               else
2101                 {
2102                   base = g;
2103                   base_height = g->ascent + g->descent;
2104                 }
2105             }
2106         }
2107
2108       /* Handle padding */
2109       if (ctx->check_mask & (LeftPaddingMask | RightPaddingMask))
2110         for (i = 0; i < ctx->out->used; i++)
2111           {
2112             g = GREF (ctx->out, i);
2113             if (! GET_COMBINING_CODE (g))
2114               {
2115                 if (GET_RIGHT_PADDING (g) && g->rbearing > g->xadv)
2116                   {
2117                     g->xadv = g->rbearing;
2118                     g->adjusted = 1;
2119                   }
2120                 if (GET_LEFT_PADDING (g) && g->lbearing < 0)
2121                   {
2122                     g->xoff += - g->lbearing;
2123                     g->xadv += - g->lbearing;
2124                     g->rbearing += - g->lbearing;
2125                     g->lbearing = 0;
2126                     g->adjusted = 1;
2127                   }
2128               }
2129           }
2130     }
2131
2132   GREPLACE (ctx->out, 0, ctx->out->used, gstring, orig_from, orig_to);
2133   to = orig_from + ctx->out->used;
2134   return to;
2135 }
2136
2137 static void
2138 setup_combining_coverage (int from, int to, void *val, void *arg)
2139 {
2140   int combining_class = (int) val;
2141   int category = 0;
2142
2143   if (combining_class < 200)
2144     category = 'a';
2145   else if (combining_class <= 204)
2146     {
2147       if ((combining_class % 2) == 0)
2148         category = "bcd"[(combining_class - 200) / 2];
2149     }
2150   else if (combining_class <= 232)
2151     {
2152       if ((combining_class % 2) == 0)
2153         category = "efghijklmnopq"[(combining_class - 208) / 2];
2154     }
2155   else if (combining_class == 233)
2156     category = 'r';
2157   else if (combining_class == 234)
2158     category = 's';
2159   else if (combining_class == 240)
2160     category = 't';
2161   mchartable_set_range ((MCharTable *) arg, from, to, (void *) category);
2162 }
2163
2164 static void
2165 setup_combining_flt (MFLT *flt)
2166 {
2167   MSymbol type;
2168   MCharTable *combininig_class_table
2169     = mchar_get_prop_table (Mcombining_class, &type);
2170
2171   mchartable_set_range (flt->coverage, 0, 0x10FFFF, (void *) 'u');
2172   if (combininig_class_table)
2173     mchartable_map (combininig_class_table, (void *) 0,
2174                     setup_combining_coverage, flt->coverage);
2175 }
2176
2177 #define CHECK_FLT_STAGES(flt) ((flt)->stages || load_flt (flt, NULL) == 0)
2178
2179 \f
2180 /* Internal API */
2181
2182 int m17n__flt_initialized;
2183
2184 \f
2185 /* External API */
2186
2187 /* The following two are actually not exposed to a user but concealed
2188    by the macro M17N_INIT (). */
2189
2190 void
2191 m17n_init_flt (void)
2192 {
2193   int mdebug_flag = MDEBUG_INIT;
2194
2195   merror_code = MERROR_NONE;
2196   if (m17n__flt_initialized++)
2197     return;
2198   m17n_init_core ();
2199   if (merror_code != MERROR_NONE)
2200     {
2201       m17n__flt_initialized--;
2202       return;
2203     }
2204
2205   MDEBUG_PUSH_TIME ();
2206
2207   Mcond = msymbol ("cond");
2208   Mrange = msymbol ("range");
2209   Mfont = msymbol ("font");
2210   Mlayouter = msymbol ("layouter");
2211   Mcombining = msymbol ("combining");
2212   Mfont_facility = msymbol ("font-facility");
2213   Mgenerator = msymbol ("generator");
2214   Mend = msymbol ("end");
2215
2216   MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the flt modules."));
2217   MDEBUG_POP_TIME ();
2218 }
2219
2220 void
2221 m17n_fini_flt (void)
2222 {
2223   int mdebug_flag = MDEBUG_FINI;
2224
2225   if (m17n__flt_initialized == 0
2226       || --m17n__flt_initialized > 0)
2227     return;
2228
2229   MDEBUG_PUSH_TIME ();
2230   free_flt_list ();
2231   MDEBUG_PRINT_TIME ("FINI", (stderr, " to finalize the flt modules."));
2232   MDEBUG_POP_TIME ();
2233   m17n_fini_core ();
2234 }
2235
2236 /*** @} */ 
2237 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
2238
2239 /*** @addtogroup m17nFLT */
2240 /*** @{ */
2241 /*=*/
2242
2243 /*=*/
2244 /***en
2245     @brief Return an FLT object that has a specified name.
2246
2247     The mflt_get () function returns an FLT object whose name is $NAME.
2248
2249     @return
2250     If the operation was successful, mflt_get () returns a pointer
2251     to the found FLT object.  Otherwise, it returns @c NULL.  */
2252
2253 /***ja
2254     @brief »ØÄꤵ¤ì¤¿Ì¾Á°¤ò»ý¤Ä FLT ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤¹.
2255
2256     ´Ø¿ô mflt_get () ¤Ï¡¢$NAME ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä FLT ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤¹¡£
2257
2258     @return
2259     ¤â¤·À®¸ù¤¹¤ì¤Ð¡¢mflt_get () ¤Ï¸«¤Ä¤«¤Ã¤¿ FLT
2260     ¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£  */
2261
2262 MFLT *
2263 mflt_get (MSymbol name)
2264 {
2265   MFLT *flt;
2266
2267   if (! flt_list && list_flt () < 0)
2268     return NULL;
2269   flt = mplist_get (flt_list, name);
2270   if (! flt || ! CHECK_FLT_STAGES (flt))
2271     return NULL;
2272   if (flt->name == Mcombining
2273       && ! mchartable_lookup (flt->coverage, 0))
2274     setup_combining_flt (flt);
2275
2276   return flt;
2277 }
2278
2279 /*=*/
2280 /***en
2281     @brief Find an FLT suitable for the specified character and font.
2282
2283     The mflt_find () function returns the most appropriate FLT for
2284     layouting character $C with font $FONT.
2285
2286     @return
2287     If the operation was successful, mflt_find () returns a pointer
2288     to the found FLT object.  Otherwise, it returns @c NULL.  */
2289
2290 /***ja
2291     @brief »ØÄꤵ¤ì¤¿Ê¸»ú¤È¥Õ¥©¥ó¥È¤Ë¹ç¤Ã¤¿ FLT ¤òõ¤¹.
2292
2293     ´Ø¿ô mflt_find () ¤Ï¡¢Ê¸»ú $C ¤ò¥Õ¥©¥ó¥È $FONT
2294     ¤Ç¥ì¥¤¥¢¥¦¥È¤¹¤ë¤¿¤á¤ËºÇ¤âŬÀڤʠFLT ¤òÊÖ¤¹¡£
2295
2296     @return
2297     ¤â¤·À®¸ù¤¹¤ì¤Ð¡¢mflt_find () ¤Ï¸«¤Ä¤«¤Ã¤¿ FLT
2298     ¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£  */
2299
2300 MFLT *
2301 mflt_find (int c, MFLTFont *font)
2302 {
2303   MPlist *plist;
2304   MFLT *flt;
2305   static MSymbol unicode_bmp = NULL, unicode_full = NULL;
2306
2307   if (! unicode_bmp)
2308     {
2309       unicode_bmp = msymbol ("unicode-bmp");
2310       unicode_full = msymbol ("unicode-full");
2311     }
2312
2313   if (! flt_list && list_flt () < 0)
2314     return NULL;
2315   if (font)
2316     {
2317       MFLT *best = NULL;
2318
2319       MPLIST_DO (plist, flt_list)
2320         {
2321           flt = MPLIST_VAL (plist);
2322           if (flt->registry != unicode_bmp
2323               && flt->registry != unicode_full)
2324             continue;
2325           if (flt->family && flt->family != font->family)
2326             continue;
2327           if (c >= 0
2328               && ! mchartable_lookup (flt->coverage, c))
2329             continue;
2330           if (flt->otf.sym)
2331             {
2332               MFLTOtfSpec *spec = &flt->otf;
2333
2334               if (! font->check_otf)
2335                 {
2336                   if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
2337                       || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
2338                     continue;
2339                 }
2340               else if (! font->check_otf (font, spec))
2341                 continue;
2342               return flt;
2343             }
2344           best = flt;
2345         }
2346       return best;
2347     }
2348   if (c >= 0)
2349     {
2350       MPLIST_DO (plist, flt_list)
2351         {
2352           flt = MPLIST_VAL (plist);
2353           if (mchartable_lookup (flt->coverage, c))
2354             return flt;
2355         }
2356     }
2357   return NULL;
2358 }
2359
2360 /*=*/
2361 /***en
2362     @brief Return the name of an FLT.
2363
2364     The mflt_name () function returns the name of $FLT.  */
2365
2366 /***ja
2367     @brief FLT ¤Î̾Á°¤òÊÖ¤¹.
2368
2369     ´Ø¿ô mflt_name () ¤Ï $FLT ¤Î̾Á°¤òÊÖ¤¹¡£  */
2370
2371 const char *
2372 mflt_name (MFLT *flt)
2373 {
2374   return MSYMBOL_NAME (flt->name);
2375 }
2376
2377 /*=*/
2378 /***en
2379     @brief Return a coverage of a FLT.
2380
2381     The mflt_coverage () function returns a char-table that contains
2382     nonzero values for characters supported by $FLT.  */
2383
2384 /***ja
2385     @brief FLT ¤ÎÈϰϤòÊÖ¤¹.
2386
2387     ´Ø¿ô mflt_coverage () ¤Ï¡¢$FLT ¤¬¥µ¥Ý¡¼¥È¤¹¤ëʸ»ú¤ËÂФ·¤Æ
2388     0 ¤Ç¤Ê¤¤Ãͤò´Þ¤àʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£  */
2389
2390 MCharTable *
2391 mflt_coverage (MFLT *flt)
2392 {
2393   return flt->coverage;
2394 }
2395
2396 /*=*/
2397 /***en
2398     @brief Layout characters with an FLT.
2399
2400     The mflt_run () function layouts characters in $GSTRING between
2401     $FROM (inclusive) and $TO (exclusive) with $FONT.  If $FLT is
2402     nonzero, it is used for all the charaters.  Otherwise, appropriate
2403     FLTs are automatically chosen.
2404
2405     @retval >=0
2406     The operation was successful.  The value is the index to the
2407     glyph, which was previously indexed by $TO, in $GSTRING->glyphs.
2408
2409     @retval -2
2410     $GSTRING->glyphs is too short to store the result.  The caller can
2411     call this fucntion again with a longer $GSTRING->glyphs.
2412
2413     @retval -1
2414     Some other error occurred.  */
2415
2416 /***ja
2417     @brief FLT ¤ò»È¤Ã¤Æʸ»ú¤ò¥ì¥¤¥¢¥¦¥È¤¹¤ë.
2418
2419     ´Ø¿ô mflt_run () ¤Ï¡¢$GSTRING Ãæ¤Î $FROM ¤«¤é $TO Ä¾Á°¤Þ¤Ç¤Îʸ»ú¤ò
2420     $FONT ¤òÍѤ¤¤Æ¥ì¥¤¥¢¥¦¥È¤¹¤ë¡£¤â¤· $FLT
2421     ¤¬¥¼¥í¤Ç¤Ê¤±¤ì¤Ð¡¢¤½¤ÎÃͤò¤¹¤Ù¤Æ¤Îʸ»ú¤ËÂФ·¤ÆÍѤ¤¤ë¡£
2422     ¤½¤¦¤Ç¤Ê¤±¤ì¤ÐŬÀڤʠFLT ¤ò¼«Æ°Åª¤ËÁªÂò¤¹¤ë¡£
2423
2424     @retval >=0
2425     ¼Â¹ÔÀ®¸ù¤ò¼¨¤¹¡£ÊÖ¤µ¤ì¤ëÃͤϡ¢$GSTRING->glyphs Ãæ¤Ç°ÊÁ° $TO
2426     ¤Ë¤è¤Ã¤Æ¼¨¤µ¤ì¤Æ¤¤¤¿¥°¥ê¥Õ¤Ø¤Î¥¤¥ó¥Ç¥¯¥¹¤Ç¤¢¤ë¡£
2427
2428     @retval -2
2429     ·ë²Ì¤ò³ÊǼ¤¹¤ë¤Ë¤Ï $GSTRING->glyphs ¤¬Ã»¤¹¤®¤ë¤³¤È¤ò¼¨¤¹¡£
2430     ¸Æ¤Ó½Ð¤·Â¦¤Ï¡¢¤è¤êŤ¤ $GSTRING->glyphs
2431     ¤òÍѤ¤¤ÆºÆÅÙ¤³¤Î´Ø¿ô¤ò¸Æ¤Ö¤³¤È¤¬¤Ç¤­¤ë¡£
2432
2433     @retval -1
2434     ¤½¤Î¾¤Î¥¨¥é¡¼¤¬µ¯¤­¤¿¤³¤È¤ò¼¨¤¹¡£  */
2435
2436 int
2437 mflt_run (MFLTGlyphString *gstring, int from, int to,
2438           MFLTFont *font, MFLT *flt)
2439 {
2440   FontLayoutContext ctx;
2441   int match_indices[NMATCH];
2442   MFLTGlyph *g;
2443   MFLTGlyphString out;
2444   int auto_flt = ! flt;
2445   int c, i, j, k;
2446   int this_from, this_to;
2447
2448   out = *gstring;
2449   out.glyphs = NULL;
2450   /* This is usually sufficient, but if not, we retry with the larger
2451      values at most 3 times.  This value is also used for the
2452      allocating size of ctx.encoded.  */
2453   out.allocated = (to - from) * 4;
2454
2455   for (i = from; i < to; i++)
2456     {
2457       g = GREF (gstring, i);
2458       c = g->c;
2459       memset (g, 0, sizeof (MFLTGlyph));
2460       g->code = g->c = c;
2461       g->from = g->to = i;
2462     }
2463
2464   for (this_from = from; this_from < to;)
2465     {
2466       if (! auto_flt)
2467         {
2468           for (this_to = this_from; this_to < to; this_to++)
2469             if (mchartable_lookup (flt->coverage, GREF (gstring, this_to)->c))
2470               break;
2471         }
2472       else
2473         {
2474           if (! flt_list && list_flt () < 0)
2475             {
2476               font->get_glyph_id (font, gstring, this_from, to);
2477               font->get_metrics (font, gstring, this_from, to);
2478               this_from = to;
2479               break;
2480             }
2481           for (this_to = this_from; this_to < to; this_to++)
2482             {
2483               c = GREF (gstring, this_to)->c;
2484               if (c >= flt_min_coverage && c <= flt_max_coverage)
2485                 break;
2486             }
2487           for (; this_to < to; this_to++)
2488             {
2489               c = GREF (gstring, this_to)->c;
2490               if (font->internal
2491                   && mchartable_lookup (((MFLT *) font->internal)->coverage, c))
2492                 {
2493                   flt = font->internal;
2494                   break;
2495                 }
2496               flt = mflt_find (c, font);
2497               if (flt)
2498                 {
2499                   if (CHECK_FLT_STAGES (flt))
2500                     {
2501                       font->internal = flt;
2502                       break;
2503                     }
2504                 }
2505             }
2506         }
2507
2508       if (this_from < this_to)
2509         {
2510           font->get_glyph_id (font, gstring, this_from, this_to);
2511           font->get_metrics (font, gstring, this_from, this_to);
2512           this_from = this_to;
2513         }
2514       if (this_to == to)
2515         break;
2516
2517       MDEBUG_PRINT1 (" [FLT] (%s", MSYMBOL_NAME (flt->name));
2518
2519       for (; this_to < to; this_to++)
2520         if (! mchartable_lookup (flt->coverage, GREF (gstring, this_to)->c))
2521           break;
2522
2523       if (MDEBUG_FLAG ())
2524         {
2525           if (font->family)
2526             MDEBUG_PRINT1 (" (%s)", MSYMBOL_NAME (font->family));
2527           MDEBUG_PRINT ("\n [FLT]   (SOURCE");
2528           for (i = this_from, j = 0; i < this_to; i++, j++)
2529             {
2530               if (j > 0 && j % 8 == 0)
2531                 MDEBUG_PRINT ("\n [FLT]          ");
2532               MDEBUG_PRINT1 (" %04X", GREF (gstring, i)->c);
2533             }
2534           MDEBUG_PRINT (")");
2535         }
2536
2537       for (i = 0; i < 3; i++)
2538         {
2539           /* Setup CTX.  */
2540           memset (&ctx, 0, sizeof ctx);
2541           ctx.match_indices = match_indices;
2542           ctx.font = font;
2543           ctx.cluster_begin_idx = -1;
2544           ctx.in = gstring;
2545           ctx.out = &out;
2546           j = run_stages (gstring, this_from, this_to, flt, &ctx);
2547           if (j != -2)
2548             break;
2549           out.allocated *= 2;
2550         }
2551
2552       if (j < 0)
2553         return j;
2554
2555       to += j - this_to;
2556       this_to = j;
2557
2558       if (MDEBUG_FLAG ())
2559         {
2560           MDEBUG_PRINT ("\n [FLT]   (RESULT");
2561           if (MDEBUG_FLAG () > 1)
2562             for (i = 0; this_from < this_to; this_from++, i++)
2563               {
2564                 if (i > 0 && i % 4 == 0)
2565                   MDEBUG_PRINT ("\n [FLT]          ");
2566                 g = GREF (gstring, this_from);
2567                 MDEBUG_PRINT4 (" (%04X %d %d %d)",
2568                                g->code, g->xadv, g->xoff, g->yoff);
2569               }
2570           else
2571             for (; this_from < this_to; this_from++)
2572               MDEBUG_PRINT1 (" %04X", GREF (gstring, this_from)->code);
2573           MDEBUG_PRINT ("))\n");
2574         }
2575       this_from = this_to;
2576     }
2577
2578   if (gstring->r2l)
2579     {
2580       int len = to - from;
2581
2582       GINIT (&out, len);
2583       memcpy (((char *) out.glyphs),
2584               ((char *) gstring->glyphs) + gstring->glyph_size * from,
2585               gstring->glyph_size * len);
2586       for (i = from, j = to; i < to;)
2587         {
2588           for (k = i + 1, j--; k < to && GREF (&out, k)->xadv == 0;
2589                k++, j--);
2590           GCPY (&out, i, (k - i), gstring, j);
2591           i = k;
2592         }
2593     }
2594
2595   return to;
2596 }
2597
2598 \f
2599 /* for debugging... */
2600
2601 static void
2602 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
2603 {
2604   char *prefix = (char *) alloca (indent + 1);
2605
2606   memset (prefix, 32, indent);
2607   prefix[indent] = 0;
2608
2609   if (id >= 0)
2610     fprintf (stderr, "0x%02X", id);
2611   else if (id <= CMD_ID_OFFSET_INDEX)
2612     {
2613       int idx = CMD_ID_TO_INDEX (id);
2614       FontLayoutCmd *cmd = stage->cmds + idx;
2615
2616       if (cmd->type == FontLayoutCmdTypeRule)
2617         {
2618           FontLayoutCmdRule *rule = &cmd->body.rule;
2619           int i;
2620
2621           fprintf (stderr, "(rule ");
2622           if (rule->src_type == SRC_REGEX)
2623             fprintf (stderr, "\"%s\"", rule->src.re.pattern);
2624           else if (rule->src_type == SRC_INDEX)
2625             fprintf (stderr, "%d", rule->src.match_idx);
2626           else if (rule->src_type == SRC_SEQ)
2627             fprintf (stderr, "(seq)");
2628           else if (rule->src_type == SRC_RANGE)
2629             fprintf (stderr, "(range)");
2630           else
2631             fprintf (stderr, "(invalid src)");
2632
2633           for (i = 0; i < rule->n_cmds; i++)
2634             {
2635               fprintf (stderr, "\n%s  ", prefix);
2636               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
2637             }
2638           fprintf (stderr, ")");
2639         }
2640       else if (cmd->type == FontLayoutCmdTypeCond)
2641         {
2642           FontLayoutCmdCond *cond = &cmd->body.cond;
2643           int i;
2644
2645           fprintf (stderr, "(cond");
2646           for (i = 0; i < cond->n_cmds; i++)
2647             {
2648               fprintf (stderr, "\n%s  ", prefix);
2649               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
2650             }
2651           fprintf (stderr, ")");
2652         }
2653       else if (cmd->type == FontLayoutCmdTypeOTF)
2654         {
2655           fprintf (stderr, "(otf)");
2656         }
2657       else
2658         fprintf (stderr, "(error-command)");
2659     }
2660   else if (id <= CMD_ID_OFFSET_COMBINING)
2661     fprintf (stderr, "cominging-code");
2662   else
2663     fprintf (stderr, "(predefiend %d)", id);
2664 }
2665
2666 void
2667 mdebug_dump_flt (MFLT *flt, int indent)
2668 {
2669   char *prefix = (char *) alloca (indent + 1);
2670   MPlist *plist;
2671   int stage_idx = 0;
2672
2673   memset (prefix, 32, indent);
2674   prefix[indent] = 0;
2675   fprintf (stderr, "(flt");
2676   MPLIST_DO (plist, flt->stages)
2677     {
2678       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
2679       int i;
2680
2681       fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
2682       for (i = 0; i < stage->used; i++)
2683         {
2684           fprintf (stderr, "\n%s    ", prefix);
2685           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
2686         }
2687       fprintf (stderr, ")");
2688       stage_idx++;
2689     }
2690   fprintf (stderr, ")");
2691 }
2692
2693 /*** @} */
2694
2695 /*
2696  Local Variables:
2697  coding: euc-japan
2698  End:
2699 */