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