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