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