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