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