f18f0a5c7a6eed691a90f27c3bbbac71650986fe
[m17n/m17n-lib.git] / src / m17n-flt.c
1 /* m17n-flt.c -- Font Layout Table sub-module.
2    Copyright (C) 2003, 2004, 2007
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).  */
29
30 /*=*/
31
32 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
33 /*** @addtogroup m17nInternal
34      @{ */
35
36 #include "config.h"
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <sys/types.h>
43 #include <regex.h>
44
45 #include "m17n-core.h"
46 #include "m17n-flt.h"
47 #include "m17n-misc.h"
48 #include "internal.h"
49 #include "mtext.h"
50 #include "symbol.h"
51 #include "plist.h"
52 #include "database.h"
53 #include "internal-flt.h"
54
55 /* Font Layouter */
56
57 /* Font Layout Table (FLT)
58
59 Predefined terms: SYMBOL, INTEGER, STRING
60
61 FLT ::= '(' STAGE + ')'
62
63 STAGE ::= CATEGORY-TABLE ? FONT-LAYOUT-RULE
64
65 ;; Each STAGE consumes a source (code sequence) and produces another
66 ;; code sequence that is given to the next STAGE as a source.  The
67 ;; source given to the first stage is a sequence of character codes
68 ;; that are assigned category codes by CATEGORY-TABLE.  The output of
69 ;; the last stage is a glyph code sequence given to the renderer.
70
71 CATEGORY-TABLE ::=
72         '(' 'category' CATEGORY-SPEC + ')'
73 CATEGORY-SPEC ::=
74         '(' CODE [ CODE ] CATEGORY ')'
75 CODE ::= INTEGER
76 CATEGORY ::= INTEGER
77 ;; ASCII character codes of alphabet ('A' .. 'Z' 'a' .. 'z').
78 ;; Ex: CATEGORY-TABLE
79 ;; (category
80 ;;   (0x0900 0x097F     ?E)     ; All Devanagari characters
81 ;;   (0x093C            ?N))    ; DEVANAGARI-LETTER NUKTA
82 ;;      Assign the category 'E' to all Devanagari characters but 0x093C,
83 ;;      assign the category 'N' to 0x093C.
84
85 FONT-LAYOUT-RULE ::=
86         '(' 'generator' RULE MACRO-DEF * ')'
87
88 RULE ::= COMMAND | REGEXP-RULE | MATCH-RULE | MAP-RULE
89          | COND-STRUCT | MACRO-NAME
90
91 COMMAND ::=
92         DIRECT-CODE | COMBINING | PREDEFIND-COMMAND | OTF-COMMAND
93
94 DIRECT-CODE ::= INTEGER
95 ;; Always succeed.  Produce the code.  Consume no source.
96
97 PREDEFIND-COMMAND ::=
98         '=' | '*' | '<' | '>' | '|'
99
100 ;; '=': Succeed when the current run contains at least one code.
101 ;; Consume the first code in the current run, and produce it as is.
102
103 ;; '*': If the the previous command succeeded, repeat it until it
104 ;; fails.  
105
106 ;; '<': Produce a special code that indicates the start of grapheme
107 ;; cluster.  Succeed always, consume nothing.
108
109 ;; '>': Produce a special code that indicates the end of grapheme
110 ;; cluster.  Succeed always, consume nothing.
111
112 ;; '|': Produce a special code whose category is ' '.  Succeed always,
113 ;; consume nothing.
114
115 OTF-COMMAND ::=
116         'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]]
117 ;; Run the Open Type Layout Table on the current run.  Succeed always,
118 ;; consume nothing.
119
120 SCRIPT ::= OTF-TAG
121 ;;      OTF's ScriptTag name (four letters) listed at:
122 ;;      <http://www.microsoft.om/typograph/otspec/scripttags.htm>
123 LANGSYS ::= OTF-TAG
124 ;;      OTF's Language System name (four letters) listed at:
125 ;;      <http://www.microsoft.om/typograph/otspec/languagetags.htm>
126
127 GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
128 GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
129 FEATURE ::= OTF-TAG
130 ;;      OTF's Feature name (four letters) listed at:
131 ;;      <http://www.microsoft.om/typograph/otspec/???.htm>
132
133 OTF-TAG ::= PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR
134
135 ;; Ex. OTF-COMMAND
136 ;; 'otf:deva'
137 ;;      Run all features in the default langsys of 'deva' script.
138 ;; 'otf:deva::nukt:haln'
139 ;;      Run all GSUB features, run 'nukt' and 'haln' GPOS features.
140 ;; 'otf:deva:: :'
141 ;;      Run all GSUB features, run no GPOS features.
142
143 REGEXP-RULE ::=
144         '(' REGEXP RULE * ')'
145
146 ;; Succeed if REGXP matches the head of source.  Run RULEs while
147 ;; limiting the source to the matching part.  Consume that part.
148
149 REGEXP ::= STRING
150 ;; Must be composed only from ASCII characters.  'A' - 'Z', 'a' - 'z'
151 ;; correspond to CATEGORY.
152
153 ;; Ex: REGEXP-RULE
154 ;; ("VA?"
155 ;;   < | vowel * | >)
156
157 MATCH-RULE ::=
158         '(' MATCH-IDX RULE * ')'
159
160 ;; Succeed if the previous REGEXP-RULE found a matching part for
161 ;; MATCH-IDX.  Run RULEs while limiting the source to the matching
162 ;; part.  If MATCH-IDX is zero, consume the whole part, else consume
163 ;; nothing.
164
165 MATCH-IDX ::= INTEGER
166 ;; Must be 0..20.
167
168 ;; Ex. MATCH-RULE
169 ;; (2 consonant *)
170
171 MAP-RULE ::=
172         '(' ( SOURCE-SEQ | SOURCE-RANGE ) RULE * ')'
173
174 ;; Succeed if the source matches SOURCE-SEQ or SOURCE-RANGE.  Run
175 ;; RULEs while limiting the source to the matching part.  Consume that
176 ;; part.
177
178 SOURCE-SEQ ::=
179         '(' CODE + ')'
180 SOURCE-RANGE ::=
181         '(' 'range' CODE CODE ')'
182 ;; Ex. MAP-RULE
183 ;; ((0x0915 0x094D)             0x43)
184 ;;      If the source code sequence is 0x0915 0x094D, produce 0x43.
185 ;; ((range 0x0F40 0x0F6A)       0x2221)
186 ;;      If the first source code CODE is in the range 0x0F40..0x0F6A, 
187 ;;      produce (0x2221 + (CODE - 0x0F40)).
188
189 COND-STRUCT ::=
190         '(' 'cond' RULE + ')'
191
192 ;; Try each rule in sequence until one succeeds.  Succeed if one
193 ;; succeeds.  Consume nothing.
194
195 ;; Ex. COND-STRUCT
196 ;; (cond
197 ;;  ((0x0915 0x094D)            0x43)
198 ;;  ((range 0x0F40 0x0F6A)      0x2221)
199 ;;  = )
200
201 COMBINING ::= 'V''H''O''V''H'
202 V ::= ( 't' | 'c' | 'b' | 'B' )
203 H ::= ( 'l' | 'c' | 'r' )
204 O ::= ( '.' | XOFF | YOFF | XOFF YOFF )
205 XOFF ::= '<'INTEGER | '>'INTEGER 
206 YOFF ::= '+'INTEGER | '-'INTEGER
207 ;; INTEGER must be integer 0..127
208
209 ;; VH pair indicates 12 reference points of a glyph as below:
210 ;;
211 ;;   0----1----2 <---- ascent    0:tl (top-left)
212 ;;   |         |                 1:tc (top-center)
213 ;;   |         |                 2:tr (top-right)
214 ;;   |         |                 3:Bl (base-left)
215 ;;   9   10   11 <---- center    4:Bc (base-center)
216 ;;   |         |                 5:Br (base-right)
217 ;; --3----4----5-- <-- baseline  6:bl (bottom-left)
218 ;;   |         |                 7:bc (bottom-center)
219 ;;   6----7----8 <---- descent   8:br (bottom-right)
220 ;;                               9:cl (center-left)
221 ;;   |    |    |                10:cc (center-center)
222 ;; left center right            11:cr (center-right)
223 ;;
224 ;; Ex. COMBINING
225 ;; 'tc.bc':
226 ;;      Align top-left point of the previous glyph and bottom-center
227 ;;      point of the current glyph.
228 ;; 'Bl<20-10Br'
229 ;;      Align 20% left and 10% below of base-left point of the previous
230 ;;      glyph and base-right point of the current glyph.
231
232 MACRO-DEF ::=
233         '(' MACRO-NAME RULE + ')'
234 MACRO-NAME ::= SYMBOL
235
236 */
237
238 static int mdebug_flag = MDEBUG_FONT_FLT;
239
240 MSymbol Mfont, Mlayouter, Mcombining;
241
242 static MSymbol Mgenerator, Mend;
243
244 static MPlist *flt_list;
245 static int flt_min_coverage, flt_max_coverage;
246
247 enum GlyphInfoMask
248 {
249   CombiningCodeMask = 0xFFFFFFF,
250   LeftPaddingMask = 1 << 28,
251   RightPaddingMask = 1 << 29
252 };
253
254 #define SET_GLYPH_INFO(g, mask, ctx, info)                      \
255   ((g)->internal = (((g)->internal & ~(mask)) | (info)),        \
256    (ctx)->check_mask |= (mask))
257
258 #define GET_COMBINING_CODE(g) ((g)->internal & CombiningCodeMask)
259 #define SET_COMBINING_CODE(g, ctx, code)        \
260   SET_GLYPH_INFO (g, CombiningCodeMask, ctx, code)
261 #define GET_LEFT_PADDING(g) ((g)->internal & LeftPaddingMask)
262 #define SET_LEFT_PADDING(g, ctx, flag)  \
263   SET_GLYPH_INFO (g, LeftPaddingMask, ctx, flag)
264 #define GET_RIGHT_PADDING(g) ((g)->internal & RightPaddingMask)
265 #define SET_RIGHT_PADDING(g, ctx, flag) \
266   SET_GLYPH_INFO (g, RightPaddingMask, ctx, flag)
267 #define GET_ENCODED(g) ((g)->encoded)
268 #define SET_ENCODED(g, flag) ((g)->encoded = (flag))
269 #define GET_MEASURED(g) ((g)->measured)
270 #define SET_MEASURED(g, flag) ((g)->measured = (flag))
271
272 #define GINIT(gstring, n)                                       \
273   do {                                                          \
274     if (! (gstring)->glyph_size)                                \
275       (gstring)->glyph_size = sizeof (MFLTGlyph);               \
276     (gstring)->glyphs = alloca ((gstring)->glyph_size * (n));   \
277     (gstring)->allocated = (n);                                 \
278     (gstring)->used = 0;                                        \
279   } while (0)
280
281 #define GALLOCA (gstring)       \
282   ((MFLTGlyph *) alloca ((gstring)->glyph_size))
283
284 #define GREF(gstring, idx)      \
285   ((MFLTGlyph *) ((char *) ((gstring)->glyphs) + (gstring)->glyph_size * (idx)))
286
287 #define PREV(gstring, g)        \
288   ((MFLTGlyph *) ((char *) (g) - (gstring)->glyph_size))
289
290 #define NEXT(gstring, g)        \
291   ((MFLTGlyph *) ((char *) (g) + (gstring)->glyph_size))
292
293 #define GCPY(src, src_idx, n, tgt, tgt_idx)                             \
294   do {                                                                  \
295     memcpy ((char *) ((tgt)->glyphs) + (tgt)->glyph_size * (tgt_idx),   \
296             (char *) ((src)->glyphs) + (src)->glyph_size * (src_idx),   \
297             (src)->glyph_size * (n));                                   \
298   } while (0)
299
300 #define GDUP(ctx, idx)                          \
301   do {                                          \
302     MFLTGlyphString *src = (ctx)->in;           \
303     MFLTGlyphString *tgt = (ctx)->out;          \
304     if (tgt->allocated <= tgt->used)            \
305       return -2;                                \
306     GCPY (src, (idx), 1, tgt, tgt->used);       \
307     tgt->used++;                                \
308   } while (0)
309
310 static int
311 GREPLACE (MFLTGlyphString *src, int src_from, int src_to,
312           MFLTGlyphString *tgt, int tgt_from, int tgt_to)
313 {
314   int src_len = src_to - src_from;
315   int tgt_len = tgt_to - tgt_from;
316   int inc = src_len - tgt_len;
317
318   if (tgt->allocated < tgt->used + inc)
319     return -2;
320   if (inc != 0 && tgt_to < tgt->used)
321     memmove ((char *) tgt->glyphs + tgt->glyph_size * (tgt_from + src_len),
322              (char *) tgt->glyphs + tgt->glyph_size * tgt_to,
323              tgt->glyph_size * (tgt->used - tgt_to));
324   if (src_len)
325     memcpy ((char *) tgt->glyphs + tgt->glyph_size * tgt_from,
326             (char *) src->glyphs + src->glyph_size * src_from,
327             src->glyph_size * src_len);
328   tgt->used += inc;
329   return 0;
330 }
331
332
333 /* Command ID:
334                  0 ...          : direct code
335                    -1           : invalid
336              -0x0F .. -2        : builtin commands
337         -0x100000F .. -0x10     : combining code
338                   ... -0x1000010: index to FontLayoutStage->cmds
339  */
340
341 #define INVALID_CMD_ID -1
342 #define CMD_ID_OFFSET_BUILTIN   -3
343 #define CMD_ID_OFFSET_COMBINING -0x10
344 #define CMD_ID_OFFSET_INDEX     -0x1000010
345
346 /* Builtin commands. */
347 #define CMD_ID_COPY             -3 /* '=' */
348 #define CMD_ID_REPEAT           -4 /* '*' */
349 #define CMD_ID_CLUSTER_BEGIN    -5 /* '<' */
350 #define CMD_ID_CLUSTER_END      -6 /* '>' */
351 #define CMD_ID_SEPARATOR        -7 /* '|' */
352 #define CMD_ID_LEFT_PADDING     -8 /* '[' */
353 #define CMD_ID_RIGHT_PADDING    -9 /* ']' */
354
355 #define CMD_ID_TO_COMBINING_CODE(id) (CMD_ID_OFFSET_COMBINING - (id))
356 #define COMBINING_CODE_TO_CMD_ID(code) (CMD_ID_OFFSET_COMBINING - (code))
357
358 #define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id))
359 #define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx))
360
361 static MSymbol Mcond, Mrange, Mfont_facility;
362
363 #define GLYPH_CODE_P(code)      \
364   ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX)
365
366 #define GLYPH_CODE_INDEX(code) ((code) - GLYPH_CODE_MIN)
367
368 #define UPDATE_CLUSTER_RANGE(ctx, g)            \
369   do {                                          \
370     if ((ctx)->cluster_begin_idx >= 0)          \
371       {                                         \
372         if (ctx->cluster_begin_pos > (g)->from) \
373           ctx->cluster_begin_pos = (g)->from;   \
374         if (ctx->cluster_end_pos < (g)->to)     \
375           ctx->cluster_end_pos = (g)->to;       \
376       }                                         \
377   } while (0)
378
379 enum FontLayoutCmdRuleSrcType
380   {
381     SRC_REGEX,
382     SRC_INDEX,
383     SRC_SEQ,
384     SRC_RANGE,
385     SRC_HAS_GLYPH,
386     SRC_OTF_SPEC
387   };
388
389 typedef struct
390 {
391   enum FontLayoutCmdRuleSrcType src_type;
392   union {
393     struct {
394       char *pattern;
395       regex_t preg;
396     } re;
397     int match_idx;
398     struct {
399       int n_codes;
400       int *codes;
401     } seq;
402     struct {
403       int from, to;
404     } range;
405     int supported_glyph;
406     MFLTOtfSpec otf_spec;
407   } src;
408
409   int n_cmds;
410   int *cmd_ids;
411 } FontLayoutCmdRule;
412
413 typedef struct
414 {
415   /* Beginning and end indices of series of SEQ commands.  */
416   int seq_beg, seq_end;
417   /* Range of the first character appears in the above series.  */
418   int seq_from, seq_to;
419
420   int n_cmds;
421   int *cmd_ids;
422 } FontLayoutCmdCond;
423
424 enum FontLayoutCmdType
425   {
426     FontLayoutCmdTypeRule,
427     FontLayoutCmdTypeCond,
428     FontLayoutCmdTypeOTF,
429     FontLayoutCmdTypeMAX
430   };
431
432 typedef struct
433 {
434   enum FontLayoutCmdType type;
435   union {
436     FontLayoutCmdRule rule;
437     FontLayoutCmdCond cond;
438     MFLTOtfSpec otf;
439   } body;
440 } FontLayoutCmd;
441
442 typedef struct 
443 {
444   MCharTable *category;
445   int size, inc, used;
446   FontLayoutCmd *cmds;
447 } FontLayoutStage;
448
449 struct _MFLT
450 {
451   MSymbol name;
452   MSymbol family;
453   MSymbol registry;
454   MFLTOtfSpec otf;
455   MDatabase *mdb;
456   MCharTable *coverage;
457   MPlist *stages;
458 };
459
460 /* Font layout table loader */
461
462 static int parse_otf_command (MSymbol symbol, MFLTOtfSpec *spec);
463
464 /* Load a category table from PLIST.  PLIST has this form:
465       PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
466 */
467
468 static MCharTable *
469 load_category_table (MPlist *plist)
470 {
471   MCharTable *table;
472
473   table = mchartable (Minteger, (void *) 0);
474
475   MPLIST_DO (plist, plist)
476     {
477       MPlist *elt;
478       int from, to, category_code;
479
480       if (! MPLIST_PLIST (plist))
481         MERROR (MERROR_FONT, NULL);
482       elt = MPLIST_PLIST (plist);
483       if (! MPLIST_INTEGER_P (elt))
484         MERROR (MERROR_FONT, NULL);
485       from = MPLIST_INTEGER (elt);
486       elt = MPLIST_NEXT (elt);
487       if (! MPLIST_INTEGER_P (elt))
488         MERROR (MERROR_FONT, NULL);
489       to = MPLIST_INTEGER (elt);
490       elt = MPLIST_NEXT (elt);
491       if (MPLIST_TAIL_P (elt))
492         {
493           category_code = to;
494           to = from;
495         }
496       else
497         {
498           if (! MPLIST_INTEGER_P (elt))
499             MERROR (MERROR_FONT, NULL);
500           category_code = MPLIST_INTEGER (elt);
501         }
502       if (! isalnum (category_code))
503         MERROR (MERROR_FONT, NULL);
504
505       if (from == to)
506         mchartable_set (table, from, (void *) category_code);
507       else
508         mchartable_set_range (table, from, to, (void *) category_code);
509     }
510
511   return table;
512 }
513
514 static unsigned int
515 gen_otf_tag (char *p)
516 {
517   unsigned int tag = 0;
518   int i;
519
520   for (i = 0; i < 4 && *p; i++, p++)
521     tag = (tag << 8) | *p;
522   for (; i < 4; i++)
523     tag = (tag << 8) | 0x20;
524   return tag;
525 }
526
527 static char *
528 otf_count_features (char *p, char *end, char stopper, int *count)
529 {
530   int negative = 0;
531
532   *count = 0;
533   if (*p != stopper && *p != '\0')
534     while (1)
535       {
536         (*count)++;
537         if (*p == '*')
538           {
539             p++;
540             if (*p == stopper || *p == '\0')
541               break;
542             return NULL;
543           }
544         if (*p == '~')
545           {
546             if (negative++ == 0)
547               (*count)++;
548             p += 5;
549           }
550         else 
551           p += 4;
552         if (p > end)
553           return NULL;
554         if (*p == stopper || *p == '\0')
555           break;
556         if (*p != ',')
557           return NULL;
558         p++;
559         if (! *p)
560           return NULL;
561       }
562   return p;
563 }
564
565 static void
566 otf_store_features (char *p, char *end, unsigned *buf)
567 {
568   int negative = 0;
569   int i;
570
571   for (i = 0; p < end;)
572     {
573       if (*p == '*')
574         buf[i++] = 0xFFFFFFFF, p += 2, negative = 1;
575       else if (*p == '~')
576         {
577           if (negative++ == 0)
578             buf[i++] = 0xFFFFFFFF;
579           buf[i++] = gen_otf_tag (p + 1), p += 6;
580         }
581       else
582         buf[i++] = gen_otf_tag (p), p += 5;
583     }
584   buf[i] = 0;
585 }
586
587 static int
588 parse_otf_command (MSymbol symbol, MFLTOtfSpec *spec)
589 {
590   char *str = MSYMBOL_NAME (symbol);
591   char *end = str + MSYMBOL_NAMELEN (symbol);
592   unsigned int script, langsys;
593   char *gsub, *gpos;
594   int gsub_count = 0, gpos_count = 0;
595   char *p;
596
597   memset (spec, 0, sizeof (MFLTOtfSpec));
598
599   spec->sym = symbol;
600   str += 5;                     /* skip the heading ":otf=" */
601   script = gen_otf_tag (str);
602   str += 4;
603   if (*str == '/')
604     {
605       langsys = gen_otf_tag (str);
606       str += 4;
607     }
608   else
609     langsys = 0;
610   gsub = str;
611   if (*str != '=')
612     /* Apply all GSUB features.  */
613       gsub_count = 1;
614   else
615     {
616       p = str + 1;
617       str = otf_count_features (p, end, '+', &gsub_count);
618       if (! str)
619         MERROR (MERROR_FLT, -1);
620     }
621   gpos = str;
622   if (*str != '+')
623     /* Apply all GPOS features.  */
624     gpos_count = 1;
625   else
626     {
627       p = str + 1;
628       str = otf_count_features (p, end, '\0', &gpos_count);
629       if (! str)
630         MERROR (MERROR_FLT, -1);
631     }
632
633   spec->script = script;
634   spec->langsys = langsys;
635   if (gsub_count > 0)
636     {
637       spec->features[0] = malloc (sizeof (int) * (gsub_count + 1));
638       if (! spec->features[0])
639         return -2;
640       if (*gsub == '=')
641         otf_store_features (gsub + 1, gpos, spec->features[0]);
642       else
643         spec->features[0][0] = 0xFFFFFFFF, spec->features[0][1] = 0;
644     }
645   if (gpos_count > 0)
646     {
647       spec->features[1] = malloc (sizeof (int) * (gpos_count + 1));
648       if (! spec->features[1])
649         {
650           if (spec->features[0])
651             free (spec->features[0]);
652           return -2;
653         }
654       if (*gpos == '+')
655         otf_store_features (gpos + 1, str, spec->features[1]);
656       else
657         spec->features[1][0] = 0xFFFFFFFF, spec->features[1][1] = 0;
658     }
659   return 0;
660 }
661
662
663 /* Parse OTF command name NAME and store the result in CMD.
664    NAME has this form:
665         :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
666    where GSUB-FEATURES and GPOS-FEATURES have this form:
667         [FEATURE[,FEATURE]*] | ' '  */
668
669 static int
670 load_otf_command (FontLayoutCmd *cmd, MSymbol sym)
671 {
672   char *name = MSYMBOL_NAME (sym);
673   int result;
674
675   if (name[0] != ':')
676     {
677       /* This is old format of "otf:...".  Change it to ":otf=...".  */
678       char *str = alloca (MSYMBOL_NAMELEN (sym) + 2);
679
680       sprintf (str, ":otf=");
681       strcat (str, name + 4);
682       sym = msymbol (str);
683     }
684
685   result = parse_otf_command (sym, &cmd->body.otf);
686   if (result == -2)
687     return result;
688   cmd->type = FontLayoutCmdTypeOTF;
689   return 0;
690 }
691
692
693 /* Read a decimal number from STR preceded by one of "+-><".  '+' and
694    '>' means a plus sign, '-' and '<' means a minus sign.  If the
695    number is greater than 127, limit it to 127.  */
696
697 static int
698 read_decimal_number (char **str)
699 {
700   char *p = *str;
701   int sign = (*p == '-' || *p == '<') ? -1 : 1;
702   int n = 0;
703
704   p++;
705   while (*p >= '0' && *p <= '9')
706     n = n * 10 + *p++ - '0';
707   *str = p;
708   if (n == 0)
709     n = 5;
710   return (n < 127 ? n * sign : 127 * sign);
711 }
712
713
714 /* Read a horizontal and vertical combining positions from STR, and
715    store them in the place pointed by X and Y.  The horizontal
716    position left, center, and right are represented by 0, 1, and 2
717    respectively.  The vertical position top, center, bottom, and base
718    are represented by 0, 1, 2, and 3 respectively.  If successfully
719    read, return 0, else return -1.  */
720
721 static int
722 read_combining_position (char *str, int *x, int *y)
723 {
724   int c = *str++;
725   int i;
726
727   /* Vertical position comes first.  */
728   for (i = 0; i < 4; i++)
729     if (c == "tcbB"[i])
730       {
731         *y = i;
732         break;
733       }
734   if (i == 4)
735     return -1;
736   c = *str;
737   /* Then comse horizontal position.  */
738   for (i = 0; i < 3; i++)
739     if (c == "lcr"[i])
740       {
741         *x = i;
742         return 0;
743       }
744   return -1;
745 }
746
747
748 /* Return a combining code corresponding to SYM.  */
749
750 static int
751 get_combining_command (MSymbol sym)
752 {
753   char *str = msymbol_name (sym);
754   int base_x, base_y, add_x, add_y, off_x, off_y;
755   int c;
756
757   if (read_combining_position (str, &base_x, &base_y) < 0)
758     return 0;
759   str += 2;
760   c = *str;
761   if (c == '.')
762     {
763       off_x = off_y = 128;
764       str++;
765     }
766   else
767     {
768       if (c == '+' || c == '-')
769         {
770           off_y = read_decimal_number (&str) + 128;
771           c = *str;
772         }
773       else
774         off_y = 128;
775       if (c == '<' || c == '>')
776         off_x = read_decimal_number (&str) + 128;
777       else
778         off_x = 128;
779     }
780   if (read_combining_position (str, &add_x, &add_y) < 0)
781     return 0;
782
783   c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
784   return (COMBINING_CODE_TO_CMD_ID (c));
785 }
786
787
788 /* Load a command from PLIST into STAGE, and return that
789    identification number.  If ID is not INVALID_CMD_ID, that means we
790    are loading a top level command or a macro.  In that case, use ID
791    as the identification number of the command.  Otherwise, generate a
792    new id number for the command.  MACROS is a list of raw macros.  */
793
794 static int
795 load_command (FontLayoutStage *stage, MPlist *plist,
796               MPlist *macros, int id)
797 {
798   int i;
799   int result;
800
801   if (MPLIST_INTEGER_P (plist))
802     {
803       int code = MPLIST_INTEGER (plist);
804
805       if (code < 0)
806         MERROR (MERROR_DRAW, INVALID_CMD_ID);
807       return code;
808     }
809   else if (MPLIST_PLIST_P (plist))
810     {
811       /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
812                    | ( ( INTEGER INTEGER ) ... )
813                    | ( ( range INTEGER INTEGER ) ... )
814                    | ( ( font-facilty [ INTEGER ] ) ... )
815                    | ( ( font-facilty OTF-SPEC ) ... )  */
816       MPlist *elt = MPLIST_PLIST (plist);
817       int len = MPLIST_LENGTH (elt) - 1;
818       FontLayoutCmd *cmd;
819
820       if (id == INVALID_CMD_ID)
821         {
822           FontLayoutCmd dummy;
823           id = INDEX_TO_CMD_ID (stage->used);
824           MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
825         }
826       cmd = stage->cmds + CMD_ID_TO_INDEX (id);
827
828       if (MPLIST_SYMBOL_P (elt))
829         {
830           FontLayoutCmdCond *cond;
831
832           if (MPLIST_SYMBOL (elt) != Mcond)
833             MERROR (MERROR_DRAW, INVALID_CMD_ID);
834           elt = MPLIST_NEXT (elt);
835           cmd->type = FontLayoutCmdTypeCond;
836           cond = &cmd->body.cond;
837           cond->seq_beg = cond->seq_end = -1;
838           cond->seq_from = cond->seq_to = 0;
839           cond->n_cmds = len;
840           MTABLE_CALLOC (cond->cmd_ids, len, MERROR_DRAW);
841           for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
842             {
843               int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
844
845               if (this_id == INVALID_CMD_ID || this_id == -2)
846                 MERROR (MERROR_DRAW, this_id);
847               /* The above load_command may relocate stage->cmds.  */
848               cmd = stage->cmds + CMD_ID_TO_INDEX (id);
849               cond = &cmd->body.cond;
850               cond->cmd_ids[i] = this_id;
851               if (this_id <= CMD_ID_OFFSET_INDEX)
852                 {
853                   FontLayoutCmd *this_cmd
854                     = stage->cmds + CMD_ID_TO_INDEX (this_id);
855
856                   if (this_cmd->type == FontLayoutCmdTypeRule
857                       && this_cmd->body.rule.src_type == SRC_SEQ)
858                     {
859                       int first_char = this_cmd->body.rule.src.seq.codes[0];
860
861                       if (cond->seq_beg < 0)
862                         {
863                           /* The first SEQ command.  */
864                           cond->seq_beg = i;
865                           cond->seq_from = cond->seq_to = first_char;
866                         }
867                       else if (cond->seq_end < 0)
868                         {
869                           /* The following SEQ command.  */
870                           if (cond->seq_from > first_char)
871                             cond->seq_from = first_char;
872                           else if (cond->seq_to < first_char)
873                             cond->seq_to = first_char;
874                         }
875                     }
876                   else
877                     {
878                       if (cond->seq_beg >= 0 && cond->seq_end < 0)
879                         /* The previous one is the last SEQ command.  */
880                         cond->seq_end = i;
881                     }
882                 }
883               else
884                 {
885                   if (cond->seq_beg >= 0 && cond->seq_end < 0)
886                     /* The previous one is the last SEQ command.  */
887                     cond->seq_end = i;
888                 }
889             }
890           if (cond->seq_beg >= 0 && cond->seq_end < 0)
891             /* The previous one is the last SEQ command.  */
892             cond->seq_end = i;
893         }
894       else
895         {
896           cmd->type = FontLayoutCmdTypeRule;
897           if (MPLIST_MTEXT_P (elt))
898             {
899               MText *mt = MPLIST_MTEXT (elt);
900               char *str = (char *) MTEXT_DATA (mt);
901
902               if (str[0] != '^')
903                 {
904                   mtext_ins_char (mt, 0, '^', 1);
905                   str = (char *) MTEXT_DATA (mt);
906                 }
907               if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
908                 MERROR (MERROR_FONT, INVALID_CMD_ID);
909               cmd->body.rule.src_type = SRC_REGEX;
910               cmd->body.rule.src.re.pattern = strdup (str);
911             }
912           else if (MPLIST_INTEGER_P (elt))
913             {
914               cmd->body.rule.src_type = SRC_INDEX;
915               cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
916             }
917           else if (MPLIST_PLIST_P (elt))
918             {
919               MPlist *pl = MPLIST_PLIST (elt);
920               int size = MPLIST_LENGTH (pl);
921
922               if (MPLIST_INTEGER_P (pl))
923                 {
924                   int i;
925
926                   cmd->body.rule.src_type = SRC_SEQ;
927                   cmd->body.rule.src.seq.n_codes = size;
928                   MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
929                                  MERROR_FONT);
930                   for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
931                     {
932                       if (! MPLIST_INTEGER_P (pl))
933                         MERROR (MERROR_DRAW, INVALID_CMD_ID);
934                       cmd->body.rule.src.seq.codes[i]
935                         = (unsigned) MPLIST_INTEGER (pl);
936                     }
937                 }
938               else if (MPLIST_SYMBOL_P (pl) && size == 3)
939                 {
940                   if (MPLIST_SYMBOL (pl) != Mrange)
941                     MERROR (MERROR_FLT, INVALID_CMD_ID);
942                   cmd->body.rule.src_type = SRC_RANGE;
943                   pl = MPLIST_NEXT (pl);
944                   if (! MPLIST_INTEGER_P (pl))
945                     MERROR (MERROR_DRAW, INVALID_CMD_ID);
946                   cmd->body.rule.src.range.from
947                     = (unsigned) MPLIST_INTEGER (pl);
948                   pl = MPLIST_NEXT (pl);
949                   if (! MPLIST_INTEGER_P (pl))
950                     MERROR (MERROR_DRAW, INVALID_CMD_ID);
951                   cmd->body.rule.src.range.to
952                     = (unsigned) MPLIST_INTEGER (pl);
953                 }
954               else if (MPLIST_SYMBOL_P (pl) && size <= 2)
955                 {
956                   if (MPLIST_SYMBOL (pl) != Mfont_facility)
957                     MERROR (MERROR_FLT, INVALID_CMD_ID);
958                   pl = MPLIST_NEXT (pl);
959                   if (MPLIST_SYMBOL_P (pl))
960                     {
961                       MSymbol sym = MPLIST_SYMBOL (pl);
962                       char *otf_spec = MSYMBOL_NAME (sym);
963
964                       if (otf_spec[0] == ':' && otf_spec[1] == 'o'
965                           && otf_spec[2] == 't' && otf_spec[3] == 'f')
966                         parse_otf_command (sym, &cmd->body.rule.src.otf_spec);
967                       else
968                         MERROR (MERROR_FLT, INVALID_CMD_ID);
969                       cmd->body.rule.src_type = SRC_OTF_SPEC;
970                     }
971                   else
972                     {
973                       cmd->body.rule.src_type = SRC_HAS_GLYPH;
974                       if (MPLIST_INTEGER_P (pl))
975                         cmd->body.rule.src.supported_glyph
976                           = MPLIST_INTEGER (pl);
977                       else
978                         cmd->body.rule.src.supported_glyph = -1;
979                     }
980                 }
981               else
982                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
983             }
984           else
985             MERROR (MERROR_DRAW, INVALID_CMD_ID);
986
987           elt = MPLIST_NEXT (elt);
988           cmd->body.rule.n_cmds = len;
989           MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
990           for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
991             {
992               int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
993
994               if (this_id == INVALID_CMD_ID || this_id == -2)
995                 MERROR (MERROR_DRAW, this_id);
996               /* The above load_command may relocate stage->cmds.  */
997               cmd = stage->cmds + CMD_ID_TO_INDEX (id);
998               cmd->body.rule.cmd_ids[i] = this_id;
999             }
1000         }
1001     }
1002   else if (MPLIST_SYMBOL_P (plist))
1003     {
1004       MPlist *elt;
1005       MSymbol sym = MPLIST_SYMBOL (plist);
1006       char *name = msymbol_name (sym);
1007       int len = strlen (name);
1008       FontLayoutCmd cmd;
1009
1010       if (len > 4
1011           && ((name[0] == 'o' && name[1] == 't'
1012                && name[2] == 'f' && name[3] == ':')
1013               || (name[0] == ':' && name[1] == 'o' && name[2] == 't'
1014                   && name[3] == 'f' && name[4] == '=')))
1015         {
1016           result = load_otf_command (&cmd, sym);
1017           if (result < 0)
1018             return result;
1019           if (id == INVALID_CMD_ID)
1020             {
1021               id = INDEX_TO_CMD_ID (stage->used);
1022               MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
1023             }
1024           else
1025             stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
1026           return id;
1027         }
1028
1029       if (len == 1)
1030         {
1031           if (*name == '=')
1032             return CMD_ID_COPY;
1033           else if (*name == '*')
1034             return CMD_ID_REPEAT;
1035           else if (*name == '<')
1036             return CMD_ID_CLUSTER_BEGIN;
1037           else if (*name == '>')
1038             return CMD_ID_CLUSTER_END;
1039           else if (*name == '|')
1040             return CMD_ID_SEPARATOR;
1041           else if (*name == '[')
1042             return CMD_ID_LEFT_PADDING;
1043           else if (*name == ']')
1044             return CMD_ID_RIGHT_PADDING;
1045           else
1046             id = 0;
1047         }
1048       else
1049         {
1050           id = get_combining_command (sym);
1051           if (id)
1052             return id;
1053         }
1054
1055       i = 1;
1056       MPLIST_DO (elt, macros)
1057         {
1058           if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
1059             {
1060               id = INDEX_TO_CMD_ID (i);
1061               if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
1062                 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
1063                                    macros, id);
1064               return id;
1065             }
1066           i++;
1067         }
1068       MERROR (MERROR_DRAW, INVALID_CMD_ID);
1069     }
1070   else
1071     MERROR (MERROR_DRAW, INVALID_CMD_ID);
1072
1073   return id;
1074 }
1075
1076 static void
1077 free_flt_command (FontLayoutCmd *cmd)
1078 {
1079   if (cmd->type == FontLayoutCmdTypeRule)
1080     {
1081       FontLayoutCmdRule *rule = &cmd->body.rule;
1082
1083       if (rule->src_type == SRC_REGEX)
1084         {
1085           free (rule->src.re.pattern);
1086           regfree (&rule->src.re.preg);
1087         }
1088       else if (rule->src_type == SRC_SEQ)
1089         free (rule->src.seq.codes);
1090       free (rule->cmd_ids);
1091     }
1092   else if (cmd->type == FontLayoutCmdTypeCond)
1093     free (cmd->body.cond.cmd_ids);
1094   else if (cmd->type == FontLayoutCmdTypeOTF)
1095     {
1096       if (cmd->body.otf.features[0])
1097         free (cmd->body.otf.features[0]);
1098       if (cmd->body.otf.features[1])
1099         free (cmd->body.otf.features[1]);
1100     }
1101 }
1102
1103 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
1104    and return it.  PLIST has this form:
1105       PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
1106 */
1107
1108 static FontLayoutStage *
1109 load_generator (MPlist *plist)
1110 {
1111   FontLayoutStage *stage;
1112   MPlist *elt, *pl;
1113   FontLayoutCmd dummy;
1114   int result;
1115
1116   MSTRUCT_CALLOC (stage, MERROR_DRAW);
1117   MLIST_INIT1 (stage, cmds, 32);
1118   dummy.type = FontLayoutCmdTypeMAX;
1119   MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
1120   MPLIST_DO (elt, MPLIST_NEXT (plist))
1121     {
1122       if (! MPLIST_PLIST_P (elt))
1123         MERROR (MERROR_FONT, NULL);
1124       pl = MPLIST_PLIST (elt);
1125       if (! MPLIST_SYMBOL_P (pl))
1126         MERROR (MERROR_FONT, NULL);
1127       MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
1128     }
1129
1130   /* Load the first command from PLIST into STAGE->cmds[0].  Macros
1131      called in the first command are also loaded from MPLIST_NEXT
1132      (PLIST) into STAGE->cmds[n].  */
1133   result = load_command (stage, plist, MPLIST_NEXT (plist),
1134                          INDEX_TO_CMD_ID (0));
1135   if (result == INVALID_CMD_ID || result == -2)
1136     {
1137       MLIST_FREE1 (stage, cmds);
1138       free (stage);
1139       return NULL;
1140     }
1141
1142   return stage;
1143 }
1144
1145
1146 /* Load stages of the font layout table FLT.  */
1147
1148 static int
1149 load_flt (MFLT *flt, MPlist *key_list)
1150 {
1151   MPlist *top, *plist, *pl, *p;
1152   MCharTable *category = NULL;
1153   MSymbol sym;
1154
1155   if (key_list)
1156     top = (MPlist *) mdatabase__load_for_keys (flt->mdb, key_list);
1157   else
1158     top = (MPlist *) mdatabase_load (flt->mdb);
1159   if (! top)
1160     return -1;
1161   if (! MPLIST_PLIST_P (top))
1162     {
1163       M17N_OBJECT_UNREF (top);
1164       MERROR (MERROR_FLT, -1);
1165     }
1166
1167   if (key_list)
1168     {
1169       plist = mdatabase__props (flt->mdb);
1170       if (! plist)
1171         MERROR (MERROR_FLT, -1);
1172       MPLIST_DO (plist, plist)
1173         if (MPLIST_PLIST_P (plist))
1174           {
1175             pl = MPLIST_PLIST (plist);
1176             if (! MPLIST_SYMBOL_P (pl)
1177                 || MPLIST_SYMBOL (pl) != Mfont)
1178               continue;
1179             pl = MPLIST_NEXT (pl);
1180             if (! MPLIST_PLIST_P (pl))
1181               continue;
1182             p = MPLIST_PLIST (pl);
1183             if (! MPLIST_SYMBOL_P (p))
1184               continue;
1185             p = MPLIST_NEXT (p);
1186             if (! MPLIST_SYMBOL_P (p))
1187               continue;
1188             flt->family = MPLIST_SYMBOL (p);
1189             MPLIST_DO (p, MPLIST_NEXT (p))
1190               if (MPLIST_SYMBOL_P (p))
1191                 {
1192                   sym = MPLIST_SYMBOL (p);
1193                   if (MSYMBOL_NAME (sym)[0] != ':')
1194                     flt->registry = sym, sym = Mnil;
1195                   else
1196                     break;
1197                 }
1198             if (sym)
1199               {
1200                 char *otf_spec = MSYMBOL_NAME (sym);
1201
1202                 if (otf_spec[0] == ':' && otf_spec[1] == 'o'
1203                     && otf_spec[2] == 't' && otf_spec[3] == 'f')
1204                   parse_otf_command (sym, &flt->otf);
1205               }
1206             break;
1207           }
1208     }
1209   MPLIST_DO (plist, top)
1210     {
1211       if (MPLIST_SYMBOL_P (plist)
1212           && MPLIST_SYMBOL (plist) == Mend)
1213         {
1214           mplist_set (plist, Mnil, NULL);
1215           break;
1216         }
1217       if (! MPLIST_PLIST (plist))
1218         continue;
1219       pl = MPLIST_PLIST (plist);
1220       if (! MPLIST_SYMBOL_P (pl))
1221         continue;
1222       sym = MPLIST_SYMBOL (pl);
1223       pl = MPLIST_NEXT (pl);
1224       if (! pl)
1225         continue;
1226       if (sym == Mcategory)
1227         {
1228           if (category)
1229             M17N_OBJECT_UNREF (category);
1230           else if (flt->coverage)
1231             {
1232               category = flt->coverage;
1233               continue;
1234             }
1235           category = load_category_table (pl);
1236           if (! flt->coverage)
1237             {
1238               flt->coverage = category;
1239               M17N_OBJECT_REF (category);
1240             }
1241         }
1242       else if (sym == Mgenerator)
1243         {
1244           FontLayoutStage *stage;
1245
1246           if (! category)
1247             break;
1248           stage = load_generator (pl);
1249           if (! stage)
1250             break;
1251           stage->category = category;
1252           M17N_OBJECT_REF (category);
1253           if (! flt->stages)
1254             flt->stages = mplist ();
1255           mplist_add (flt->stages, Mt, stage);
1256         }
1257     }
1258   if (category)
1259     M17N_OBJECT_UNREF (category);
1260   M17N_OBJECT_UNREF (top);
1261   if (! MPLIST_TAIL_P (plist))
1262     {
1263       M17N_OBJECT_UNREF (flt->stages);
1264       MERROR (MERROR_FLT, -1);
1265     }
1266   return 0;
1267 }
1268
1269
1270 static void
1271 free_flt_stage (FontLayoutStage *stage)
1272 {
1273   int i;
1274
1275   M17N_OBJECT_UNREF (stage->category);
1276   for (i = 0; i < stage->used; i++)
1277     free_flt_command (stage->cmds + i);
1278   MLIST_FREE1 (stage, cmds);
1279   free (stage);
1280 }
1281
1282 static void
1283 free_flt_list ()
1284 {
1285   if (flt_list)
1286     {
1287       MPlist *plist, *pl;
1288
1289       MPLIST_DO (plist, flt_list)
1290         {
1291           MFLT *flt = MPLIST_VAL (plist);
1292
1293           if (flt->coverage)
1294             M17N_OBJECT_UNREF (flt->coverage);
1295           if (flt->stages)
1296             {
1297               MPLIST_DO (pl, MPLIST_NEXT (flt->stages))
1298                 free_flt_stage (MPLIST_VAL (pl));
1299               M17N_OBJECT_UNREF (flt->stages);
1300             }
1301         }
1302       M17N_OBJECT_UNREF (flt_list);
1303     }
1304 }
1305
1306 static int
1307 list_flt ()
1308 {
1309   MPlist *plist, *key_list = NULL;
1310   MPlist *pl;
1311   int result = 0;
1312
1313   if (! (plist = mdatabase_list (Mfont, Mlayouter, Mnil, Mnil)))
1314     return -1;
1315   if (! (flt_list = mplist ()))
1316     goto err;
1317   if (! (key_list = mplist ()))
1318     goto err;
1319   if (! mplist_add (key_list, Mcategory, Mt))
1320     goto err;
1321
1322   MPLIST_DO (pl, plist)
1323     {
1324       MDatabase *mdb = MPLIST_VAL (pl);
1325       MSymbol *tags = mdatabase_tag (mdb);
1326       MFLT *flt;
1327
1328       if (! MSTRUCT_CALLOC_SAFE (flt))
1329         goto err;
1330       flt->name = tags[2];
1331       flt->mdb = mdb;
1332       if (load_flt (flt, key_list) < 0)
1333         free (flt);
1334       else
1335         {
1336           if (MPLIST_TAIL_P (flt_list))
1337             {
1338               flt_min_coverage = mchartable_min_char (flt->coverage);
1339               flt_max_coverage = mchartable_max_char (flt->coverage);
1340             }
1341           else
1342             {
1343               int c;
1344
1345               c = mchartable_min_char (flt->coverage);
1346               if (flt_min_coverage > c)
1347                 flt_min_coverage = c;
1348               c = mchartable_max_char (flt->coverage);
1349               if (flt_max_coverage < c)
1350                 flt_max_coverage = c;
1351             }
1352           if (! mplist_push (flt_list, flt->name, flt))
1353             goto err;
1354         }
1355     }
1356   goto end;
1357
1358  err:
1359   free_flt_list ();
1360   result = -1;
1361  end:
1362   M17N_OBJECT_UNREF (plist);  
1363   M17N_OBJECT_UNREF (key_list);
1364   return result;
1365 }
1366
1367 /* FLS (Font Layout Service) */
1368
1369 /* Structure to hold information about a context of FLS.  */
1370
1371 typedef struct
1372 {
1373   /* Pointer to the current stage.  */
1374   FontLayoutStage *stage;
1375
1376   /* Pointer to the font.  */
1377   MFLTFont *font;
1378
1379   /* Input and output glyph string.  */
1380   MFLTGlyphString *in, *out;
1381
1382   /* Encode each character or code of a glyph by the current category
1383      table into this array.  An element is a category letter used for
1384      a regular expression matching.  */
1385   char *encoded;
1386   int *match_indices;
1387   int code_offset;
1388   int cluster_begin_idx;
1389   int cluster_begin_pos;
1390   int cluster_end_pos;
1391   int combining_code;
1392   int left_padding;
1393   int check_mask;
1394 } FontLayoutContext;
1395
1396 static int run_command (int, int, int, int, FontLayoutContext *);
1397
1398 #define NMATCH 20
1399
1400 static int
1401 run_rule (int depth,
1402           FontLayoutCmdRule *rule, int from, int to, FontLayoutContext *ctx)
1403 {
1404   int *saved_match_indices = ctx->match_indices;
1405   int match_indices[NMATCH * 2];
1406   int consumed;
1407   int i;
1408   int orig_from = from;
1409
1410   if (rule->src_type == SRC_SEQ)
1411     {
1412       int len;
1413
1414       len = rule->src.seq.n_codes;
1415       if (len > (to - from))
1416         return 0;
1417       for (i = 0; i < len; i++)
1418         if (rule->src.seq.codes[i] != GREF (ctx->in, from + i)->code)
1419           break;
1420       if (i < len)
1421         return 0;
1422       to = from + len;
1423       if (MDEBUG_FLAG () > 2)
1424         MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "",
1425                        rule->src.seq.codes[0]);
1426     }
1427   else if (rule->src_type == SRC_RANGE)
1428     {
1429       int head;
1430
1431       if (from >= to)
1432         return 0;
1433       head = GREF (ctx->in, from)->code;
1434       if (head < rule->src.range.from || head > rule->src.range.to)
1435         return 0;
1436       ctx->code_offset = head - rule->src.range.from;
1437       to = from + 1;
1438       if (MDEBUG_FLAG () > 2)
1439         MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "",
1440                        rule->src.range.from, rule->src.range.to);
1441     }
1442   else if (rule->src_type == SRC_REGEX)
1443     {
1444       regmatch_t pmatch[NMATCH];
1445       char saved_code;
1446       int result;
1447
1448       if (from > to)
1449         return 0;
1450       saved_code = ctx->encoded[to];
1451       ctx->encoded[to] = '\0';
1452       result = regexec (&(rule->src.re.preg),
1453                         ctx->encoded + from, NMATCH, pmatch, 0);
1454       if (result == 0 && pmatch[0].rm_so == 0)
1455         {
1456           if (MDEBUG_FLAG () > 2)
1457             MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "",
1458                            rule->src.re.pattern,
1459                            ctx->encoded + from,
1460                            pmatch[0].rm_eo);
1461           ctx->encoded[to] = saved_code;
1462           for (i = 0; i < NMATCH; i++)
1463             {
1464               if (pmatch[i].rm_so < 0)
1465                 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1466               else
1467                 {
1468                   match_indices[i * 2] = from + pmatch[i].rm_so;
1469                   match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1470                 }
1471             }
1472           ctx->match_indices = match_indices;
1473           to = match_indices[1];
1474         }
1475       else
1476         {
1477           ctx->encoded[to] = saved_code;
1478           return 0;
1479         }
1480     }
1481   else if (rule->src_type == SRC_INDEX)
1482     {
1483       if (rule->src.match_idx >= NMATCH)
1484         return 0;
1485       from = ctx->match_indices[rule->src.match_idx * 2];
1486       if (from < 0)
1487         return 0;
1488       to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1489       if (MDEBUG_FLAG () > 2)
1490         MDEBUG_PRINT3 ("\n [FLT] %*s(INDEX %d", depth, "", rule->src.match_idx);
1491     }
1492   else if (rule->src_type == SRC_HAS_GLYPH)
1493     {
1494       int encoded;
1495       unsigned code;
1496
1497       if (rule->src.supported_glyph < 0)
1498         {
1499           if (from >= to)
1500             return 0;
1501           code = GREF (ctx->in, from)->code;
1502           to = from + 1;
1503           encoded = GREF (ctx->in, from)->encoded;
1504         }
1505       else
1506         {
1507           code = rule->src.supported_glyph;
1508           to = from;
1509           encoded = 0;
1510         }
1511       if (! encoded)
1512         {
1513           static MFLTGlyphString gstring;
1514
1515           if (! gstring.glyph_size)
1516             {
1517               gstring.glyph_size = ctx->in->glyph_size;
1518               gstring.glyphs = calloc (1, gstring.glyph_size);
1519               gstring.allocated = 1;
1520               gstring.used = 1;
1521             }
1522           gstring.glyphs[0].code = code;
1523           if (ctx->font->get_glyph_id (ctx->font, &gstring, 0, 1) < 0
1524               || ! gstring.glyphs[0].encoded)
1525             return 0;
1526         }
1527     }
1528   else if (rule->src_type == SRC_OTF_SPEC)
1529     {
1530       MFLTOtfSpec *spec = &rule->src.otf_spec;
1531
1532       if (! ctx->font->check_otf)
1533         {
1534           if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
1535               || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
1536             return 0;
1537         }
1538       else if (! ctx->font->check_otf (ctx->font, spec))
1539         return 0;
1540     }
1541
1542   consumed = 0;
1543   depth++;
1544   for (i = 0; i < rule->n_cmds; i++)
1545     {
1546       int pos;
1547
1548       if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1549         {
1550           if (! consumed)
1551             continue;
1552           i--;
1553         }
1554       pos = run_command (depth, rule->cmd_ids[i], from, to, ctx);
1555       if (pos < 0)
1556         return pos;
1557       consumed = pos > from;
1558       if (consumed)
1559         from = pos;
1560     }
1561
1562   ctx->match_indices = saved_match_indices;
1563   if (MDEBUG_FLAG () > 2)
1564     MDEBUG_PRINT (")");
1565   return (rule->src_type == SRC_INDEX ? orig_from : to);
1566 }
1567
1568 static int
1569 run_cond (int depth,
1570           FontLayoutCmdCond *cond, int from, int to, FontLayoutContext *ctx)
1571 {
1572   int i, pos = 0;
1573
1574   if (MDEBUG_FLAG () > 2)
1575     MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1576   depth++;
1577   for (i = 0; i < cond->n_cmds; i++)
1578     {
1579       /* TODO: Write a code for optimization utilizaing the info
1580          cond->seq_XXX.  */
1581       if ((pos = run_command (depth, cond->cmd_ids[i], from, to, ctx))
1582           != 0)
1583         break;
1584     }
1585   if (pos < 0)
1586     return pos;
1587   if (MDEBUG_FLAG () > 2)
1588     MDEBUG_PRINT (")");
1589   return (pos);
1590 }
1591
1592 static int
1593 run_otf (int depth,
1594          MFLTOtfSpec *otf_spec, int from, int to, FontLayoutContext *ctx)
1595 {
1596   MFLTFont *font = ctx->font;
1597   int from_idx = ctx->out->used;
1598
1599   if (MDEBUG_FLAG () > 2)
1600     MDEBUG_PRINT3 ("\n [FLT] %*s%s", depth, "", MSYMBOL_NAME (otf_spec->sym));
1601
1602   font->get_glyph_id (font, ctx->in, from, to);
1603   if (! font->drive_otf)
1604     {
1605       if (ctx->out->used + (to - from) > ctx->out->allocated)
1606         return -2;
1607       font->get_metrics (font, ctx->in, from, to);
1608       GCPY (ctx->in, from, to - from, ctx->out, ctx->out->used);
1609       ctx->out->used += to - from;
1610     }
1611   else
1612     {
1613       MFLTGlyphAdjustment *adjustment;
1614       int out_len;
1615       int i;
1616
1617       adjustment = alloca ((sizeof *adjustment)
1618                            * (ctx->out->allocated - ctx->out->used));
1619       if (! adjustment)
1620         MERROR (MERROR_FLT, -1);
1621       memset (adjustment, 0,
1622               (sizeof *adjustment) * (ctx->out->allocated - ctx->out->used));
1623       to = font->drive_otf (font, otf_spec, ctx->in, from, to, ctx->out,
1624                             adjustment);
1625       if (to < 0)
1626         return to;
1627       out_len = ctx->out->used - from_idx;
1628       if (otf_spec->features[1])
1629         {
1630           MFLTGlyphAdjustment *a;
1631           MFLTGlyph *g;
1632           
1633           for (i = 0, a = adjustment; i < out_len; i++, a++)
1634             if (a->set)
1635               break;
1636           if (i < out_len)
1637             {
1638               font->get_metrics (font, ctx->out, from_idx, ctx->out->used);
1639               for (g = GREF (ctx->out, from_idx + i);
1640                    i < out_len; i++, a++, g = NEXT (ctx->out, g))
1641                 if (a->set)
1642                   {
1643                     if (a->advance_is_absolute)
1644                       {
1645                         g->xadv = a->xadv;
1646                         g->yadv = a->yadv;
1647                       }
1648                     else if (a->xadv || a->yadv)
1649                       {
1650                         g->xadv += a->xadv;
1651                         g->yadv += a->yadv;
1652                       }
1653                     if (a->xoff || a->yoff)
1654                       {
1655                         int j;
1656                         MFLTGlyph *gg = PREV (ctx->out, g);
1657                         MFLTGlyphAdjustment *aa = a;
1658
1659                         g->xoff = a->xoff;
1660                         g->yoff = a->yoff;
1661                         while (aa->back > 0)
1662                           {
1663                             for (j = 0; j < aa->back;
1664                                  j++, gg = PREV (ctx->out, gg))
1665                               g->xoff -= gg->xadv;
1666                             aa = aa - aa->back;
1667                             g->xoff += aa->xoff;
1668                             g->yoff += aa->yoff;
1669                           }
1670                       }
1671                     g->adjusted = 1;
1672                   }
1673             }
1674         }
1675     }
1676
1677   if (ctx->cluster_begin_idx >= 0)
1678     for (; from_idx < ctx->out->used; from_idx++)
1679       {
1680         MFLTGlyph *g = GREF (ctx->out, from_idx);
1681         UPDATE_CLUSTER_RANGE (ctx, g);
1682       }
1683   return to;
1684 }
1685
1686 static char work[16];
1687
1688 static char *
1689 dump_combining_code (int code)
1690 {
1691   char *vallign = "tcbB";
1692   char *hallign = "lcr";
1693   char *p;
1694   int off_x, off_y;
1695
1696   if (! code)
1697     return "none";
1698   work[0] = vallign[COMBINING_CODE_BASE_Y (code)];
1699   work[1] = hallign[COMBINING_CODE_BASE_X (code)];
1700   off_y = COMBINING_CODE_OFF_Y (code);
1701   off_x = COMBINING_CODE_OFF_X (code);
1702   if (off_y > 0)
1703     sprintf (work + 2, "+%d", off_y);
1704   else if (off_y < 0)
1705     sprintf (work + 2, "%d", off_y);
1706   else if (off_x == 0)
1707     sprintf (work + 2, ".");
1708   p = work + strlen (work);
1709   if (off_x > 0)
1710     sprintf (p, ">%d", off_x);
1711   else if (off_x < 0)
1712     sprintf (p, "<%d", -off_x);
1713   p += strlen (p);
1714   p[0] = vallign[COMBINING_CODE_ADD_Y (code)];
1715   p[1] = hallign[COMBINING_CODE_ADD_X (code)];
1716   p[2] = '\0';
1717   return work;
1718 }
1719
1720 static int
1721 run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
1722 {
1723   MFLTGlyph *g;
1724
1725   if (id >= 0)
1726     {
1727       int i;
1728
1729       /* Direct code (== ctx->code_offset + id) output.
1730          The source is not consumed.  */
1731       if (MDEBUG_FLAG () > 2)
1732         MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "",
1733                        ctx->code_offset + id);
1734       i = (from < to || from == 0) ? from : from - 1;
1735       GDUP (ctx, i);
1736       g = GREF (ctx->out, ctx->out->used - 1);
1737       g->code = ctx->code_offset + id;
1738       SET_ENCODED (g, 0);
1739       SET_MEASURED (g, 0);
1740       if (ctx->combining_code)
1741         SET_COMBINING_CODE (g, ctx, ctx->combining_code);
1742       if (ctx->left_padding)
1743         SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
1744       for (i = from; i < to; i++)
1745         {
1746           MFLTGlyph *tmp = GREF (ctx->in, i);
1747
1748           if (g->from > tmp->from)
1749             g->from = tmp->from;
1750           else if (g->to < tmp->to)
1751             g->to = tmp->to;
1752         }
1753       UPDATE_CLUSTER_RANGE (ctx, g);
1754       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1755       if (MDEBUG_FLAG () > 2)
1756         MDEBUG_PRINT (")");
1757       return (from);
1758     }
1759
1760   if (id <= CMD_ID_OFFSET_INDEX)
1761     {
1762       int idx = CMD_ID_TO_INDEX (id);
1763       FontLayoutCmd *cmd;
1764
1765       if (idx >= ctx->stage->used)
1766         MERROR (MERROR_DRAW, -1);
1767       cmd = ctx->stage->cmds + idx;
1768       if (cmd->type == FontLayoutCmdTypeRule)
1769         to = run_rule (depth, &cmd->body.rule, from, to, ctx);
1770       else if (cmd->type == FontLayoutCmdTypeCond)
1771         to = run_cond (depth, &cmd->body.cond, from, to, ctx);
1772       else if (cmd->type == FontLayoutCmdTypeOTF)
1773         to = run_otf (depth, &cmd->body.otf, from, to, ctx);
1774       return to;
1775     }
1776
1777   if (id <= CMD_ID_OFFSET_COMBINING)
1778     {
1779       ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1780       if (MDEBUG_FLAG () > 2)
1781         MDEBUG_PRINT3 ("\n [FLT] %*s(CMB %s)", depth, "",
1782                        dump_combining_code (ctx->combining_code));
1783       return from;
1784     }
1785
1786   switch (id)
1787     {
1788     case CMD_ID_COPY:
1789       {
1790         if (from >= to)
1791           return from;
1792         GDUP (ctx, from);
1793         g = GREF (ctx->out, ctx->out->used - 1);
1794         if (ctx->combining_code)
1795           SET_COMBINING_CODE (g, ctx, ctx->combining_code);
1796         if (ctx->left_padding)
1797           SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
1798         UPDATE_CLUSTER_RANGE (ctx, g);
1799         if (MDEBUG_FLAG () > 2)
1800           {
1801             if (g->c < 0)
1802               MDEBUG_PRINT2 ("\n [FLT] %*s(COPY |)", depth, "");
1803             else
1804               MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g->code);
1805           }
1806         ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1807         return (from + 1);
1808       }
1809
1810     case CMD_ID_CLUSTER_BEGIN:
1811       if (ctx->cluster_begin_idx < 0)
1812         {
1813           if (MDEBUG_FLAG () > 2)
1814             MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "",
1815                            GREF (ctx->in, from)->from);
1816           ctx->cluster_begin_idx = ctx->out->used;
1817           ctx->cluster_begin_pos = GREF (ctx->in, from)->from;
1818           ctx->cluster_end_pos = GREF (ctx->in, from)->to;
1819         }
1820       return from;
1821
1822     case CMD_ID_CLUSTER_END:
1823       if (ctx->cluster_begin_idx >= 0
1824           && ctx->cluster_begin_idx < ctx->out->used)
1825         {
1826           int i;
1827
1828           if (MDEBUG_FLAG () > 2)
1829             MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos + 1);
1830           for (i = ctx->cluster_begin_idx; i < ctx->out->used; i++)
1831             {
1832               GREF (ctx->out, i)->from = ctx->cluster_begin_pos;
1833               GREF (ctx->out, i)->to = ctx->cluster_end_pos;
1834             }
1835           ctx->cluster_begin_idx = -1;
1836         }
1837       return from;
1838
1839     case CMD_ID_SEPARATOR:
1840       {
1841         int i;
1842
1843         i = from < to ? from : from - 1;
1844         GDUP (ctx, i);
1845         g = GREF (ctx->out, ctx->out->used - 1);
1846         g->c = -1, g->code = 0;
1847         g->xadv = g->yadv = 0;
1848         SET_ENCODED (g, 0);
1849         SET_MEASURED (g, 0);
1850         return from;
1851       }
1852
1853     case CMD_ID_LEFT_PADDING:
1854       if (MDEBUG_FLAG () > 2)
1855         MDEBUG_PRINT2 ("\n [FLT] %*s[", depth, "");
1856       ctx->left_padding = 1;
1857       return from;
1858
1859     case CMD_ID_RIGHT_PADDING:
1860       if (ctx->out->used > 0)
1861         {
1862           if (MDEBUG_FLAG () > 2)
1863             MDEBUG_PRINT2 ("\n [FLT] %*s]", depth, "");
1864           g = GREF (ctx->out, ctx->out->used - 1);
1865           SET_RIGHT_PADDING (g, ctx, RightPaddingMask);
1866         }
1867       return from;
1868     }
1869
1870   MERROR (MERROR_DRAW, -1);
1871 }
1872
1873 static int
1874 run_stages (MFLTGlyphString *gstring, int from, int to,
1875             MFLT *flt, FontLayoutContext *ctx)
1876 {
1877   MFLTGlyphString buf, *temp;
1878   int stage_idx = 0;
1879   int orig_from = from, orig_to = to;
1880   int from_pos, to_pos, len;
1881   int i, j;
1882   MFLTGlyph *g;
1883   MPlist *stages = flt->stages;
1884
1885   from_pos = GREF (ctx->in, from)->from;
1886   to_pos = GREF (ctx->in, to - 1)->to;
1887   len = to_pos - from_pos;
1888
1889   buf = *(ctx->in);
1890   buf.glyphs = NULL;
1891   GINIT (ctx->out, ctx->out->allocated);
1892   ctx->encoded = alloca (ctx->out->allocated);
1893   if (! ctx->out->glyphs || ! ctx->encoded)
1894     return -1;
1895
1896   for (stage_idx = 0; 1; stage_idx++)
1897     {
1898       MCharTable *table;
1899       int result;
1900
1901       ctx->stage = (FontLayoutStage *) MPLIST_VAL (stages);
1902       table = ctx->stage->category;
1903       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1904       for (i = from; i < to; i++)
1905         {
1906           MFLTGlyph *g = GREF (ctx->in, i);
1907           char enc = (GET_ENCODED (g)
1908                       ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
1909                       : g->code
1910                       ? (int) mchartable_lookup (table, g->code)
1911                       : ' ');
1912
1913           ctx->encoded[i] = enc;
1914           if (! enc && stage_idx == 0)
1915             {
1916               to = i;
1917               break;
1918             }
1919         }
1920       ctx->encoded[i] = '\0';
1921       ctx->match_indices[0] = from;
1922       ctx->match_indices[1] = to;
1923       for (i = 2; i < NMATCH; i++)
1924         ctx->match_indices[i] = -1;
1925
1926       if (MDEBUG_FLAG () > 2)
1927         {
1928           MDEBUG_PRINT2 ("\n [FLT]   (STAGE %d \"%s\"", stage_idx,
1929                          ctx->encoded + from);
1930           MDEBUG_PRINT (" (");
1931           for (i = from; i < to; i++)
1932             {
1933               g = GREF (ctx->in, i);
1934               if (g->c == -1)
1935                 MDEBUG_PRINT2 ("%*s|", (i > 0), "");
1936               else
1937                 MDEBUG_PRINT3 ("%*s%04X", (i > 0), "", GREF (ctx->in, i)->code);
1938             }
1939           MDEBUG_PRINT (")");
1940         }
1941       result = run_command (4, INDEX_TO_CMD_ID (0), from, to, ctx);
1942       if (MDEBUG_FLAG () > 2)
1943         MDEBUG_PRINT (")");
1944       if (result < 0)
1945         return result;
1946
1947       stages = MPLIST_NEXT (stages);
1948       /* If this is the last stage, break the loop. */
1949       if (MPLIST_TAIL_P (stages))
1950         break;
1951
1952       /* Otherwise, prepare for the next stage.   */
1953       temp = ctx->in;
1954       ctx->in = ctx->out;
1955       if (buf.glyphs)
1956         ctx->out = temp;
1957       else
1958         {
1959           GINIT (&buf, ctx->out->allocated);
1960           ctx->out = &buf;
1961         }
1962       ctx->out->used = 0;
1963
1964       from = 0;
1965       to = ctx->in->used;
1966     }
1967
1968   if (ctx->out->used > 0)
1969     {
1970       int *g_indices;
1971       int x_ppem = ctx->font->x_ppem << 6, y_ppem = ctx->font->y_ppem << 6;
1972
1973       /* Remove separator glyphs.  */
1974       for (i = 0; i < ctx->out->used;)
1975         {
1976           g = GREF (ctx->out, i);
1977           if (g->c < 0)
1978             GREPLACE (NULL, 0, 0, ctx->out, i, i + 1);
1979           else
1980             i++;
1981         }
1982
1983       /* Get actual glyph IDs of glyphs.  */
1984       ctx->font->get_glyph_id (ctx->font, ctx->out, 0, ctx->out->used);
1985
1986       /* Check if all characters in the range are covered by some
1987          glyph(s).  If not, change <from> and <to> of glyphs to cover
1988          uncovered characters.  */
1989       g_indices = alloca (sizeof (int) * len);
1990       if (! g_indices)
1991         return -1;
1992       for (i = 0; i < len; i++) g_indices[i] = -1;
1993       for (i = 0; i < ctx->out->used; i++)
1994         {
1995           int pos;
1996
1997           g = GREF (ctx->out, i);
1998           for (pos = g->from; pos <= g->to; pos++)
1999             if (g_indices[pos - orig_from] < 0)
2000               g_indices[pos - orig_from] = i;
2001         }
2002       for (i = 0; i < len; i++)
2003         if (g_indices[i] < 0)
2004           {
2005             if (i == 0)
2006               {
2007                 int this_from;
2008
2009                 for (i++; i < len && g_indices[i] < 0; i++);
2010                 j = g_indices[i];
2011                 g = GREF (ctx->out, j);
2012                 this_from = g->from;
2013                 do {
2014                   g->from = orig_from + i;
2015                 } while (++j < ctx->out->used
2016                          && (g = GREF (ctx->out, j))
2017                          && g->from == this_from);
2018               }
2019             else
2020               {
2021                 int this_to;
2022
2023                 j = g_indices[i - 1];
2024                 g = GREF (ctx->out, j);
2025                 this_to = g->to;
2026                 do {
2027                   g->to = orig_from + i + 1;
2028                 } while (--j >= 0
2029                          && (g = GREF (ctx->out, j))
2030                          && g->to == this_to);
2031               }
2032           }
2033
2034       ctx->font->get_metrics (ctx->font, ctx->out, 0, ctx->out->used);
2035
2036       /* Handle combining.  */
2037       if (ctx->check_mask & CombiningCodeMask)
2038         {
2039           MFLTGlyph *base = GREF (ctx->out, 0);
2040           int base_height = base->ascent + base->descent;
2041           int combining_code;
2042
2043           for (i = 1; i < ctx->out->used; i++)
2044             {
2045               if ((g = GREF (ctx->out, i))
2046                   && (combining_code = GET_COMBINING_CODE (g)))
2047                 {
2048                   int height = g->ascent + g->descent;
2049                   int base_x, base_y, add_x, add_y, off_x, off_y;
2050
2051                   if (base->from > g->from)
2052                     base->from = g->from;
2053                   else if (base->to < g->to)
2054                     base->to = g->to;
2055                 
2056                   base_x = COMBINING_CODE_BASE_X (combining_code);
2057                   base_y = COMBINING_CODE_BASE_Y (combining_code);
2058                   add_x = COMBINING_CODE_ADD_X (combining_code);
2059                   add_y = COMBINING_CODE_ADD_Y (combining_code);
2060                   off_x = COMBINING_CODE_OFF_X (combining_code);
2061                   off_y = COMBINING_CODE_OFF_Y (combining_code);
2062
2063                   g->xoff = ((base->xadv * base_x - g->xadv * add_x) / 2
2064                              + x_ppem * off_x / 100 - base->xadv);
2065                   if (base_y < 3)
2066                     g->yoff = base_height * base_y / 2 - base->ascent;
2067                   else
2068                     g->yoff = 0;
2069                   if (add_y < 3)
2070                     g->yoff -= height * add_y / 2 - g->ascent;
2071                   g->yoff -= y_ppem * off_y / 100;
2072                   if (base->lbearing > base->xadv + g->lbearing + g->xoff)
2073                     base->lbearing = base->xadv + g->lbearing + g->xoff;
2074                   if (base->rbearing < base->xadv + g->xadv + g->xoff)
2075                     base->rbearing = base->xadv + g->xadv + g->xoff;
2076                   if (base->ascent < g->ascent - g->yoff)
2077                     base->ascent = g->ascent - g->yoff;
2078                   if (base->descent < g->descent - g->yoff)
2079                     base->descent = g->descent - g->yoff;
2080                   g->xadv = g->yadv = 0;
2081                   if (GET_RIGHT_PADDING (g))
2082                     SET_RIGHT_PADDING (base, ctx, RightPaddingMask);
2083                   g->adjusted = 1;
2084                 }
2085               else
2086                 {
2087                   base = g;
2088                   base_height = g->ascent + g->descent;
2089                 }
2090             }
2091         }
2092
2093       /* Handle padding */
2094       if (ctx->check_mask & (LeftPaddingMask | RightPaddingMask))
2095         for (i = 0; i < ctx->out->used; i++)
2096           {
2097             g = GREF (ctx->out, i);
2098             if (! GET_COMBINING_CODE (g))
2099               {
2100                 if (GET_RIGHT_PADDING (g) && g->rbearing > g->xadv)
2101                   {
2102                     g->xadv = g->rbearing;
2103                     g->adjusted = 1;
2104                   }
2105                 if (GET_LEFT_PADDING (g) && g->lbearing < 0)
2106                   {
2107                     g->xoff += - g->lbearing;
2108                     g->xadv += - g->lbearing;
2109                     g->rbearing += - g->lbearing;
2110                     g->lbearing = 0;
2111                     g->adjusted = 1;
2112                   }
2113               }
2114           }
2115     }
2116
2117   GREPLACE (ctx->out, 0, ctx->out->used, gstring, orig_from, orig_to);
2118   to = orig_from + ctx->out->used;
2119   return to;
2120 }
2121
2122 static void
2123 setup_combining_coverage (int from, int to, void *val, void *arg)
2124 {
2125   int combining_class = (int) val;
2126   int category = 0;
2127
2128   if (combining_class < 200)
2129     category = 'a';
2130   else if (combining_class <= 204)
2131     {
2132       if ((combining_class % 2) == 0)
2133         category = "bcd"[(combining_class - 200) / 2];
2134     }
2135   else if (combining_class <= 232)
2136     {
2137       if ((combining_class % 2) == 0)
2138         category = "efghijklmnopq"[(combining_class - 208) / 2];
2139     }
2140   else if (combining_class == 233)
2141     category = 'r';
2142   else if (combining_class == 234)
2143     category = 's';
2144   else if (combining_class == 240)
2145     category = 't';
2146   mchartable_set_range ((MCharTable *) arg, from, to, (void *) category);
2147 }
2148
2149 static void
2150 setup_combining_flt (MFLT *flt)
2151 {
2152   MSymbol type;
2153   MCharTable *combininig_class_table
2154     = mchar_get_prop_table (Mcombining_class, &type);
2155
2156   mchartable_set_range (flt->coverage, 0, 0x10FFFF, (void *) 'u');
2157   if (combininig_class_table)
2158     mchartable_map (combininig_class_table, (void *) 0,
2159                     setup_combining_coverage, flt->coverage);
2160 }
2161
2162 #define CHECK_FLT_STAGES(flt) ((flt)->stages || load_flt (flt, NULL) == 0)
2163
2164 \f
2165 /* Internal API */
2166
2167 int m17n__flt_initialized;
2168
2169 \f
2170 /* External API */
2171
2172 /* The following two are actually not exposed to a user but concealed
2173    by the macro M17N_INIT (). */
2174
2175 void
2176 m17n_init_flt (void)
2177 {
2178   int mdebug_flag = MDEBUG_INIT;
2179
2180   merror_code = MERROR_NONE;
2181   if (m17n__flt_initialized++)
2182     return;
2183   m17n_init_core ();
2184   if (merror_code != MERROR_NONE)
2185     {
2186       m17n__flt_initialized--;
2187       return;
2188     }
2189
2190   MDEBUG_PUSH_TIME ();
2191
2192   Mcond = msymbol ("cond");
2193   Mrange = msymbol ("range");
2194   Mfont = msymbol ("font");
2195   Mlayouter = msymbol ("layouter");
2196   Mcombining = msymbol ("combining");
2197   Mfont_facility = msymbol ("font-facility");
2198   Mgenerator = msymbol ("generator");
2199   Mend = msymbol ("end");
2200
2201   MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the flt modules."));
2202   MDEBUG_POP_TIME ();
2203 }
2204
2205 void
2206 m17n_fini_flt (void)
2207 {
2208   int mdebug_flag = MDEBUG_FINI;
2209
2210   if (m17n__flt_initialized == 0
2211       || --m17n__flt_initialized > 0)
2212     return;
2213
2214   MDEBUG_PUSH_TIME ();
2215   free_flt_list ();
2216   MDEBUG_PRINT_TIME ("FINI", (stderr, " to finalize the flt modules."));
2217   MDEBUG_POP_TIME ();
2218   m17n_fini_core ();
2219 }
2220
2221 /*** @} */ 
2222 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
2223
2224 /*** @addtogroup m17nFLT */
2225 /*** @{ */
2226 /*=*/
2227
2228 /*=*/
2229 /***en
2230     @brief Return a FLT object whose name is NAME.
2231
2232     The mflt_get () function returns a FLT object whose name is $NAME.
2233
2234     @return
2235     If the operation was successfully, mflt_get () returns a pointer
2236     to a FLT object.  Otherwise, it returns @c NULL.  */
2237
2238 MFLT *
2239 mflt_get (MSymbol name)
2240 {
2241   MFLT *flt;
2242
2243   if (! flt_list && list_flt () < 0)
2244     return NULL;
2245   flt = mplist_get (flt_list, name);
2246   if (! flt || ! CHECK_FLT_STAGES (flt))
2247     return NULL;
2248   if (flt->name == Mcombining
2249       && ! mchartable_lookup (flt->coverage, 0))
2250     setup_combining_flt (flt);
2251
2252   return flt;
2253 }
2254
2255 /*=*/
2256 /***en
2257     @brief Find a FLT suitable for a specified character and font.
2258
2259     The mflt_find () function returns the most appropriate FLT for
2260     rendering the character $C by font $FONT.
2261
2262     @return
2263     If the operation was successfully, mflt_find () returns a pointer
2264     to a FLT object.  Otherwise, it returns @c NULL.  */
2265
2266 MFLT *
2267 mflt_find (int c, MFLTFont *font)
2268 {
2269   MPlist *plist;
2270   MFLT *flt;
2271   static MSymbol unicode_bmp = NULL, unicode_full = NULL;
2272
2273   if (! unicode_bmp)
2274     {
2275       unicode_bmp = msymbol ("unicode-bmp");
2276       unicode_full = msymbol ("unicode-full");
2277     }
2278
2279   if (! flt_list && list_flt () < 0)
2280     return NULL;
2281   if (font)
2282     {
2283       MFLT *best = NULL;
2284
2285       MPLIST_DO (plist, flt_list)
2286         {
2287           flt = MPLIST_VAL (plist);
2288           if (flt->registry != unicode_bmp
2289               && flt->registry != unicode_full)
2290             continue;
2291           if (flt->family && flt->family != font->family)
2292             continue;
2293           if (c >= 0
2294               && ! mchartable_lookup (flt->coverage, c))
2295             continue;
2296           if (flt->otf.sym)
2297             {
2298               MFLTOtfSpec *spec = &flt->otf;
2299
2300               if (! font->check_otf)
2301                 {
2302                   if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
2303                       || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
2304                     continue;
2305                 }
2306               else if (! font->check_otf (font, spec))
2307                 continue;
2308               return flt;
2309             }
2310           best = flt;
2311         }
2312       return best;
2313     }
2314   if (c >= 0)
2315     {
2316       MPLIST_DO (plist, flt_list)
2317         {
2318           flt = MPLIST_VAL (plist);
2319           if (mchartable_lookup (flt->coverage, c))
2320             return flt;
2321         }
2322     }
2323   return NULL;
2324 }
2325
2326 /*=*/
2327 /***en
2328     @brief Return a name of a FLT.
2329
2330     The mflt_name () function returns the name of $FLT.  */
2331
2332 const char *
2333 mflt_name (MFLT *flt)
2334 {
2335   return MSYMBOL_NAME (flt->name);
2336 }
2337
2338 /*=*/
2339 /***en
2340     @brief Return a coverage of a FLT.
2341
2342     The mflt_coverage () function returns a char-table that contains
2343     nonzero value for characters supported by $FLT.  */
2344
2345 MCharTable *
2346 mflt_coverage (MFLT *flt)
2347 {
2348   return flt->coverage;
2349 }
2350
2351 /*=*/
2352 /***en
2353     @brief Layout characters by Font Layout Table.
2354
2355     The mflt_run () function layout characters in $GSTRING between
2356     $FROM (inclusive) and $TO (exclusive) by $FONT.  If $FLT is
2357     nonzero, it is used for all the charaters.  Otherwise, appropriate
2358     FLTs are automatically chosen.
2359
2360     @retval >=0
2361     The operation was successful.  The value is an index to the
2362     $GSTRING->glyphs which was previously indexed by $TO.
2363
2364     @retval -2
2365     $GSTRING->glyphs is too short to store the result.  A caller can
2366     call this fucntion again with the larger $GSTRING->glyphs.
2367
2368     @retval -1
2369     Some other error occurred.  */
2370
2371 int
2372 mflt_run (MFLTGlyphString *gstring, int from, int to,
2373           MFLTFont *font, MFLT *flt)
2374 {
2375   FontLayoutContext ctx;
2376   int match_indices[NMATCH];
2377   MFLTGlyph *g;
2378   MFLTGlyphString out;
2379   int auto_flt = ! flt;
2380   int c, i, j, k;
2381   int this_from, this_to;
2382
2383   out = *gstring;
2384   out.glyphs = NULL;
2385   /* This is usually sufficient, but if not, we retry with the larger
2386      values at most 3 times.  This value is also used for the
2387      allocating size of ctx.encoded.  */
2388   out.allocated = (to - from) * 4;
2389
2390   for (i = from; i < to; i++)
2391     {
2392       g = GREF (gstring, i);
2393       c = g->c;
2394       memset (g, 0, sizeof (MFLTGlyph));
2395       g->code = g->c = c;
2396       g->from = g->to = i;
2397     }
2398
2399   for (this_from = from; this_from < to;)
2400     {
2401       if (! auto_flt)
2402         {
2403           for (this_to = this_from; this_to < to; this_to++)
2404             if (mchartable_lookup (flt->coverage, GREF (gstring, this_to)->c))
2405               break;
2406         }
2407       else
2408         {
2409           if (! flt_list && list_flt () < 0)
2410             {
2411               font->get_glyph_id (font, gstring, this_from, to);
2412               font->get_metrics (font, gstring, this_from, to);
2413               this_from = to;
2414               break;
2415             }
2416           for (this_to = this_from; this_to < to; this_to++)
2417             {
2418               c = GREF (gstring, this_to)->c;
2419               if (c >= flt_min_coverage && c <= flt_max_coverage)
2420                 break;
2421             }
2422           for (; this_to < to; this_to++)
2423             {
2424               c = GREF (gstring, this_to)->c;
2425               if (font->internal
2426                   && mchartable_lookup (((MFLT *) font->internal)->coverage, c))
2427                 {
2428                   flt = font->internal;
2429                   break;
2430                 }
2431               flt = mflt_find (c, font);
2432               if (flt)
2433                 {
2434                   if (CHECK_FLT_STAGES (flt))
2435                     {
2436                       font->internal = flt;
2437                       break;
2438                     }
2439                 }
2440             }
2441         }
2442
2443       if (this_from < this_to)
2444         {
2445           font->get_glyph_id (font, gstring, this_from, this_to);
2446           font->get_metrics (font, gstring, this_from, this_to);
2447           this_from = this_to;
2448         }
2449       if (this_to == to)
2450         break;
2451
2452       MDEBUG_PRINT1 (" [FLT] (%s", MSYMBOL_NAME (flt->name));
2453
2454       for (; this_to < to; this_to++)
2455         if (! mchartable_lookup (flt->coverage, GREF (gstring, this_to)->c))
2456           break;
2457
2458       if (MDEBUG_FLAG ())
2459         {
2460           if (font->family)
2461             MDEBUG_PRINT1 (" (%s)", MSYMBOL_NAME (font->family));
2462           MDEBUG_PRINT ("\n [FLT]   (SOURCE");
2463           for (i = this_from, j = 0; i < this_to; i++, j++)
2464             {
2465               if (j > 0 && j % 8 == 0)
2466                 MDEBUG_PRINT ("\n [FLT]          ");
2467               MDEBUG_PRINT1 (" %04X", GREF (gstring, i)->c);
2468             }
2469           MDEBUG_PRINT (")");
2470         }
2471
2472       for (i = 0; i < 3; i++)
2473         {
2474           /* Setup CTX.  */
2475           memset (&ctx, 0, sizeof ctx);
2476           ctx.match_indices = match_indices;
2477           ctx.font = font;
2478           ctx.cluster_begin_idx = -1;
2479           ctx.in = gstring;
2480           ctx.out = &out;
2481           j = run_stages (gstring, this_from, this_to, flt, &ctx);
2482           if (j != -2)
2483             break;
2484           out.allocated *= 2;
2485         }
2486
2487       if (j < 0)
2488         return j;
2489
2490       to += j - this_to;
2491       this_to = j;
2492
2493       if (MDEBUG_FLAG ())
2494         {
2495           MDEBUG_PRINT ("\n [FLT]   (RESULT");
2496           if (MDEBUG_FLAG () > 1)
2497             for (i = 0; this_from < this_to; this_from++, i++)
2498               {
2499                 if (i > 0 && i % 4 == 0)
2500                   MDEBUG_PRINT ("\n [FLT]          ");
2501                 g = GREF (gstring, this_from);
2502                 MDEBUG_PRINT4 (" (%04X %d %d %d)",
2503                                g->code, g->xadv, g->xoff, g->yoff);
2504               }
2505           else
2506             for (; this_from < this_to; this_from++)
2507               MDEBUG_PRINT1 (" %04X", GREF (gstring, this_from)->code);
2508           MDEBUG_PRINT ("))\n");
2509         }
2510       this_from = this_to;
2511     }
2512
2513   if (gstring->r2l)
2514     {
2515       int len = to - from;
2516
2517       GINIT (&out, len);
2518       memcpy (((char *) out.glyphs),
2519               ((char *) gstring->glyphs) + gstring->glyph_size * from,
2520               gstring->glyph_size * len);
2521       for (i = from, j = to; i < to;)
2522         {
2523           for (k = i + 1, j--; k < to && GREF (&out, k)->xadv == 0;
2524                k++, j--);
2525           GCPY (&out, i, (k - i), gstring, j);
2526           i = k;
2527         }
2528     }
2529
2530   return to;
2531 }
2532
2533 \f
2534 /* for debugging... */
2535
2536 static void
2537 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
2538 {
2539   char *prefix = (char *) alloca (indent + 1);
2540
2541   memset (prefix, 32, indent);
2542   prefix[indent] = 0;
2543
2544   if (id >= 0)
2545     fprintf (stderr, "0x%02X", id);
2546   else if (id <= CMD_ID_OFFSET_INDEX)
2547     {
2548       int idx = CMD_ID_TO_INDEX (id);
2549       FontLayoutCmd *cmd = stage->cmds + idx;
2550
2551       if (cmd->type == FontLayoutCmdTypeRule)
2552         {
2553           FontLayoutCmdRule *rule = &cmd->body.rule;
2554           int i;
2555
2556           fprintf (stderr, "(rule ");
2557           if (rule->src_type == SRC_REGEX)
2558             fprintf (stderr, "\"%s\"", rule->src.re.pattern);
2559           else if (rule->src_type == SRC_INDEX)
2560             fprintf (stderr, "%d", rule->src.match_idx);
2561           else if (rule->src_type == SRC_SEQ)
2562             fprintf (stderr, "(seq)");
2563           else if (rule->src_type == SRC_RANGE)
2564             fprintf (stderr, "(range)");
2565           else
2566             fprintf (stderr, "(invalid src)");
2567
2568           for (i = 0; i < rule->n_cmds; i++)
2569             {
2570               fprintf (stderr, "\n%s  ", prefix);
2571               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
2572             }
2573           fprintf (stderr, ")");
2574         }
2575       else if (cmd->type == FontLayoutCmdTypeCond)
2576         {
2577           FontLayoutCmdCond *cond = &cmd->body.cond;
2578           int i;
2579
2580           fprintf (stderr, "(cond");
2581           for (i = 0; i < cond->n_cmds; i++)
2582             {
2583               fprintf (stderr, "\n%s  ", prefix);
2584               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
2585             }
2586           fprintf (stderr, ")");
2587         }
2588       else if (cmd->type == FontLayoutCmdTypeOTF)
2589         {
2590           fprintf (stderr, "(otf)");
2591         }
2592       else
2593         fprintf (stderr, "(error-command)");
2594     }
2595   else if (id <= CMD_ID_OFFSET_COMBINING)
2596     fprintf (stderr, "cominging-code");
2597   else
2598     fprintf (stderr, "(predefiend %d)", id);
2599 }
2600
2601 void
2602 mdebug_dump_flt (MFLT *flt, int indent)
2603 {
2604   char *prefix = (char *) alloca (indent + 1);
2605   MPlist *plist;
2606   int stage_idx = 0;
2607
2608   memset (prefix, 32, indent);
2609   prefix[indent] = 0;
2610   fprintf (stderr, "(flt");
2611   MPLIST_DO (plist, flt->stages)
2612     {
2613       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
2614       int i;
2615
2616       fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
2617       for (i = 0; i < stage->used; i++)
2618         {
2619           fprintf (stderr, "\n%s    ", prefix);
2620           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
2621         }
2622       fprintf (stderr, ")");
2623       stage_idx++;
2624     }
2625   fprintf (stderr, ")");
2626 }
2627
2628 /*** @} */
2629
2630 /*
2631  Local Variables:
2632  coding: euc-japan
2633  End:
2634 */