7423026341376d084eb1254df120eb8bb35a82f3
[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
2022   if (MDEBUG_FLAG () > 2)
2023     MDEBUG_PRINT3 ("\n [FLT] %*s%s", depth, "", MSYMBOL_NAME (otf_spec->sym));
2024
2025   if (! otf_spec->features[0] && ! otf_spec->features[1])
2026     {
2027       /* Reset categories.  */
2028       MCharTable *table = ctx->category->table;
2029       int i;
2030
2031       for (i = from; i < to; i++)
2032         {
2033           MFLTGlyph *g = GREF (ctx->in, i);
2034
2035           if (! GET_COMBINED (g))
2036             {
2037               char enc = (GET_ENCODED (g)
2038                           ? (g->c > 0 ? (int) mchartable_lookup (table, g->c)
2039                              : 1)
2040                           : g->code
2041                           ? (int) mchartable_lookup (table, g->code)
2042                           : ' ');
2043               SET_CATEGORY_CODE (g, enc);
2044               ctx->encoded[i - ctx->encoded_offset] = enc;
2045             }
2046         }
2047       return from;
2048     }
2049
2050   if (ctx->stage->category->feature_table.size == 0)
2051     return from;
2052
2053   font->get_glyph_id (font, ctx->in, from, to);
2054   if (mflt_try_otf)
2055     {
2056       to = mflt_try_otf (font, otf_spec, ctx->in, from, to);
2057       if (to < 0)
2058         return from;
2059       decode_packed_otf_tag (ctx, ctx->in, from, to, ctx->stage->category);
2060     }
2061   return from;
2062 }
2063
2064 static char work[16];
2065
2066 static char *
2067 dump_combining_code (int code)
2068 {
2069   char *vallign = "tcbB";
2070   char *hallign = "lcr";
2071   char *p;
2072   int off_x, off_y;
2073
2074   if (! code)
2075     return "none";
2076   work[0] = vallign[COMBINING_CODE_BASE_Y (code)];
2077   work[1] = hallign[COMBINING_CODE_BASE_X (code)];
2078   off_y = COMBINING_CODE_OFF_Y (code);
2079   off_x = COMBINING_CODE_OFF_X (code);
2080   if (off_y > 0)
2081     sprintf (work + 2, "+%d", off_y);
2082   else if (off_y < 0)
2083     sprintf (work + 2, "%d", off_y);
2084   else if (off_x == 0)
2085     sprintf (work + 2, ".");
2086   p = work + strlen (work);
2087   if (off_x > 0)
2088     sprintf (p, ">%d", off_x);
2089   else if (off_x < 0)
2090     sprintf (p, "<%d", -off_x);
2091   p += strlen (p);
2092   p[0] = vallign[COMBINING_CODE_ADD_Y (code)];
2093   p[1] = hallign[COMBINING_CODE_ADD_X (code)];
2094   p[2] = '\0';
2095   return work;
2096 }
2097
2098 static int
2099 run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
2100 {
2101   MFLTGlyph *g;
2102
2103   if (id >= 0)
2104     {
2105       int i;
2106       MCharTable *table = ctx->category ? ctx->category->table : NULL;
2107       char enc;
2108
2109       /* Direct code (== ctx->code_offset + id) output.
2110          The source is not consumed.  */
2111       if (MDEBUG_FLAG () > 2)
2112         MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "",
2113                        ctx->code_offset + id);
2114       i = (from < to || from == 0) ? from : from - 1;
2115       GDUP (ctx, i);
2116       g = GREF (ctx->out, ctx->out->used - 1);
2117       g->c = g->code = ctx->code_offset + id;
2118       if (ctx->combining_code)
2119         SET_COMBINING_CODE (g, ctx, ctx->combining_code);
2120       else if (table)
2121         {
2122           enc = (GET_ENCODED (g)
2123                  ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
2124                  : g->code
2125                  ? (int) mchartable_lookup (table, g->code)
2126                  : ' ');
2127           SET_CATEGORY_CODE (g, enc);
2128         }
2129       SET_ENCODED (g, 0);
2130       SET_MEASURED (g, 0);
2131       if (ctx->left_padding)
2132         SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
2133       for (i = from; i < to; i++)
2134         {
2135           MFLTGlyph *tmp = GREF (ctx->in, i);
2136
2137           if (g->from > tmp->from)
2138             g->from = tmp->from;
2139           else if (g->to < tmp->to)
2140             g->to = tmp->to;
2141         }
2142       if (ctx->cluster_begin_idx >= 0)
2143         UPDATE_CLUSTER_RANGE (ctx, g);
2144       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
2145       if (MDEBUG_FLAG () > 2)
2146         MDEBUG_PRINT (")");
2147       return (from);
2148     }
2149
2150   if (id <= CMD_ID_OFFSET_INDEX)
2151     {
2152       int idx = CMD_ID_TO_INDEX (id);
2153       FontLayoutCmd *cmd;
2154
2155       if (idx >= ctx->stage->used)
2156         MERROR (MERROR_DRAW, -1);
2157       cmd = ctx->stage->cmds + idx;
2158       if (cmd->type == FontLayoutCmdTypeRule)
2159         to = run_rule (depth, &cmd->body.rule, from, to, ctx);
2160       else if (cmd->type == FontLayoutCmdTypeCond)
2161         to = run_cond (depth, &cmd->body.cond, from, to, ctx);
2162       else if (cmd->type == FontLayoutCmdTypeOTF)
2163         to = run_otf (depth, &cmd->body.otf, from, to, ctx);
2164       else if (cmd->type == FontLayoutCmdTypeOTFCategory)
2165         to = try_otf (depth, &cmd->body.otf, from, to, ctx);
2166       return to;
2167     }
2168
2169   if (id <= CMD_ID_OFFSET_COMBINING)
2170     {
2171       ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
2172       if (MDEBUG_FLAG () > 2)
2173         MDEBUG_PRINT3 ("\n [FLT] %*s(CMB %s)", depth, "",
2174                        dump_combining_code (ctx->combining_code));
2175       return from;
2176     }
2177
2178   switch (id)
2179     {
2180     case CMD_ID_COPY:
2181       {
2182         if (from >= to)
2183           return from;
2184         GDUP (ctx, from);
2185         g = GREF (ctx->out, ctx->out->used - 1);
2186         if (ctx->combining_code)
2187           SET_COMBINING_CODE (g, ctx, ctx->combining_code);
2188         if (ctx->left_padding)
2189           SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
2190         if (ctx->cluster_begin_idx >= 0)
2191           UPDATE_CLUSTER_RANGE (ctx, g);
2192         if (MDEBUG_FLAG () > 2)
2193           {
2194             if (g->c < 0)
2195               MDEBUG_PRINT2 ("\n [FLT] %*s(COPY |)", depth, "");
2196             else
2197               MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g->c);
2198           }
2199         ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
2200         return (from + 1);
2201       }
2202
2203     case CMD_ID_CLUSTER_BEGIN:
2204       if (ctx->cluster_begin_idx < 0)
2205         {
2206           if (MDEBUG_FLAG () > 2)
2207             MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "",
2208                            GREF (ctx->in, from)->from);
2209           ctx->cluster_begin_idx = ctx->out->used;
2210           ctx->cluster_begin_pos = GREF (ctx->in, from)->from;
2211           ctx->cluster_end_pos = GREF (ctx->in, from)->to;
2212         }
2213       return from;
2214
2215     case CMD_ID_CLUSTER_END:
2216       if (ctx->cluster_begin_idx >= 0
2217           && ctx->cluster_begin_idx < ctx->out->used)
2218         {
2219           int i;
2220
2221           if (MDEBUG_FLAG () > 2)
2222             MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos + 1);
2223           for (i = ctx->cluster_begin_idx; i < ctx->out->used; i++)
2224             {
2225               GREF (ctx->out, i)->from = ctx->cluster_begin_pos;
2226               GREF (ctx->out, i)->to = ctx->cluster_end_pos;
2227             }
2228           ctx->cluster_begin_idx = -1;
2229         }
2230       return from;
2231
2232     case CMD_ID_SEPARATOR:
2233       {
2234         int i;
2235
2236         i = from < to ? from : from - 1;
2237         GDUP (ctx, i);
2238         g = GREF (ctx->out, ctx->out->used - 1);
2239         g->c = -1, g->code = 0;
2240         g->xadv = g->yadv = 0;
2241         SET_ENCODED (g, 0);
2242         SET_MEASURED (g, 0);
2243         SET_CATEGORY_CODE (g, ' ');
2244         return from;
2245       }
2246
2247     case CMD_ID_LEFT_PADDING:
2248       if (MDEBUG_FLAG () > 2)
2249         MDEBUG_PRINT2 ("\n [FLT] %*s[", depth, "");
2250       ctx->left_padding = 1;
2251       return from;
2252
2253     case CMD_ID_RIGHT_PADDING:
2254       if (ctx->out->used > 0)
2255         {
2256           if (MDEBUG_FLAG () > 2)
2257             MDEBUG_PRINT2 ("\n [FLT] %*s]", depth, "");
2258           g = GREF (ctx->out, ctx->out->used - 1);
2259           SET_RIGHT_PADDING (g, ctx, RightPaddingMask);
2260         }
2261       return from;
2262     }
2263
2264   MERROR (MERROR_DRAW, -1);
2265 }
2266
2267 static int
2268 run_stages (MFLTGlyphString *gstring, int from, int to,
2269             MFLT *flt, FontLayoutContext *ctx)
2270 {
2271   MFLTGlyphString buf, *temp;
2272   int stage_idx = 0;
2273   int orig_from = from, orig_to = to;
2274   int from_pos, to_pos, len;
2275   int i, j;
2276   MFLTGlyph *g;
2277   MPlist *stages = flt->stages;
2278   FontLayoutCategory *prev_category = NULL;
2279
2280   from_pos = GREF (ctx->in, from)->from;
2281   to_pos = GREF (ctx->in, to - 1)->to;
2282   len = to_pos - from_pos;
2283
2284   buf = *(ctx->in);
2285   buf.glyphs = NULL;
2286   GINIT (ctx->out, ctx->out->allocated);
2287   ctx->encoded = alloca (ctx->out->allocated);
2288   if (! ctx->out->glyphs || ! ctx->encoded)
2289     return -1;
2290
2291   for (stage_idx = 0; 1; stage_idx++)
2292     {
2293       MCharTable *table;
2294       int result;
2295
2296       ctx->stage = (FontLayoutStage *) MPLIST_VAL (stages);
2297       table = ctx->stage->category->table;
2298       stages = MPLIST_NEXT (stages);
2299       if (MPLIST_TAIL_P (stages))
2300         ctx->category = NULL;
2301       else
2302         ctx->category = ((FontLayoutStage *) MPLIST_VAL (stages))->category;
2303       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
2304       ctx->encoded_offset = from;
2305       for (i = from; i < to; i++)
2306         {
2307           MFLTGlyph *g = GREF (ctx->in, i);
2308           char enc;
2309
2310           if (GET_COMBINED (g)
2311               || (prev_category && prev_category != ctx->stage->category))
2312             {
2313               enc = (GET_ENCODED (g)
2314                      ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
2315                      : g->code
2316                      ? (int) mchartable_lookup (table, g->code)
2317                      : ' ');
2318               if (! GET_COMBINED (g))
2319                 SET_CATEGORY_CODE (g, enc);
2320             }
2321           else
2322             enc = GET_CATEGORY_CODE (g);
2323           ctx->encoded[i - from] = enc;
2324           if (! enc && stage_idx == 0)
2325             {
2326               to = i;
2327               break;
2328             }
2329         }
2330       ctx->encoded[i - from] = '\0';
2331       ctx->match_indices[0] = from;
2332       ctx->match_indices[1] = to;
2333       for (i = 2; i < NMATCH; i++)
2334         ctx->match_indices[i] = -1;
2335
2336       if (MDEBUG_FLAG () > 2)
2337         {
2338           MDEBUG_PRINT2 ("\n [FLT]   (STAGE %d \"%s\"", stage_idx,
2339                          ctx->encoded);
2340           MDEBUG_PRINT (" (");
2341           for (i = from; i < to; i++)
2342             {
2343               g = GREF (ctx->in, i);
2344               if (g->c == -1)
2345                 MDEBUG_PRINT2 ("%*s|", (i > 0), "");
2346               else
2347                 MDEBUG_PRINT3 ("%*s%04X", (i > 0), "", GREF (ctx->in, i)->code);
2348             }
2349           MDEBUG_PRINT (")");
2350         }
2351       result = run_command (4, INDEX_TO_CMD_ID (0), from, to, ctx);
2352       if (MDEBUG_FLAG () > 2)
2353         MDEBUG_PRINT (")");
2354       if (result < 0)
2355         return result;
2356
2357       /* If this is the last stage, break the loop. */
2358       if (MPLIST_TAIL_P (stages))
2359         break;
2360
2361       /* Otherwise, prepare for the next stage.   */
2362       prev_category = ctx->stage->category;
2363       temp = ctx->in;
2364       ctx->in = ctx->out;
2365       if (buf.glyphs)
2366         ctx->out = temp;
2367       else
2368         {
2369           GINIT (&buf, ctx->out->allocated);
2370           ctx->out = &buf;
2371         }
2372       ctx->out->used = 0;
2373
2374       from = 0;
2375       to = ctx->in->used;
2376     }
2377
2378   if (ctx->out->used > 0)
2379     {
2380       int *g_indices;
2381       int x_ppem = ctx->font->x_ppem << 6, y_ppem = ctx->font->y_ppem << 6;
2382
2383       /* Remove separator glyphs.  */
2384       for (i = 0; i < ctx->out->used;)
2385         {
2386           g = GREF (ctx->out, i);
2387           if (g->c < 0)
2388             GREPLACE (NULL, 0, 0, ctx->out, i, i + 1);
2389           else
2390             i++;
2391         }
2392
2393       /* Get actual glyph IDs of glyphs.  */
2394       ctx->font->get_glyph_id (ctx->font, ctx->out, 0, ctx->out->used);
2395
2396       /* Check if all characters in the range are covered by some
2397          glyph(s).  If not, change <from> and <to> of glyphs to cover
2398          uncovered characters.  */
2399       g_indices = alloca (sizeof (int) * len);
2400       if (! g_indices)
2401         return -1;
2402       for (i = 0; i < len; i++) g_indices[i] = -1;
2403       for (i = 0; i < ctx->out->used; i++)
2404         {
2405           int pos;
2406
2407           g = GREF (ctx->out, i);
2408           for (pos = g->from; pos <= g->to; pos++)
2409             if (g_indices[pos - from_pos] < 0)
2410               g_indices[pos - from_pos] = i;
2411         }
2412       for (i = 0; i < len; i++)
2413         if (g_indices[i] < 0)
2414           {
2415             if (i == 0)
2416               {
2417                 int this_from;
2418
2419                 for (i++; i < len && g_indices[i] < 0; i++);
2420                 j = g_indices[i];
2421                 g = GREF (ctx->out, j);
2422                 this_from = g->from;
2423                 do {
2424                   g->from = orig_from + i;
2425                 } while (++j < ctx->out->used
2426                          && (g = GREF (ctx->out, j))
2427                          && g->from == this_from);
2428               }
2429             else
2430               {
2431                 int this_to;
2432
2433                 j = g_indices[i - 1];
2434                 g = GREF (ctx->out, j);
2435                 this_to = g->to;
2436                 do {
2437                   g->to = orig_from + i + 1;
2438                 } while (--j >= 0
2439                          && (g = GREF (ctx->out, j))
2440                          && g->to == this_to);
2441               }
2442           }
2443
2444       ctx->font->get_metrics (ctx->font, ctx->out, 0, ctx->out->used);
2445
2446       /* Handle combining.  */
2447       if (ctx->check_mask & CombinedMask)
2448         {
2449           MFLTGlyph *base = GREF (ctx->out, 0);
2450           int base_height = base->ascent + base->descent;
2451           int base_width = base->rbearing - base->lbearing;
2452           int combining_code;
2453
2454           for (i = 1; i < ctx->out->used; i++)
2455             {
2456               if ((g = GREF (ctx->out, i))
2457                   && GET_COMBINED (g)
2458                   && (combining_code = GET_COMBINING_CODE (g)))
2459                 {
2460                   int height = g->ascent + g->descent;
2461                   int width = g->rbearing - g->lbearing;
2462                   int base_x, base_y, add_x, add_y, off_x, off_y;
2463
2464                   if (base->from > g->from)
2465                     base->from = g->from;
2466                   else if (base->to < g->to)
2467                     base->to = g->to;
2468                 
2469                   base_x = COMBINING_CODE_BASE_X (combining_code);
2470                   base_y = COMBINING_CODE_BASE_Y (combining_code);
2471                   add_x = COMBINING_CODE_ADD_X (combining_code);
2472                   add_y = COMBINING_CODE_ADD_Y (combining_code);
2473                   off_x = COMBINING_CODE_OFF_X (combining_code);
2474                   off_y = COMBINING_CODE_OFF_Y (combining_code);
2475
2476                   g->xoff = ((base_width * base_x - width * add_x) / 2
2477                              + x_ppem * off_x / 100
2478                              - (base->xadv - base->lbearing) - g->lbearing);
2479                   if (base_y < 3)
2480                     g->yoff = base_height * base_y / 2 - base->ascent;
2481                   else
2482                     g->yoff = 0;
2483                   if (add_y < 3)
2484                     g->yoff -= height * add_y / 2 - g->ascent;
2485                   g->yoff -= y_ppem * off_y / 100;
2486                   if (base->lbearing > base->xadv + g->lbearing + g->xoff)
2487                     base->lbearing = base->xadv + g->lbearing + g->xoff;
2488                   if (base->rbearing < base->xadv + g->rbearing + g->xoff)
2489                     base->rbearing = base->xadv + g->rbearing + g->xoff;
2490                   if (base->ascent < g->ascent - g->yoff)
2491                     base->ascent = g->ascent - g->yoff;
2492                   if (base->descent < g->descent - g->yoff)
2493                     base->descent = g->descent - g->yoff;
2494                   g->xadv = g->yadv = 0;
2495                   if (GET_RIGHT_PADDING (g))
2496                     SET_RIGHT_PADDING (base, ctx, RightPaddingMask);
2497                   g->adjusted = 1;
2498                 }
2499               else
2500                 {
2501                   base = g;
2502                   base_height = g->ascent + g->descent;
2503                   base_width = g->rbearing - g->lbearing;
2504                 }
2505             }
2506         }
2507
2508       /* Handle padding */
2509       if (ctx->check_mask & (LeftPaddingMask | RightPaddingMask))
2510         for (i = 0; i < ctx->out->used; i++)
2511           {
2512             g = GREF (ctx->out, i);
2513             if (! GET_COMBINED (g))
2514               {
2515                 if (GET_RIGHT_PADDING (g) && g->rbearing > g->xadv)
2516                   {
2517                     g->xadv = g->rbearing;
2518                     g->adjusted = 1;
2519                   }
2520                 if (GET_LEFT_PADDING (g) && g->lbearing < 0)
2521                   {
2522                     g->xoff += - g->lbearing;
2523                     g->xadv += - g->lbearing;
2524                     g->rbearing += - g->lbearing;
2525                     g->lbearing = 0;
2526                     g->adjusted = 1;
2527                   }
2528               }
2529           }
2530     }
2531
2532   GREPLACE (ctx->out, 0, ctx->out->used, gstring, orig_from, orig_to);
2533   to = orig_from + ctx->out->used;
2534   return to;
2535 }
2536
2537 static void
2538 setup_combining_coverage (int from, int to, void *val, void *arg)
2539 {
2540   int combining_class = (int) val;
2541   int category = 0;
2542
2543   if (combining_class < 200)
2544     category = 'a';
2545   else if (combining_class <= 204)
2546     {
2547       if ((combining_class % 2) == 0)
2548         category = "bcd"[(combining_class - 200) / 2];
2549     }
2550   else if (combining_class <= 232)
2551     {
2552       if ((combining_class % 2) == 0)
2553         category = "efghijklmnopq"[(combining_class - 208) / 2];
2554     }
2555   else if (combining_class == 233)
2556     category = 'r';
2557   else if (combining_class == 234)
2558     category = 's';
2559   else if (combining_class == 240)
2560     category = 't';
2561   mchartable_set_range ((MCharTable *) arg, from, to, (void *) category);
2562 }
2563
2564 static void
2565 setup_combining_flt (MFLT *flt)
2566 {
2567   MSymbol type;
2568   MCharTable *combininig_class_table
2569     = mchar_get_prop_table (Mcombining_class, &type);
2570
2571   mchartable_set_range (flt->coverage->table, 0, 0x10FFFF, (void *) 'u');
2572   if (combininig_class_table)
2573     mchartable_map (combininig_class_table, (void *) 0,
2574                     setup_combining_coverage, flt->coverage->table);
2575 }
2576
2577 #define CHECK_FLT_STAGES(flt) ((flt)->stages || load_flt (flt, NULL) == 0)
2578
2579 static FontLayoutCategory *
2580 configure_category (FontLayoutCategory *category, MFLTFont *font)
2581 {
2582   if (! mflt_font_id || ! mflt_iterate_otf_feature)
2583     {
2584       FontLayoutCategory *new = malloc (sizeof (FontLayoutCategory));
2585       new->definition = NULL;
2586       new->table = category->table;
2587       M17N_OBJECT_REF (new->table);
2588       return new;
2589     }
2590   return load_category_table (category->definition, font);
2591 }
2592
2593 static MFLT *
2594 configure_flt (MFLT *flt, MFLTFont *font, MSymbol font_id)
2595 {
2596   MPlist *plist;
2597   MFLT *configured;
2598
2599   if (! mflt_font_id || ! mflt_iterate_otf_feature)
2600     return flt;
2601   MPLIST_DO (plist, flt_list)
2602     {
2603       configured = MPLIST_VAL (plist);
2604       if (! configured->font_id)
2605         break;
2606       if (configured->name == flt->name
2607           && configured->font_id == font_id)
2608         return configured;
2609     }
2610   if (! MSTRUCT_CALLOC_SAFE (configured))
2611     return flt;
2612   *configured = *flt;
2613   configured->stages = mplist_copy (flt->stages);
2614   MPLIST_DO (plist, configured->stages)
2615     {
2616       FontLayoutStage *stage = MPLIST_VAL (plist);
2617       if (stage->category->definition)
2618         {
2619           MSTRUCT_CALLOC (stage, MERROR_FLT);
2620           *stage = *((FontLayoutStage *) MPLIST_VAL (plist));
2621           stage->category = configure_category (stage->category, font);
2622           MPLIST_VAL (plist) = stage;
2623         }
2624       else
2625         M17N_OBJECT_REF (stage->category->table);
2626     }
2627   configured->need_config = 0;
2628   configured->font_id = font_id;
2629   mplist_push (flt_list, flt->name, configured);
2630   return configured;
2631 }
2632 \f
2633 /* Internal API */
2634
2635 int m17n__flt_initialized;
2636
2637 \f
2638 /* External API */
2639
2640 /* The following two are actually not exposed to a user but concealed
2641    by the macro M17N_INIT (). */
2642
2643 void
2644 m17n_init_flt (void)
2645 {
2646   int mdebug_flag = MDEBUG_INIT;
2647
2648   merror_code = MERROR_NONE;
2649   if (m17n__flt_initialized++)
2650     return;
2651   m17n_init_core ();
2652   if (merror_code != MERROR_NONE)
2653     {
2654       m17n__flt_initialized--;
2655       return;
2656     }
2657
2658   MDEBUG_PUSH_TIME ();
2659
2660   Mcond = msymbol ("cond");
2661   Mrange = msymbol ("range");
2662   Mfont = msymbol ("font");
2663   Mlayouter = msymbol ("layouter");
2664   Mcombining = msymbol ("combining");
2665   Mfont_facility = msymbol ("font-facility");
2666   Mequal = msymbol ("=");
2667   Mgenerator = msymbol ("generator");
2668   Mend = msymbol ("end");
2669
2670   mflt_enable_new_feature = 0;
2671   mflt_iterate_otf_feature = NULL;
2672   mflt_font_id = NULL;
2673   mflt_try_otf = NULL;
2674
2675   MDEBUG_PRINT_TIME ("INIT", (mdebug__output, " to initialize the flt modules."));
2676   MDEBUG_POP_TIME ();
2677 }
2678
2679 void
2680 m17n_fini_flt (void)
2681 {
2682   int mdebug_flag = MDEBUG_FINI;
2683
2684   if (m17n__flt_initialized == 0
2685       || --m17n__flt_initialized > 0)
2686     return;
2687
2688   MDEBUG_PUSH_TIME ();
2689   free_flt_list ();
2690   MDEBUG_PRINT_TIME ("FINI", (mdebug__output, " to finalize the flt modules."));
2691   MDEBUG_POP_TIME ();
2692   m17n_fini_core ();
2693 }
2694
2695 /*** @} */ 
2696 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
2697
2698 /*** @addtogroup m17nFLT */
2699 /*** @{ */
2700 /*=*/
2701
2702 /*=*/
2703 /***en
2704     @brief Return an FLT object that has a specified name.
2705
2706     The mflt_get () function returns an FLT object whose name is $NAME.
2707
2708     @return
2709     If the operation was successful, mflt_get () returns a pointer
2710     to the found FLT object.  Otherwise, it returns @c NULL.  */
2711
2712 /***ja
2713     @brief »ØÄꤵ¤ì¤¿Ì¾Á°¤ò»ý¤Ä FLT ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤¹.
2714
2715     ´Ø¿ô mflt_get () ¤Ï¡¢$NAME ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä FLT ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤¹¡£
2716
2717     @return
2718     ¤â¤·À®¸ù¤¹¤ì¤Ð¡¢mflt_get () ¤Ï¸«¤Ä¤«¤Ã¤¿ FLT
2719     ¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£  */
2720
2721 MFLT *
2722 mflt_get (MSymbol name)
2723 {
2724   MFLT *flt;
2725   MPlist *plist;
2726
2727   if (! flt_list && list_flt () < 0)
2728     return NULL;
2729   for (plist = flt_list; plist; plist = plist->next)
2730     if (((MFLT *) MPLIST_VAL (plist))->font_id == Mnil)
2731       break;
2732   flt = mplist_get (plist, name);
2733   if (! flt || ! CHECK_FLT_STAGES (flt))
2734     return NULL;
2735   if (flt->name == Mcombining
2736       && ! mchartable_lookup (flt->coverage->table, 0))
2737     setup_combining_flt (flt);
2738
2739   return flt;
2740 }
2741
2742 /*=*/
2743 /***en
2744     @brief Find an FLT suitable for the specified character and font.
2745
2746     The mflt_find () function returns the most appropriate FLT for
2747     layouting character $C with font $FONT.
2748
2749     @return
2750     If the operation was successful, mflt_find () returns a pointer
2751     to the found FLT object.  Otherwise, it returns @c NULL.  */
2752
2753 /***ja
2754     @brief »ØÄꤵ¤ì¤¿Ê¸»ú¤È¥Õ¥©¥ó¥È¤Ë¹ç¤Ã¤¿ FLT ¤òõ¤¹.
2755
2756     ´Ø¿ô mflt_find () ¤Ï¡¢Ê¸»ú $C ¤ò¥Õ¥©¥ó¥È $FONT
2757     ¤Ç¥ì¥¤¥¢¥¦¥È¤¹¤ë¤¿¤á¤ËºÇ¤âŬÀڤʠFLT ¤òÊÖ¤¹¡£
2758
2759     @return
2760     ¤â¤·À®¸ù¤¹¤ì¤Ð¡¢mflt_find () ¤Ï¸«¤Ä¤«¤Ã¤¿ FLT
2761     ¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£  */
2762
2763 MFLT *
2764 mflt_find (int c, MFLTFont *font)
2765 {
2766   MPlist *plist, *pl;
2767   MFLT *flt;
2768   static MSymbol unicode_bmp = NULL, unicode_full = NULL;
2769
2770   if (! unicode_bmp)
2771     {
2772       unicode_bmp = msymbol ("unicode-bmp");
2773       unicode_full = msymbol ("unicode-full");
2774     }
2775
2776   if (! flt_list && list_flt () < 0)
2777     return NULL;
2778   /* Skip configured FLTs.  */
2779   MPLIST_DO (plist, flt_list)
2780     if (((MFLT *) MPLIST_VAL (plist))->font_id == Mnil)
2781       break;
2782   if (font)
2783     {
2784       MFLT *best = NULL;
2785
2786       MPLIST_DO (pl, plist)
2787         {
2788           flt = MPLIST_VAL (pl);
2789           if (flt->registry != unicode_bmp
2790               && flt->registry != unicode_full)
2791             continue;
2792           if (flt->family && flt->family != font->family)
2793             continue;
2794           if (flt->name == Mcombining
2795               && ! mchartable_lookup (flt->coverage->table, 0))
2796             setup_combining_flt (flt);
2797           if (c >= 0
2798               && ! mchartable_lookup (flt->coverage->table, c))
2799             continue;
2800           if (flt->otf.sym)
2801             {
2802               MFLTOtfSpec *spec = &flt->otf;
2803
2804               if (! font->check_otf)
2805                 {
2806                   if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
2807                       || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
2808                     continue;
2809                 }
2810               else if (! font->check_otf (font, spec))
2811                 continue;
2812               goto found;
2813             }
2814           best = flt;
2815         }
2816       if (best == NULL)
2817         return NULL;
2818       flt = best;
2819       goto found;
2820     }
2821   if (c >= 0)
2822     {
2823       MPLIST_DO (pl, plist)
2824         {
2825           flt = MPLIST_VAL (pl);
2826           if (mchartable_lookup (flt->coverage->table, c))
2827             goto found;
2828         }
2829     }
2830   return NULL;
2831
2832  found:
2833   if (! CHECK_FLT_STAGES (flt))
2834     return NULL;
2835   if (font && flt->need_config && mflt_font_id)
2836     flt = configure_flt (flt, font, mflt_font_id (font));
2837   return flt;
2838 }
2839
2840 /*=*/
2841 /***en
2842     @brief Return the name of an FLT.
2843
2844     The mflt_name () function returns the name of $FLT.  */
2845
2846 /***ja
2847     @brief FLT ¤Î̾Á°¤òÊÖ¤¹.
2848
2849     ´Ø¿ô mflt_name () ¤Ï $FLT ¤Î̾Á°¤òÊÖ¤¹¡£  */
2850
2851 const char *
2852 mflt_name (MFLT *flt)
2853 {
2854   return MSYMBOL_NAME (flt->name);
2855 }
2856
2857 /*=*/
2858 /***en
2859     @brief Return a coverage of a FLT.
2860
2861     The mflt_coverage () function returns a char-table that contains
2862     nonzero values for characters supported by $FLT.  */
2863
2864 /***ja
2865     @brief FLT ¤ÎÈϰϤòÊÖ¤¹.
2866
2867     ´Ø¿ô mflt_coverage () ¤Ï¡¢$FLT ¤¬¥µ¥Ý¡¼¥È¤¹¤ëʸ»ú¤ËÂФ·¤Æ
2868     0 ¤Ç¤Ê¤¤Ãͤò´Þ¤àʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£  */
2869
2870 MCharTable *
2871 mflt_coverage (MFLT *flt)
2872 {
2873   return flt->coverage->table;
2874 }
2875
2876 /*=*/
2877 /***en
2878     @brief Layout characters with an FLT.
2879
2880     The mflt_run () function layouts characters in $GSTRING between
2881     $FROM (inclusive) and $TO (exclusive) with $FONT.  If $FLT is
2882     nonzero, it is used for all the charaters.  Otherwise, appropriate
2883     FLTs are automatically chosen.
2884
2885     @retval >=0
2886     The operation was successful.  The value is the index to the
2887     glyph, which was previously indexed by $TO, in $GSTRING->glyphs.
2888
2889     @retval -2
2890     $GSTRING->glyphs is too short to store the result.  The caller can
2891     call this fucntion again with a longer $GSTRING->glyphs.
2892
2893     @retval -1
2894     Some other error occurred.  */
2895
2896 /***ja
2897     @brief FLT ¤ò»È¤Ã¤Æʸ»ú¤ò¥ì¥¤¥¢¥¦¥È¤¹¤ë.
2898
2899     ´Ø¿ô mflt_run () ¤Ï¡¢$GSTRING Ãæ¤Î $FROM ¤«¤é $TO Ä¾Á°¤Þ¤Ç¤Îʸ»ú¤ò
2900     $FONT ¤òÍѤ¤¤Æ¥ì¥¤¥¢¥¦¥È¤¹¤ë¡£¤â¤· $FLT
2901     ¤¬¥¼¥í¤Ç¤Ê¤±¤ì¤Ð¡¢¤½¤ÎÃͤò¤¹¤Ù¤Æ¤Îʸ»ú¤ËÂФ·¤ÆÍѤ¤¤ë¡£
2902     ¤½¤¦¤Ç¤Ê¤±¤ì¤ÐŬÀڤʠFLT ¤ò¼«Æ°Åª¤ËÁªÂò¤¹¤ë¡£
2903
2904     @retval >=0
2905     ¼Â¹ÔÀ®¸ù¤ò¼¨¤¹¡£ÊÖ¤µ¤ì¤ëÃͤϡ¢$GSTRING->glyphs Ãæ¤Ç°ÊÁ° $TO
2906     ¤Ë¤è¤Ã¤Æ¼¨¤µ¤ì¤Æ¤¤¤¿¥°¥ê¥Õ¤Ø¤Î¥¤¥ó¥Ç¥¯¥¹¤Ç¤¢¤ë¡£
2907
2908     @retval -2
2909     ·ë²Ì¤ò³ÊǼ¤¹¤ë¤Ë¤Ï $GSTRING->glyphs ¤¬Ã»¤¹¤®¤ë¤³¤È¤ò¼¨¤¹¡£
2910     ¸Æ¤Ó½Ð¤·Â¦¤Ï¡¢¤è¤êŤ¤ $GSTRING->glyphs
2911     ¤òÍѤ¤¤ÆºÆÅÙ¤³¤Î´Ø¿ô¤ò¸Æ¤Ö¤³¤È¤¬¤Ç¤­¤ë¡£
2912
2913     @retval -1
2914     ¤½¤Î¾¤Î¥¨¥é¡¼¤¬µ¯¤­¤¿¤³¤È¤ò¼¨¤¹¡£  */
2915
2916 int
2917 mflt_run (MFLTGlyphString *gstring, int from, int to,
2918           MFLTFont *font, MFLT *flt)
2919 {
2920   FontLayoutContext ctx;
2921   int match_indices[NMATCH];
2922   MFLTGlyph *g;
2923   MFLTGlyphString out;
2924   int auto_flt = ! flt;
2925   int c, i, j, k;
2926   int this_from, this_to;
2927   MSymbol font_id = mflt_font_id ? mflt_font_id (font) : Mnil;
2928
2929   out = *gstring;
2930   out.glyphs = NULL;
2931   /* This is usually sufficient, but if not, we retry with the larger
2932      values at most 3 times.  This value is also used for the
2933      allocating size of ctx.encoded.  */
2934   out.allocated = (to - from) * 4;
2935
2936   for (i = from; i < to; i++)
2937     {
2938       g = GREF (gstring, i);
2939       if (! g->encoded)
2940         {
2941           c = g->c;
2942           memset (g, 0, sizeof (MFLTGlyph));
2943           g->code = g->c = c;
2944         }
2945       g->from = g->to = i;
2946     }
2947
2948   for (this_from = from; this_from < to;)
2949     {
2950       if (! auto_flt)
2951         {
2952           for (this_to = this_from; this_to < to; this_to++)
2953             if (mchartable_lookup (flt->coverage->table,
2954                                    GREF (gstring, this_to)->c))
2955               break;
2956         }
2957       else
2958         {
2959           if (! flt_list && list_flt () < 0)
2960             {
2961               font->get_glyph_id (font, gstring, this_from, to);
2962               font->get_metrics (font, gstring, this_from, to);
2963               this_from = to;
2964               break;
2965             }
2966           for (this_to = this_from; this_to < to; this_to++)
2967             {
2968               c = GREF (gstring, this_to)->c;
2969               if (c >= flt_min_coverage && c <= flt_max_coverage)
2970                 break;
2971             }
2972           for (; this_to < to; this_to++)
2973             {
2974               c = GREF (gstring, this_to)->c;
2975               if (font->internal
2976                   && mchartable_lookup (((MFLT *) font->internal)->coverage->table, c))
2977                 {
2978                   flt = font->internal;
2979                   break;
2980                 }
2981               flt = mflt_find (c, font);
2982               if (flt)
2983                 {
2984                   if (CHECK_FLT_STAGES (flt))
2985                     {
2986                       font->internal = flt;
2987                       break;
2988                     }
2989                 }
2990             }
2991         }
2992
2993       if (this_from < this_to)
2994         {
2995           font->get_glyph_id (font, gstring, this_from, this_to);
2996           font->get_metrics (font, gstring, this_from, this_to);
2997           this_from = this_to;
2998         }
2999       if (this_to == to)
3000         break;
3001
3002       MDEBUG_PRINT1 (" [FLT] (%s", MSYMBOL_NAME (flt->name));
3003
3004       if (flt->need_config && font_id != Mnil)
3005         flt = configure_flt (flt, font, font_id);
3006
3007       for (; this_to < to; this_to++)
3008         {
3009           char enc;
3010           g = GREF (gstring, this_to);
3011           enc = (int) mchartable_lookup (flt->coverage->table, g->c);
3012           if (! enc)
3013             break;
3014           SET_CATEGORY_CODE (g, enc);
3015         }
3016
3017       if (MDEBUG_FLAG ())
3018         {
3019           if (font->family)
3020             MDEBUG_PRINT1 (" (%s)", MSYMBOL_NAME (font->family));
3021           MDEBUG_PRINT ("\n [FLT]   (SOURCE");
3022           for (i = this_from, j = 0; i < this_to; i++, j++)
3023             {
3024               if (j > 0 && j % 8 == 0)
3025                 MDEBUG_PRINT ("\n [FLT]          ");
3026               MDEBUG_PRINT1 (" %04X", GREF (gstring, i)->c);
3027             }
3028           MDEBUG_PRINT (")");
3029         }
3030
3031       for (i = 0; i < 3; i++)
3032         {
3033           /* Setup CTX.  */
3034           memset (&ctx, 0, sizeof ctx);
3035           ctx.match_indices = match_indices;
3036           ctx.font = font;
3037           ctx.cluster_begin_idx = -1;
3038           ctx.in = gstring;
3039           ctx.out = &out;
3040           j = run_stages (gstring, this_from, this_to, flt, &ctx);
3041           if (j != -2)
3042             break;
3043           out.allocated *= 2;
3044         }
3045
3046       if (j < 0)
3047         return j;
3048
3049       to += j - this_to;
3050       this_to = j;
3051
3052       if (MDEBUG_FLAG ())
3053         {
3054           MDEBUG_PRINT ("\n [FLT]   (RESULT");
3055           if (MDEBUG_FLAG () > 1)
3056             for (i = 0; this_from < this_to; this_from++, i++)
3057               {
3058                 if (i > 0 && i % 4 == 0)
3059                   MDEBUG_PRINT ("\n [FLT]          ");
3060                 g = GREF (gstring, this_from);
3061                 MDEBUG_PRINT4 (" (%04X %d %d %d)",
3062                                g->code, g->xadv, g->xoff, g->yoff);
3063               }
3064           else
3065             for (; this_from < this_to; this_from++)
3066               MDEBUG_PRINT1 (" %04X", GREF (gstring, this_from)->code);
3067           MDEBUG_PRINT ("))\n");
3068         }
3069       this_from = this_to;
3070     }
3071
3072   if (gstring->r2l)
3073     {
3074       int len = to - from;
3075
3076       GINIT (&out, len);
3077       memcpy (((char *) out.glyphs),
3078               ((char *) gstring->glyphs) + gstring->glyph_size * from,
3079               gstring->glyph_size * len);
3080       for (i = from, j = to; i < to;)
3081         {
3082           for (k = i + 1, j--; k < to && GREF (&out, k)->xadv == 0;
3083                k++, j--);
3084           GCPY (&out, i, (k - i), gstring, j);
3085           i = k;
3086         }
3087     }
3088
3089   return to;
3090 }
3091
3092 int mflt_enable_new_feature;
3093
3094 int (*mflt_iterate_otf_feature) (struct _MFLTFont *font,
3095                                  MFLTOtfSpec *spec,
3096                                  int from, int to,
3097                                  unsigned char *table);
3098
3099 MSymbol (*mflt_font_id) (struct _MFLTFont *font);
3100
3101 int (*mflt_try_otf) (struct _MFLTFont *font, MFLTOtfSpec *spec,
3102                      MFLTGlyphString *gstring, int from, int to);
3103
3104 \f
3105 /* for debugging... */
3106
3107 static void
3108 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
3109 {
3110   char *prefix = (char *) alloca (indent + 1);
3111
3112   memset (prefix, 32, indent);
3113   prefix[indent] = 0;
3114
3115   if (id >= 0)
3116     fprintf (mdebug__output, "0x%02X", id);
3117   else if (id <= CMD_ID_OFFSET_INDEX)
3118     {
3119       int idx = CMD_ID_TO_INDEX (id);
3120       FontLayoutCmd *cmd = stage->cmds + idx;
3121
3122       if (cmd->type == FontLayoutCmdTypeRule)
3123         {
3124           FontLayoutCmdRule *rule = &cmd->body.rule;
3125           int i;
3126
3127           fprintf (mdebug__output, "(rule ");
3128           if (rule->src_type == SRC_REGEX)
3129             fprintf (mdebug__output, "\"%s\"", rule->src.re.pattern);
3130           else if (rule->src_type == SRC_INDEX)
3131             fprintf (mdebug__output, "%d", rule->src.match_idx);
3132           else if (rule->src_type == SRC_SEQ)
3133             fprintf (mdebug__output, "(seq)");
3134           else if (rule->src_type == SRC_RANGE)
3135             fprintf (mdebug__output, "(range)");
3136           else
3137             fprintf (mdebug__output, "(invalid src)");
3138
3139           for (i = 0; i < rule->n_cmds; i++)
3140             {
3141               fprintf (mdebug__output, "\n%s  ", prefix);
3142               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
3143             }
3144           fprintf (mdebug__output, ")");
3145         }
3146       else if (cmd->type == FontLayoutCmdTypeCond)
3147         {
3148           FontLayoutCmdCond *cond = &cmd->body.cond;
3149           int i;
3150
3151           fprintf (mdebug__output, "(cond");
3152           for (i = 0; i < cond->n_cmds; i++)
3153             {
3154               fprintf (mdebug__output, "\n%s  ", prefix);
3155               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
3156             }
3157           fprintf (mdebug__output, ")");
3158         }
3159       else if (cmd->type == FontLayoutCmdTypeOTF)
3160         {
3161           fprintf (mdebug__output, "(otf)");
3162         }
3163       else
3164         fprintf (mdebug__output, "(error-command)");
3165     }
3166   else if (id <= CMD_ID_OFFSET_COMBINING)
3167     fprintf (mdebug__output, "cominging-code");
3168   else
3169     fprintf (mdebug__output, "(predefiend %d)", id);
3170 }
3171
3172 /***en
3173     @brief Dump a Font Layout Table.
3174
3175     The mdebug_dump_flt () function prints the Font Layout Table $FLT
3176     in a human readable way to the stderr or to what specified by the
3177     environment variable MDEBUG_OUTPUT_FILE.  $INDENT specifies how
3178     many columns to indent the lines but the first one.
3179
3180     @return
3181     This function returns $FLT.  */
3182
3183 MFLT *
3184 mdebug_dump_flt (MFLT *flt, int indent)
3185 {
3186   char *prefix = (char *) alloca (indent + 1);
3187   MPlist *plist;
3188   int stage_idx = 0;
3189
3190   memset (prefix, 32, indent);
3191   prefix[indent] = 0;
3192   fprintf (mdebug__output, "(flt");
3193   MPLIST_DO (plist, flt->stages)
3194     {
3195       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
3196       int i;
3197
3198       fprintf (mdebug__output, "\n%s  (stage %d", prefix, stage_idx);
3199       for (i = 0; i < stage->used; i++)
3200         {
3201           fprintf (mdebug__output, "\n%s    ", prefix);
3202           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
3203         }
3204       fprintf (mdebug__output, ")");
3205       stage_idx++;
3206     }
3207   fprintf (mdebug__output, ")");
3208   return flt;
3209 }
3210
3211 void
3212 mflt_dump_gstring (MFLTGlyphString *gstring)
3213 {
3214   int i;
3215
3216   fprintf (mdebug__output, "(flt-gstring");
3217   for (i = 0; i < gstring->used; i++)
3218     {
3219       MFLTGlyph *g = GREF (gstring, i);
3220       fprintf (mdebug__output, "\n  (%02d pos:%d-%d c:%04X code:%04X cat:%c)",
3221                i, g->from, g->to, g->c, g->code, GET_CATEGORY_CODE (g));
3222     }
3223   fprintf (mdebug__output, ")\n");
3224 }
3225
3226 /*** @} */
3227
3228 /*
3229  Local Variables:
3230  coding: euc-japan
3231  End:
3232 */