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