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