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