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