Cancel previous changes.
[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_FONT_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(INDEX %d", depth, "", rule->src.match_idx);
1503     }
1504   else if (rule->src_type == SRC_HAS_GLYPH)
1505     {
1506       int encoded;
1507       unsigned code;
1508
1509       if (rule->src.supported_glyph < 0)
1510         {
1511           if (from >= to)
1512             return 0;
1513           code = GREF (ctx->in, from)->code;
1514           to = from + 1;
1515           encoded = GREF (ctx->in, from)->encoded;
1516         }
1517       else
1518         {
1519           code = rule->src.supported_glyph;
1520           to = from;
1521           encoded = 0;
1522         }
1523       if (! encoded)
1524         {
1525           static MFLTGlyphString gstring;
1526
1527           if (! gstring.glyph_size)
1528             {
1529               gstring.glyph_size = ctx->in->glyph_size;
1530               gstring.glyphs = calloc (1, gstring.glyph_size);
1531               gstring.allocated = 1;
1532               gstring.used = 1;
1533             }
1534           gstring.glyphs[0].code = code;
1535           if (ctx->font->get_glyph_id (ctx->font, &gstring, 0, 1) < 0
1536               || ! gstring.glyphs[0].encoded)
1537             return 0;
1538         }
1539     }
1540   else if (rule->src_type == SRC_OTF_SPEC)
1541     {
1542       MFLTOtfSpec *spec = &rule->src.otf_spec;
1543
1544       if (! ctx->font->check_otf)
1545         {
1546           if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
1547               || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
1548             return 0;
1549         }
1550       else if (! ctx->font->check_otf (ctx->font, spec))
1551         return 0;
1552     }
1553
1554   consumed = 0;
1555   depth++;
1556   for (i = 0; i < rule->n_cmds; i++)
1557     {
1558       int pos;
1559
1560       if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1561         {
1562           if (! consumed)
1563             continue;
1564           i--;
1565         }
1566       pos = run_command (depth, rule->cmd_ids[i], from, to, ctx);
1567       if (pos < 0)
1568         return pos;
1569       consumed = pos > from;
1570       if (consumed)
1571         from = pos;
1572     }
1573
1574   ctx->match_indices = saved_match_indices;
1575   if (MDEBUG_FLAG () > 2)
1576     MDEBUG_PRINT (")");
1577   return (rule->src_type == SRC_INDEX ? orig_from : to);
1578 }
1579
1580 static int
1581 run_cond (int depth,
1582           FontLayoutCmdCond *cond, int from, int to, FontLayoutContext *ctx)
1583 {
1584   int i, pos = 0;
1585
1586   if (MDEBUG_FLAG () > 2)
1587     MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1588   depth++;
1589   for (i = 0; i < cond->n_cmds; i++)
1590     {
1591       /* TODO: Write a code for optimization utilizaing the info
1592          cond->seq_XXX.  */
1593       if ((pos = run_command (depth, cond->cmd_ids[i], from, to, ctx))
1594           != 0)
1595         break;
1596     }
1597   if (pos < 0)
1598     return pos;
1599   if (MDEBUG_FLAG () > 2)
1600     MDEBUG_PRINT (")");
1601   return (pos);
1602 }
1603
1604 static int
1605 run_otf (int depth,
1606          MFLTOtfSpec *otf_spec, int from, int to, FontLayoutContext *ctx)
1607 {
1608   MFLTFont *font = ctx->font;
1609   int from_idx = ctx->out->used;
1610
1611   if (MDEBUG_FLAG () > 2)
1612     MDEBUG_PRINT3 ("\n [FLT] %*s%s", depth, "", MSYMBOL_NAME (otf_spec->sym));
1613
1614   font->get_glyph_id (font, ctx->in, from, to);
1615   if (! font->drive_otf)
1616     {
1617       if (ctx->out->used + (to - from) > ctx->out->allocated)
1618         return -2;
1619       font->get_metrics (font, ctx->in, from, to);
1620       GCPY (ctx->in, from, to - from, ctx->out, ctx->out->used);
1621       ctx->out->used += to - from;
1622     }
1623   else
1624     {
1625       MFLTGlyphAdjustment *adjustment;
1626       int out_len;
1627       int i;
1628
1629       adjustment = alloca ((sizeof *adjustment)
1630                            * (ctx->out->allocated - ctx->out->used));
1631       if (! adjustment)
1632         MERROR (MERROR_FLT, -1);
1633       memset (adjustment, 0,
1634               (sizeof *adjustment) * (ctx->out->allocated - ctx->out->used));
1635       to = font->drive_otf (font, otf_spec, ctx->in, from, to, ctx->out,
1636                             adjustment);
1637       if (to < 0)
1638         return to;
1639       out_len = ctx->out->used - from_idx;
1640       if (otf_spec->features[1])
1641         {
1642           MFLTGlyphAdjustment *a;
1643           MFLTGlyph *g;
1644           
1645           for (i = 0, a = adjustment; i < out_len; i++, a++)
1646             if (a->set)
1647               break;
1648           if (i < out_len)
1649             {
1650               font->get_metrics (font, ctx->out, from_idx, ctx->out->used);
1651               for (g = GREF (ctx->out, from_idx + i);
1652                    i < out_len; i++, a++, g = NEXT (ctx->out, g))
1653                 if (a->set)
1654                   {
1655                     if (a->advance_is_absolute)
1656                       {
1657                         g->xadv = a->xadv;
1658                         g->yadv = a->yadv;
1659                       }
1660                     else if (a->xadv || a->yadv)
1661                       {
1662                         g->xadv += a->xadv;
1663                         g->yadv += a->yadv;
1664                       }
1665                     if (a->xoff || a->yoff)
1666                       {
1667                         int j;
1668                         MFLTGlyph *gg = PREV (ctx->out, g);
1669                         MFLTGlyphAdjustment *aa = a;
1670
1671                         g->xoff = a->xoff;
1672                         g->yoff = a->yoff;
1673                         while (aa->back > 0)
1674                           {
1675                             for (j = 0; j < aa->back;
1676                                  j++, gg = PREV (ctx->out, gg))
1677                               g->xoff -= gg->xadv;
1678                             aa = aa - aa->back;
1679                             g->xoff += aa->xoff;
1680                             g->yoff += aa->yoff;
1681                           }
1682                       }
1683                     g->adjusted = 1;
1684                   }
1685             }
1686         }
1687     }
1688
1689   if (ctx->cluster_begin_idx >= 0)
1690     for (; from_idx < ctx->out->used; from_idx++)
1691       {
1692         MFLTGlyph *g = GREF (ctx->out, from_idx);
1693         UPDATE_CLUSTER_RANGE (ctx, g);
1694       }
1695   return to;
1696 }
1697
1698 static char work[16];
1699
1700 static char *
1701 dump_combining_code (int code)
1702 {
1703   char *vallign = "tcbB";
1704   char *hallign = "lcr";
1705   char *p;
1706   int off_x, off_y;
1707
1708   if (! code)
1709     return "none";
1710   work[0] = vallign[COMBINING_CODE_BASE_Y (code)];
1711   work[1] = hallign[COMBINING_CODE_BASE_X (code)];
1712   off_y = COMBINING_CODE_OFF_Y (code);
1713   off_x = COMBINING_CODE_OFF_X (code);
1714   if (off_y > 0)
1715     sprintf (work + 2, "+%d", off_y);
1716   else if (off_y < 0)
1717     sprintf (work + 2, "%d", off_y);
1718   else if (off_x == 0)
1719     sprintf (work + 2, ".");
1720   p = work + strlen (work);
1721   if (off_x > 0)
1722     sprintf (p, ">%d", off_x);
1723   else if (off_x < 0)
1724     sprintf (p, "<%d", -off_x);
1725   p += strlen (p);
1726   p[0] = vallign[COMBINING_CODE_ADD_Y (code)];
1727   p[1] = hallign[COMBINING_CODE_ADD_X (code)];
1728   p[2] = '\0';
1729   return work;
1730 }
1731
1732 static int
1733 run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
1734 {
1735   MFLTGlyph *g;
1736
1737   if (id >= 0)
1738     {
1739       int i;
1740
1741       /* Direct code (== ctx->code_offset + id) output.
1742          The source is not consumed.  */
1743       if (MDEBUG_FLAG () > 2)
1744         MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "",
1745                        ctx->code_offset + id);
1746       i = (from < to || from == 0) ? from : from - 1;
1747       GDUP (ctx, i);
1748       g = GREF (ctx->out, ctx->out->used - 1);
1749       g->c = g->code = ctx->code_offset + id;
1750       SET_ENCODED (g, 0);
1751       SET_MEASURED (g, 0);
1752       if (ctx->combining_code)
1753         SET_COMBINING_CODE (g, ctx, ctx->combining_code);
1754       if (ctx->left_padding)
1755         SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
1756       for (i = from; i < to; i++)
1757         {
1758           MFLTGlyph *tmp = GREF (ctx->in, i);
1759
1760           if (g->from > tmp->from)
1761             g->from = tmp->from;
1762           else if (g->to < tmp->to)
1763             g->to = tmp->to;
1764         }
1765       UPDATE_CLUSTER_RANGE (ctx, g);
1766       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1767       if (MDEBUG_FLAG () > 2)
1768         MDEBUG_PRINT (")");
1769       return (from);
1770     }
1771
1772   if (id <= CMD_ID_OFFSET_INDEX)
1773     {
1774       int idx = CMD_ID_TO_INDEX (id);
1775       FontLayoutCmd *cmd;
1776
1777       if (idx >= ctx->stage->used)
1778         MERROR (MERROR_DRAW, -1);
1779       cmd = ctx->stage->cmds + idx;
1780       if (cmd->type == FontLayoutCmdTypeRule)
1781         to = run_rule (depth, &cmd->body.rule, from, to, ctx);
1782       else if (cmd->type == FontLayoutCmdTypeCond)
1783         to = run_cond (depth, &cmd->body.cond, from, to, ctx);
1784       else if (cmd->type == FontLayoutCmdTypeOTF)
1785         to = run_otf (depth, &cmd->body.otf, from, to, ctx);
1786       return to;
1787     }
1788
1789   if (id <= CMD_ID_OFFSET_COMBINING)
1790     {
1791       ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1792       if (MDEBUG_FLAG () > 2)
1793         MDEBUG_PRINT3 ("\n [FLT] %*s(CMB %s)", depth, "",
1794                        dump_combining_code (ctx->combining_code));
1795       return from;
1796     }
1797
1798   switch (id)
1799     {
1800     case CMD_ID_COPY:
1801       {
1802         if (from >= to)
1803           return from;
1804         GDUP (ctx, from);
1805         g = GREF (ctx->out, ctx->out->used - 1);
1806         if (ctx->combining_code)
1807           SET_COMBINING_CODE (g, ctx, ctx->combining_code);
1808         if (ctx->left_padding)
1809           SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
1810         UPDATE_CLUSTER_RANGE (ctx, g);
1811         if (MDEBUG_FLAG () > 2)
1812           {
1813             if (g->c < 0)
1814               MDEBUG_PRINT2 ("\n [FLT] %*s(COPY |)", depth, "");
1815             else
1816               MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g->code);
1817           }
1818         ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1819         return (from + 1);
1820       }
1821
1822     case CMD_ID_CLUSTER_BEGIN:
1823       if (ctx->cluster_begin_idx < 0)
1824         {
1825           if (MDEBUG_FLAG () > 2)
1826             MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "",
1827                            GREF (ctx->in, from)->from);
1828           ctx->cluster_begin_idx = ctx->out->used;
1829           ctx->cluster_begin_pos = GREF (ctx->in, from)->from;
1830           ctx->cluster_end_pos = GREF (ctx->in, from)->to;
1831         }
1832       return from;
1833
1834     case CMD_ID_CLUSTER_END:
1835       if (ctx->cluster_begin_idx >= 0
1836           && ctx->cluster_begin_idx < ctx->out->used)
1837         {
1838           int i;
1839
1840           if (MDEBUG_FLAG () > 2)
1841             MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos + 1);
1842           for (i = ctx->cluster_begin_idx; i < ctx->out->used; i++)
1843             {
1844               GREF (ctx->out, i)->from = ctx->cluster_begin_pos;
1845               GREF (ctx->out, i)->to = ctx->cluster_end_pos;
1846             }
1847           ctx->cluster_begin_idx = -1;
1848         }
1849       return from;
1850
1851     case CMD_ID_SEPARATOR:
1852       {
1853         int i;
1854
1855         i = from < to ? from : from - 1;
1856         GDUP (ctx, i);
1857         g = GREF (ctx->out, ctx->out->used - 1);
1858         g->c = -1, g->code = 0;
1859         g->xadv = g->yadv = 0;
1860         SET_ENCODED (g, 0);
1861         SET_MEASURED (g, 0);
1862         return from;
1863       }
1864
1865     case CMD_ID_LEFT_PADDING:
1866       if (MDEBUG_FLAG () > 2)
1867         MDEBUG_PRINT2 ("\n [FLT] %*s[", depth, "");
1868       ctx->left_padding = 1;
1869       return from;
1870
1871     case CMD_ID_RIGHT_PADDING:
1872       if (ctx->out->used > 0)
1873         {
1874           if (MDEBUG_FLAG () > 2)
1875             MDEBUG_PRINT2 ("\n [FLT] %*s]", depth, "");
1876           g = GREF (ctx->out, ctx->out->used - 1);
1877           SET_RIGHT_PADDING (g, ctx, RightPaddingMask);
1878         }
1879       return from;
1880     }
1881
1882   MERROR (MERROR_DRAW, -1);
1883 }
1884
1885 static int
1886 run_stages (MFLTGlyphString *gstring, int from, int to,
1887             MFLT *flt, FontLayoutContext *ctx)
1888 {
1889   MFLTGlyphString buf, *temp;
1890   int stage_idx = 0;
1891   int orig_from = from, orig_to = to;
1892   int from_pos, to_pos, len;
1893   int i, j;
1894   MFLTGlyph *g;
1895   MPlist *stages = flt->stages;
1896
1897   from_pos = GREF (ctx->in, from)->from;
1898   to_pos = GREF (ctx->in, to - 1)->to;
1899   len = to_pos - from_pos;
1900
1901   buf = *(ctx->in);
1902   buf.glyphs = NULL;
1903   GINIT (ctx->out, ctx->out->allocated);
1904   ctx->encoded = alloca (ctx->out->allocated);
1905   if (! ctx->out->glyphs || ! ctx->encoded)
1906     return -1;
1907
1908   for (stage_idx = 0; 1; stage_idx++)
1909     {
1910       MCharTable *table;
1911       int result;
1912
1913       ctx->stage = (FontLayoutStage *) MPLIST_VAL (stages);
1914       table = ctx->stage->category;
1915       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1916       ctx->encoded_offset = from;
1917       for (i = from; i < to; i++)
1918         {
1919           MFLTGlyph *g = GREF (ctx->in, i);
1920           char enc = (GET_ENCODED (g)
1921                       ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
1922                       : g->code
1923                       ? (int) mchartable_lookup (table, g->code)
1924                       : ' ');
1925
1926           ctx->encoded[i - from] = enc;
1927           if (! enc && stage_idx == 0)
1928             {
1929               to = i;
1930               break;
1931             }
1932         }
1933       ctx->encoded[i - from] = '\0';
1934       ctx->match_indices[0] = from;
1935       ctx->match_indices[1] = to;
1936       for (i = 2; i < NMATCH; i++)
1937         ctx->match_indices[i] = -1;
1938
1939       if (MDEBUG_FLAG () > 2)
1940         {
1941           MDEBUG_PRINT2 ("\n [FLT]   (STAGE %d \"%s\"", stage_idx,
1942                          ctx->encoded);
1943           MDEBUG_PRINT (" (");
1944           for (i = from; i < to; i++)
1945             {
1946               g = GREF (ctx->in, i);
1947               if (g->c == -1)
1948                 MDEBUG_PRINT2 ("%*s|", (i > 0), "");
1949               else
1950                 MDEBUG_PRINT3 ("%*s%04X", (i > 0), "", GREF (ctx->in, i)->code);
1951             }
1952           MDEBUG_PRINT (")");
1953         }
1954       result = run_command (4, INDEX_TO_CMD_ID (0), from, to, ctx);
1955       if (MDEBUG_FLAG () > 2)
1956         MDEBUG_PRINT (")");
1957       if (result < 0)
1958         return result;
1959
1960       stages = MPLIST_NEXT (stages);
1961       /* If this is the last stage, break the loop. */
1962       if (MPLIST_TAIL_P (stages))
1963         break;
1964
1965       /* Otherwise, prepare for the next stage.   */
1966       temp = ctx->in;
1967       ctx->in = ctx->out;
1968       if (buf.glyphs)
1969         ctx->out = temp;
1970       else
1971         {
1972           GINIT (&buf, ctx->out->allocated);
1973           ctx->out = &buf;
1974         }
1975       ctx->out->used = 0;
1976
1977       from = 0;
1978       to = ctx->in->used;
1979     }
1980
1981   if (ctx->out->used > 0)
1982     {
1983       int *g_indices;
1984       int x_ppem = ctx->font->x_ppem << 6, y_ppem = ctx->font->y_ppem << 6;
1985
1986       /* Remove separator glyphs.  */
1987       for (i = 0; i < ctx->out->used;)
1988         {
1989           g = GREF (ctx->out, i);
1990           if (g->c < 0)
1991             GREPLACE (NULL, 0, 0, ctx->out, i, i + 1);
1992           else
1993             i++;
1994         }
1995
1996       /* Get actual glyph IDs of glyphs.  */
1997       ctx->font->get_glyph_id (ctx->font, ctx->out, 0, ctx->out->used);
1998
1999       /* Check if all characters in the range are covered by some
2000          glyph(s).  If not, change <from> and <to> of glyphs to cover
2001          uncovered characters.  */
2002       g_indices = alloca (sizeof (int) * len);
2003       if (! g_indices)
2004         return -1;
2005       for (i = 0; i < len; i++) g_indices[i] = -1;
2006       for (i = 0; i < ctx->out->used; i++)
2007         {
2008           int pos;
2009
2010           g = GREF (ctx->out, i);
2011           for (pos = g->from; pos <= g->to; pos++)
2012             if (g_indices[pos - orig_from] < 0)
2013               g_indices[pos - orig_from] = i;
2014         }
2015       for (i = 0; i < len; i++)
2016         if (g_indices[i] < 0)
2017           {
2018             if (i == 0)
2019               {
2020                 int this_from;
2021
2022                 for (i++; i < len && g_indices[i] < 0; i++);
2023                 j = g_indices[i];
2024                 g = GREF (ctx->out, j);
2025                 this_from = g->from;
2026                 do {
2027                   g->from = orig_from + i;
2028                 } while (++j < ctx->out->used
2029                          && (g = GREF (ctx->out, j))
2030                          && g->from == this_from);
2031               }
2032             else
2033               {
2034                 int this_to;
2035
2036                 j = g_indices[i - 1];
2037                 g = GREF (ctx->out, j);
2038                 this_to = g->to;
2039                 do {
2040                   g->to = orig_from + i + 1;
2041                 } while (--j >= 0
2042                          && (g = GREF (ctx->out, j))
2043                          && g->to == this_to);
2044               }
2045           }
2046
2047       ctx->font->get_metrics (ctx->font, ctx->out, 0, ctx->out->used);
2048
2049       /* Handle combining.  */
2050       if (ctx->check_mask & CombiningCodeMask)
2051         {
2052           MFLTGlyph *base = GREF (ctx->out, 0);
2053           int base_height = base->ascent + base->descent;
2054           int combining_code;
2055
2056           for (i = 1; i < ctx->out->used; i++)
2057             {
2058               if ((g = GREF (ctx->out, i))
2059                   && (combining_code = GET_COMBINING_CODE (g)))
2060                 {
2061                   int height = g->ascent + g->descent;
2062                   int base_x, base_y, add_x, add_y, off_x, off_y;
2063
2064                   if (base->from > g->from)
2065                     base->from = g->from;
2066                   else if (base->to < g->to)
2067                     base->to = g->to;
2068                 
2069                   base_x = COMBINING_CODE_BASE_X (combining_code);
2070                   base_y = COMBINING_CODE_BASE_Y (combining_code);
2071                   add_x = COMBINING_CODE_ADD_X (combining_code);
2072                   add_y = COMBINING_CODE_ADD_Y (combining_code);
2073                   off_x = COMBINING_CODE_OFF_X (combining_code);
2074                   off_y = COMBINING_CODE_OFF_Y (combining_code);
2075
2076                   g->xoff = ((base->xadv * base_x - g->xadv * add_x) / 2
2077                              + x_ppem * off_x / 100 - base->xadv);
2078                   if (base_y < 3)
2079                     g->yoff = base_height * base_y / 2 - base->ascent;
2080                   else
2081                     g->yoff = 0;
2082                   if (add_y < 3)
2083                     g->yoff -= height * add_y / 2 - g->ascent;
2084                   g->yoff -= y_ppem * off_y / 100;
2085                   if (base->lbearing > base->xadv + g->lbearing + g->xoff)
2086                     base->lbearing = base->xadv + g->lbearing + g->xoff;
2087                   if (base->rbearing < base->xadv + g->xadv + g->xoff)
2088                     base->rbearing = base->xadv + g->xadv + g->xoff;
2089                   if (base->ascent < g->ascent - g->yoff)
2090                     base->ascent = g->ascent - g->yoff;
2091                   if (base->descent < g->descent - g->yoff)
2092                     base->descent = g->descent - g->yoff;
2093                   g->xadv = g->yadv = 0;
2094                   if (GET_RIGHT_PADDING (g))
2095                     SET_RIGHT_PADDING (base, ctx, RightPaddingMask);
2096                   g->adjusted = 1;
2097                 }
2098               else
2099                 {
2100                   base = g;
2101                   base_height = g->ascent + g->descent;
2102                 }
2103             }
2104         }
2105
2106       /* Handle padding */
2107       if (ctx->check_mask & (LeftPaddingMask | RightPaddingMask))
2108         for (i = 0; i < ctx->out->used; i++)
2109           {
2110             g = GREF (ctx->out, i);
2111             if (! GET_COMBINING_CODE (g))
2112               {
2113                 if (GET_RIGHT_PADDING (g) && g->rbearing > g->xadv)
2114                   {
2115                     g->xadv = g->rbearing;
2116                     g->adjusted = 1;
2117                   }
2118                 if (GET_LEFT_PADDING (g) && g->lbearing < 0)
2119                   {
2120                     g->xoff += - g->lbearing;
2121                     g->xadv += - g->lbearing;
2122                     g->rbearing += - g->lbearing;
2123                     g->lbearing = 0;
2124                     g->adjusted = 1;
2125                   }
2126               }
2127           }
2128     }
2129
2130   GREPLACE (ctx->out, 0, ctx->out->used, gstring, orig_from, orig_to);
2131   to = orig_from + ctx->out->used;
2132   return to;
2133 }
2134
2135 static void
2136 setup_combining_coverage (int from, int to, void *val, void *arg)
2137 {
2138   int combining_class = (int) val;
2139   int category = 0;
2140
2141   if (combining_class < 200)
2142     category = 'a';
2143   else if (combining_class <= 204)
2144     {
2145       if ((combining_class % 2) == 0)
2146         category = "bcd"[(combining_class - 200) / 2];
2147     }
2148   else if (combining_class <= 232)
2149     {
2150       if ((combining_class % 2) == 0)
2151         category = "efghijklmnopq"[(combining_class - 208) / 2];
2152     }
2153   else if (combining_class == 233)
2154     category = 'r';
2155   else if (combining_class == 234)
2156     category = 's';
2157   else if (combining_class == 240)
2158     category = 't';
2159   mchartable_set_range ((MCharTable *) arg, from, to, (void *) category);
2160 }
2161
2162 static void
2163 setup_combining_flt (MFLT *flt)
2164 {
2165   MSymbol type;
2166   MCharTable *combininig_class_table
2167     = mchar_get_prop_table (Mcombining_class, &type);
2168
2169   mchartable_set_range (flt->coverage, 0, 0x10FFFF, (void *) 'u');
2170   if (combininig_class_table)
2171     mchartable_map (combininig_class_table, (void *) 0,
2172                     setup_combining_coverage, flt->coverage);
2173 }
2174
2175 #define CHECK_FLT_STAGES(flt) ((flt)->stages || load_flt (flt, NULL) == 0)
2176
2177 \f
2178 /* Internal API */
2179
2180 int m17n__flt_initialized;
2181
2182 \f
2183 /* External API */
2184
2185 /* The following two are actually not exposed to a user but concealed
2186    by the macro M17N_INIT (). */
2187
2188 void
2189 m17n_init_flt (void)
2190 {
2191   int mdebug_flag = MDEBUG_INIT;
2192
2193   merror_code = MERROR_NONE;
2194   if (m17n__flt_initialized++)
2195     return;
2196   m17n_init_core ();
2197   if (merror_code != MERROR_NONE)
2198     {
2199       m17n__flt_initialized--;
2200       return;
2201     }
2202
2203   MDEBUG_PUSH_TIME ();
2204
2205   Mcond = msymbol ("cond");
2206   Mrange = msymbol ("range");
2207   Mfont = msymbol ("font");
2208   Mlayouter = msymbol ("layouter");
2209   Mcombining = msymbol ("combining");
2210   Mfont_facility = msymbol ("font-facility");
2211   Mgenerator = msymbol ("generator");
2212   Mend = msymbol ("end");
2213
2214   MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the flt modules."));
2215   MDEBUG_POP_TIME ();
2216 }
2217
2218 void
2219 m17n_fini_flt (void)
2220 {
2221   int mdebug_flag = MDEBUG_FINI;
2222
2223   if (m17n__flt_initialized == 0
2224       || --m17n__flt_initialized > 0)
2225     return;
2226
2227   MDEBUG_PUSH_TIME ();
2228   free_flt_list ();
2229   MDEBUG_PRINT_TIME ("FINI", (stderr, " to finalize the flt modules."));
2230   MDEBUG_POP_TIME ();
2231   m17n_fini_core ();
2232 }
2233
2234 /*** @} */ 
2235 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
2236
2237 /*** @addtogroup m17nFLT */
2238 /*** @{ */
2239 /*=*/
2240
2241 /*=*/
2242 /***en
2243     @brief Return an FLT object that has a specified name.
2244
2245     The mflt_get () function returns an FLT object whose name is $NAME.
2246
2247     @return
2248     If the operation was successful, mflt_get () returns a pointer
2249     to the found FLT object.  Otherwise, it returns @c NULL.  */
2250
2251 /***ja
2252     @brief »ØÄꤵ¤ì¤¿Ì¾Á°¤ò»ý¤Ä FLT ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤¹.
2253
2254     ´Ø¿ô mflt_get () ¤Ï¡¢$NAME ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä FLT ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤¹¡£
2255
2256     @return
2257     ¤â¤·À®¸ù¤¹¤ì¤Ð¡¢mflt_get () ¤Ï¸«¤Ä¤«¤Ã¤¿ FLT
2258     ¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£  */
2259
2260 MFLT *
2261 mflt_get (MSymbol name)
2262 {
2263   MFLT *flt;
2264
2265   if (! flt_list && list_flt () < 0)
2266     return NULL;
2267   flt = mplist_get (flt_list, name);
2268   if (! flt || ! CHECK_FLT_STAGES (flt))
2269     return NULL;
2270   if (flt->name == Mcombining
2271       && ! mchartable_lookup (flt->coverage, 0))
2272     setup_combining_flt (flt);
2273
2274   return flt;
2275 }
2276
2277 /*=*/
2278 /***en
2279     @brief Find an FLT suitable for the specified character and font.
2280
2281     The mflt_find () function returns the most appropriate FLT for
2282     layouting character $C with font $FONT.
2283
2284     @return
2285     If the operation was successful, mflt_find () returns a pointer
2286     to the found FLT object.  Otherwise, it returns @c NULL.  */
2287
2288 /***ja
2289     @brief »ØÄꤵ¤ì¤¿Ê¸»ú¤È¥Õ¥©¥ó¥È¤Ë¹ç¤Ã¤¿ FLT ¤òõ¤¹.
2290
2291     ´Ø¿ô mflt_find () ¤Ï¡¢Ê¸»ú $C ¤ò¥Õ¥©¥ó¥È $FONT
2292     ¤Ç¥ì¥¤¥¢¥¦¥È¤¹¤ë¤¿¤á¤ËºÇ¤âŬÀڤʠFLT ¤òÊÖ¤¹¡£
2293
2294     @return
2295     ¤â¤·À®¸ù¤¹¤ì¤Ð¡¢mflt_find () ¤Ï¸«¤Ä¤«¤Ã¤¿ FLT
2296     ¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£  */
2297
2298 MFLT *
2299 mflt_find (int c, MFLTFont *font)
2300 {
2301   MPlist *plist;
2302   MFLT *flt;
2303   static MSymbol unicode_bmp = NULL, unicode_full = NULL;
2304
2305   if (! unicode_bmp)
2306     {
2307       unicode_bmp = msymbol ("unicode-bmp");
2308       unicode_full = msymbol ("unicode-full");
2309     }
2310
2311   if (! flt_list && list_flt () < 0)
2312     return NULL;
2313   if (font)
2314     {
2315       MFLT *best = NULL;
2316
2317       MPLIST_DO (plist, flt_list)
2318         {
2319           flt = MPLIST_VAL (plist);
2320           if (flt->registry != unicode_bmp
2321               && flt->registry != unicode_full)
2322             continue;
2323           if (flt->family && flt->family != font->family)
2324             continue;
2325           if (c >= 0
2326               && ! mchartable_lookup (flt->coverage, c))
2327             continue;
2328           if (flt->otf.sym)
2329             {
2330               MFLTOtfSpec *spec = &flt->otf;
2331
2332               if (! font->check_otf)
2333                 {
2334                   if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
2335                       || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
2336                     continue;
2337                 }
2338               else if (! font->check_otf (font, spec))
2339                 continue;
2340               return flt;
2341             }
2342           best = flt;
2343         }
2344       return best;
2345     }
2346   if (c >= 0)
2347     {
2348       MPLIST_DO (plist, flt_list)
2349         {
2350           flt = MPLIST_VAL (plist);
2351           if (mchartable_lookup (flt->coverage, c))
2352             return flt;
2353         }
2354     }
2355   return NULL;
2356 }
2357
2358 /*=*/
2359 /***en
2360     @brief Return the name of an FLT.
2361
2362     The mflt_name () function returns the name of $FLT.  */
2363
2364 /***ja
2365     @brief FLT ¤Î̾Á°¤òÊÖ¤¹.
2366
2367     ´Ø¿ô mflt_name () ¤Ï $FLT ¤Î̾Á°¤òÊÖ¤¹¡£  */
2368
2369 const char *
2370 mflt_name (MFLT *flt)
2371 {
2372   return MSYMBOL_NAME (flt->name);
2373 }
2374
2375 /*=*/
2376 /***en
2377     @brief Return a coverage of a FLT.
2378
2379     The mflt_coverage () function returns a char-table that contains
2380     nonzero values for characters supported by $FLT.  */
2381
2382 /***
2383     @brief FLT ¤ÎÈϰϤòÊÖ¤¹.
2384
2385     ´Ø¿ô mflt_coverage () ¤Ï¡¢$FLT ¤¬¥µ¥Ý¡¼¥È¤¹¤ëʸ»ú¤ËÂФ·¤Æ
2386     0 ¤Ç¤Ê¤¤Ãͤò´Þ¤àʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£  */
2387
2388 MCharTable *
2389 mflt_coverage (MFLT *flt)
2390 {
2391   return flt->coverage;
2392 }
2393
2394 /*=*/
2395 /***en
2396     @brief Layout characters with an FLT.
2397
2398     The mflt_run () function layouts characters in $GSTRING between
2399     $FROM (inclusive) and $TO (exclusive) with $FONT.  If $FLT is
2400     nonzero, it is used for all the charaters.  Otherwise, appropriate
2401     FLTs are automatically chosen.
2402
2403     @retval >=0
2404     The operation was successful.  The value is the index to the
2405     glyph, which was previously indexed by $TO, in $GSTRING->glyphs.
2406
2407     @retval -2
2408     $GSTRING->glyphs is too short to store the result.  The caller can
2409     call this fucntion again with a longer $GSTRING->glyphs.
2410
2411     @retval -1
2412     Some other error occurred.  */
2413
2414 /***ja
2415     @brief FLT ¤ò»È¤Ã¤Æʸ»ú¤ò¥ì¥¤¥¢¥¦¥È¤¹¤ë.
2416
2417     ´Ø¿ô mflt_run () ¤Ï¡¢$GSTRING Ãæ¤Î $FROM ¤«¤é $TO Ä¾Á°¤Þ¤Ç¤Îʸ»ú¤ò
2418     $FONT ¤òÍѤ¤¤Æ¥ì¥¤¥¢¥¦¥È¤¹¤ë¡£¤â¤· $FLT
2419     ¤¬¥¼¥í¤Ç¤Ê¤±¤ì¤Ð¡¢¤½¤ÎÃͤò¤¹¤Ù¤Æ¤Îʸ»ú¤ËÂФ·¤ÆÍѤ¤¤ë¡£
2420     ¤½¤¦¤Ç¤Ê¤±¤ì¤ÐŬÀڤʠFLT ¤ò¼«Æ°Åª¤ËÁªÂò¤¹¤ë¡£
2421
2422     @retval >=0
2423     ¼Â¹ÔÀ®¸ù¤ò¼¨¤¹¡£ÊÖ¤µ¤ì¤ëÃͤϡ¢$GSTRING->glyphs Ãæ¤Ç°ÊÁ° $TO
2424     ¤Ë¤è¤Ã¤Æ¼¨¤µ¤ì¤Æ¤¤¤¿¥°¥ê¥Õ¤Ø¤Î¥¤¥ó¥Ç¥¯¥¹¤Ç¤¢¤ë¡£
2425
2426     @retval -2
2427     ·ë²Ì¤ò³ÊǼ¤¹¤ë¤Ë¤Ï $GSTRING->glyphs ¤¬Ã»¤¹¤®¤ë¤³¤È¤ò¼¨¤¹¡£
2428     ¸Æ¤Ó½Ð¤·Â¦¤Ï¡¢¤è¤êŤ¤ $GSTRING->glyphs
2429     ¤òÍѤ¤¤ÆºÆÅÙ¤³¤Î´Ø¿ô¤ò¸Æ¤Ö¤³¤È¤¬¤Ç¤­¤ë¡£
2430
2431     @retval -1
2432     ¤½¤Î¾¤Î¥¨¥é¡¼¤¬µ¯¤­¤¿¤³¤È¤ò¼¨¤¹¡£  */
2433
2434 int
2435 mflt_run (MFLTGlyphString *gstring, int from, int to,
2436           MFLTFont *font, MFLT *flt)
2437 {
2438   FontLayoutContext ctx;
2439   int match_indices[NMATCH];
2440   MFLTGlyph *g;
2441   MFLTGlyphString out;
2442   int auto_flt = ! flt;
2443   int c, i, j, k;
2444   int this_from, this_to;
2445
2446   out = *gstring;
2447   out.glyphs = NULL;
2448   /* This is usually sufficient, but if not, we retry with the larger
2449      values at most 3 times.  This value is also used for the
2450      allocating size of ctx.encoded.  */
2451   out.allocated = (to - from) * 4;
2452
2453   for (i = from; i < to; i++)
2454     {
2455       g = GREF (gstring, i);
2456       c = g->c;
2457       memset (g, 0, sizeof (MFLTGlyph));
2458       g->code = g->c = c;
2459       g->from = g->to = i;
2460     }
2461
2462   for (this_from = from; this_from < to;)
2463     {
2464       if (! auto_flt)
2465         {
2466           for (this_to = this_from; this_to < to; this_to++)
2467             if (mchartable_lookup (flt->coverage, GREF (gstring, this_to)->c))
2468               break;
2469         }
2470       else
2471         {
2472           if (! flt_list && list_flt () < 0)
2473             {
2474               font->get_glyph_id (font, gstring, this_from, to);
2475               font->get_metrics (font, gstring, this_from, to);
2476               this_from = to;
2477               break;
2478             }
2479           for (this_to = this_from; this_to < to; this_to++)
2480             {
2481               c = GREF (gstring, this_to)->c;
2482               if (c >= flt_min_coverage && c <= flt_max_coverage)
2483                 break;
2484             }
2485           for (; this_to < to; this_to++)
2486             {
2487               c = GREF (gstring, this_to)->c;
2488               if (font->internal
2489                   && mchartable_lookup (((MFLT *) font->internal)->coverage, c))
2490                 {
2491                   flt = font->internal;
2492                   break;
2493                 }
2494               flt = mflt_find (c, font);
2495               if (flt)
2496                 {
2497                   if (CHECK_FLT_STAGES (flt))
2498                     {
2499                       font->internal = flt;
2500                       break;
2501                     }
2502                 }
2503             }
2504         }
2505
2506       if (this_from < this_to)
2507         {
2508           font->get_glyph_id (font, gstring, this_from, this_to);
2509           font->get_metrics (font, gstring, this_from, this_to);
2510           this_from = this_to;
2511         }
2512       if (this_to == to)
2513         break;
2514
2515       MDEBUG_PRINT1 (" [FLT] (%s", MSYMBOL_NAME (flt->name));
2516
2517       for (; this_to < to; this_to++)
2518         if (! mchartable_lookup (flt->coverage, GREF (gstring, this_to)->c))
2519           break;
2520
2521       if (MDEBUG_FLAG ())
2522         {
2523           if (font->family)
2524             MDEBUG_PRINT1 (" (%s)", MSYMBOL_NAME (font->family));
2525           MDEBUG_PRINT ("\n [FLT]   (SOURCE");
2526           for (i = this_from, j = 0; i < this_to; i++, j++)
2527             {
2528               if (j > 0 && j % 8 == 0)
2529                 MDEBUG_PRINT ("\n [FLT]          ");
2530               MDEBUG_PRINT1 (" %04X", GREF (gstring, i)->c);
2531             }
2532           MDEBUG_PRINT (")");
2533         }
2534
2535       for (i = 0; i < 3; i++)
2536         {
2537           /* Setup CTX.  */
2538           memset (&ctx, 0, sizeof ctx);
2539           ctx.match_indices = match_indices;
2540           ctx.font = font;
2541           ctx.cluster_begin_idx = -1;
2542           ctx.in = gstring;
2543           ctx.out = &out;
2544           j = run_stages (gstring, this_from, this_to, flt, &ctx);
2545           if (j != -2)
2546             break;
2547           out.allocated *= 2;
2548         }
2549
2550       if (j < 0)
2551         return j;
2552
2553       to += j - this_to;
2554       this_to = j;
2555
2556       if (MDEBUG_FLAG ())
2557         {
2558           MDEBUG_PRINT ("\n [FLT]   (RESULT");
2559           if (MDEBUG_FLAG () > 1)
2560             for (i = 0; this_from < this_to; this_from++, i++)
2561               {
2562                 if (i > 0 && i % 4 == 0)
2563                   MDEBUG_PRINT ("\n [FLT]          ");
2564                 g = GREF (gstring, this_from);
2565                 MDEBUG_PRINT4 (" (%04X %d %d %d)",
2566                                g->code, g->xadv, g->xoff, g->yoff);
2567               }
2568           else
2569             for (; this_from < this_to; this_from++)
2570               MDEBUG_PRINT1 (" %04X", GREF (gstring, this_from)->code);
2571           MDEBUG_PRINT ("))\n");
2572         }
2573       this_from = this_to;
2574     }
2575
2576   if (gstring->r2l)
2577     {
2578       int len = to - from;
2579
2580       GINIT (&out, len);
2581       memcpy (((char *) out.glyphs),
2582               ((char *) gstring->glyphs) + gstring->glyph_size * from,
2583               gstring->glyph_size * len);
2584       for (i = from, j = to; i < to;)
2585         {
2586           for (k = i + 1, j--; k < to && GREF (&out, k)->xadv == 0;
2587                k++, j--);
2588           GCPY (&out, i, (k - i), gstring, j);
2589           i = k;
2590         }
2591     }
2592
2593   return to;
2594 }
2595
2596 \f
2597 /* for debugging... */
2598
2599 static void
2600 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
2601 {
2602   char *prefix = (char *) alloca (indent + 1);
2603
2604   memset (prefix, 32, indent);
2605   prefix[indent] = 0;
2606
2607   if (id >= 0)
2608     fprintf (stderr, "0x%02X", id);
2609   else if (id <= CMD_ID_OFFSET_INDEX)
2610     {
2611       int idx = CMD_ID_TO_INDEX (id);
2612       FontLayoutCmd *cmd = stage->cmds + idx;
2613
2614       if (cmd->type == FontLayoutCmdTypeRule)
2615         {
2616           FontLayoutCmdRule *rule = &cmd->body.rule;
2617           int i;
2618
2619           fprintf (stderr, "(rule ");
2620           if (rule->src_type == SRC_REGEX)
2621             fprintf (stderr, "\"%s\"", rule->src.re.pattern);
2622           else if (rule->src_type == SRC_INDEX)
2623             fprintf (stderr, "%d", rule->src.match_idx);
2624           else if (rule->src_type == SRC_SEQ)
2625             fprintf (stderr, "(seq)");
2626           else if (rule->src_type == SRC_RANGE)
2627             fprintf (stderr, "(range)");
2628           else
2629             fprintf (stderr, "(invalid src)");
2630
2631           for (i = 0; i < rule->n_cmds; i++)
2632             {
2633               fprintf (stderr, "\n%s  ", prefix);
2634               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
2635             }
2636           fprintf (stderr, ")");
2637         }
2638       else if (cmd->type == FontLayoutCmdTypeCond)
2639         {
2640           FontLayoutCmdCond *cond = &cmd->body.cond;
2641           int i;
2642
2643           fprintf (stderr, "(cond");
2644           for (i = 0; i < cond->n_cmds; i++)
2645             {
2646               fprintf (stderr, "\n%s  ", prefix);
2647               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
2648             }
2649           fprintf (stderr, ")");
2650         }
2651       else if (cmd->type == FontLayoutCmdTypeOTF)
2652         {
2653           fprintf (stderr, "(otf)");
2654         }
2655       else
2656         fprintf (stderr, "(error-command)");
2657     }
2658   else if (id <= CMD_ID_OFFSET_COMBINING)
2659     fprintf (stderr, "cominging-code");
2660   else
2661     fprintf (stderr, "(predefiend %d)", id);
2662 }
2663
2664 void
2665 mdebug_dump_flt (MFLT *flt, int indent)
2666 {
2667   char *prefix = (char *) alloca (indent + 1);
2668   MPlist *plist;
2669   int stage_idx = 0;
2670
2671   memset (prefix, 32, indent);
2672   prefix[indent] = 0;
2673   fprintf (stderr, "(flt");
2674   MPLIST_DO (plist, flt->stages)
2675     {
2676       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
2677       int i;
2678
2679       fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
2680       for (i = 0; i < stage->used; i++)
2681         {
2682           fprintf (stderr, "\n%s    ", prefix);
2683           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
2684         }
2685       fprintf (stderr, ")");
2686       stage_idx++;
2687     }
2688   fprintf (stderr, ")");
2689 }
2690
2691 /*** @} */
2692
2693 /*
2694  Local Variables:
2695  coding: euc-japan
2696  End:
2697 */