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