eabc0bef08a2c1fcb9903bdcc65e2d96db7f371e
[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   if (! MPLIST_TAIL_P (plist))
1261     {
1262       M17N_OBJECT_UNREF (top);
1263       M17N_OBJECT_UNREF (flt->stages);
1264       MERROR (MERROR_FLT, -1);
1265     }
1266   M17N_OBJECT_UNREF (top);
1267   return 0;
1268 }
1269
1270
1271 static void
1272 free_flt_stage (FontLayoutStage *stage)
1273 {
1274   int i;
1275
1276   M17N_OBJECT_UNREF (stage->category);
1277   for (i = 0; i < stage->used; i++)
1278     free_flt_command (stage->cmds + i);
1279   MLIST_FREE1 (stage, cmds);
1280   free (stage);
1281 }
1282
1283 static void
1284 free_flt_list ()
1285 {
1286   if (flt_list)
1287     {
1288       MPlist *plist, *pl;
1289
1290       MPLIST_DO (plist, flt_list)
1291         {
1292           MFLT *flt = MPLIST_VAL (plist);
1293
1294           if (flt->coverage)
1295             M17N_OBJECT_UNREF (flt->coverage);
1296           if (flt->stages)
1297             {
1298               MPLIST_DO (pl, MPLIST_NEXT (flt->stages))
1299                 free_flt_stage (MPLIST_VAL (pl));
1300               M17N_OBJECT_UNREF (flt->stages);
1301             }
1302         }
1303       M17N_OBJECT_UNREF (flt_list);
1304     }
1305 }
1306
1307 static int
1308 list_flt ()
1309 {
1310   MPlist *plist, *key_list = NULL;
1311   MPlist *pl;
1312   int result = 0;
1313
1314   if (! (plist = mdatabase_list (Mfont, Mlayouter, Mnil, Mnil)))
1315     return -1;
1316   if (! (flt_list = mplist ()))
1317     goto err;
1318   if (! (key_list = mplist ()))
1319     goto err;
1320   if (! mplist_add (key_list, Mcategory, Mt))
1321     goto err;
1322
1323   MPLIST_DO (pl, plist)
1324     {
1325       MDatabase *mdb = MPLIST_VAL (pl);
1326       MSymbol *tags = mdatabase_tag (mdb);
1327       MFLT *flt;
1328
1329       if (! MSTRUCT_CALLOC_SAFE (flt))
1330         goto err;
1331       flt->name = tags[2];
1332       flt->mdb = mdb;
1333       if (load_flt (flt, key_list) < 0)
1334         free (flt);
1335       else
1336         {
1337           if (MPLIST_TAIL_P (flt_list))
1338             {
1339               flt_min_coverage = mchartable_min_char (flt->coverage);
1340               flt_max_coverage = mchartable_max_char (flt->coverage);
1341             }
1342           else
1343             {
1344               int c;
1345
1346               c = mchartable_min_char (flt->coverage);
1347               if (flt_min_coverage > c)
1348                 flt_min_coverage = c;
1349               c = mchartable_max_char (flt->coverage);
1350               if (flt_max_coverage < c)
1351                 flt_max_coverage = c;
1352             }
1353           if (! mplist_push (flt_list, flt->name, flt))
1354             goto err;
1355         }
1356     }
1357   goto end;
1358
1359  err:
1360   free_flt_list ();
1361   result = -1;
1362  end:
1363   M17N_OBJECT_UNREF (plist);  
1364   M17N_OBJECT_UNREF (key_list);
1365   return result;
1366 }
1367
1368 /* FLS (Font Layout Service) */
1369
1370 /* Structure to hold information about a context of FLS.  */
1371
1372 typedef struct
1373 {
1374   /* Pointer to the current stage.  */
1375   FontLayoutStage *stage;
1376
1377   /* Pointer to the font.  */
1378   MFLTFont *font;
1379
1380   /* Input and output glyph string.  */
1381   MFLTGlyphString *in, *out;
1382
1383   /* Encode each character or code of a glyph by the current category
1384      table into this array.  An element is a category letter used for
1385      a regular expression matching.  */
1386   char *encoded;
1387   int *match_indices;
1388   int code_offset;
1389   int cluster_begin_idx;
1390   int cluster_begin_pos;
1391   int cluster_end_pos;
1392   int combining_code;
1393   int left_padding;
1394   int check_mask;
1395 } FontLayoutContext;
1396
1397 static int run_command (int, int, int, int, FontLayoutContext *);
1398
1399 #define NMATCH 20
1400
1401 static int
1402 run_rule (int depth,
1403           FontLayoutCmdRule *rule, int from, int to, FontLayoutContext *ctx)
1404 {
1405   int *saved_match_indices = ctx->match_indices;
1406   int match_indices[NMATCH * 2];
1407   int consumed;
1408   int i;
1409   int orig_from = from;
1410
1411   if (rule->src_type == SRC_SEQ)
1412     {
1413       int len;
1414
1415       len = rule->src.seq.n_codes;
1416       if (len > (to - from))
1417         return 0;
1418       for (i = 0; i < len; i++)
1419         if (rule->src.seq.codes[i] != GREF (ctx->in, from + i)->code)
1420           break;
1421       if (i < len)
1422         return 0;
1423       to = from + len;
1424       if (MDEBUG_FLAG () > 2)
1425         MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "",
1426                        rule->src.seq.codes[0]);
1427     }
1428   else if (rule->src_type == SRC_RANGE)
1429     {
1430       int head;
1431
1432       if (from >= to)
1433         return 0;
1434       head = GREF (ctx->in, from)->code;
1435       if (head < rule->src.range.from || head > rule->src.range.to)
1436         return 0;
1437       ctx->code_offset = head - rule->src.range.from;
1438       to = from + 1;
1439       if (MDEBUG_FLAG () > 2)
1440         MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "",
1441                        rule->src.range.from, rule->src.range.to);
1442     }
1443   else if (rule->src_type == SRC_REGEX)
1444     {
1445       regmatch_t pmatch[NMATCH];
1446       char saved_code;
1447       int result;
1448
1449       if (from > to)
1450         return 0;
1451       saved_code = ctx->encoded[to];
1452       ctx->encoded[to] = '\0';
1453       result = regexec (&(rule->src.re.preg),
1454                         ctx->encoded + from, NMATCH, pmatch, 0);
1455       if (result == 0 && pmatch[0].rm_so == 0)
1456         {
1457           if (MDEBUG_FLAG () > 2)
1458             MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "",
1459                            rule->src.re.pattern,
1460                            ctx->encoded + from,
1461                            pmatch[0].rm_eo);
1462           ctx->encoded[to] = saved_code;
1463           for (i = 0; i < NMATCH; i++)
1464             {
1465               if (pmatch[i].rm_so < 0)
1466                 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1467               else
1468                 {
1469                   match_indices[i * 2] = from + pmatch[i].rm_so;
1470                   match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1471                 }
1472             }
1473           ctx->match_indices = match_indices;
1474           to = match_indices[1];
1475         }
1476       else
1477         {
1478           ctx->encoded[to] = saved_code;
1479           return 0;
1480         }
1481     }
1482   else if (rule->src_type == SRC_INDEX)
1483     {
1484       if (rule->src.match_idx >= NMATCH)
1485         return 0;
1486       from = ctx->match_indices[rule->src.match_idx * 2];
1487       if (from < 0)
1488         return 0;
1489       to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1490       if (MDEBUG_FLAG () > 2)
1491         MDEBUG_PRINT3 ("\n [FLT] %*s(INDEX %d", depth, "", rule->src.match_idx);
1492     }
1493   else if (rule->src_type == SRC_HAS_GLYPH)
1494     {
1495       int encoded;
1496       unsigned code;
1497
1498       if (rule->src.supported_glyph < 0)
1499         {
1500           if (from >= to)
1501             return 0;
1502           code = GREF (ctx->in, from)->code;
1503           to = from + 1;
1504           encoded = GREF (ctx->in, from)->encoded;
1505         }
1506       else
1507         {
1508           code = rule->src.supported_glyph;
1509           to = from;
1510           encoded = 0;
1511         }
1512       if (! encoded)
1513         {
1514           static MFLTGlyphString gstring;
1515
1516           if (! gstring.glyph_size)
1517             {
1518               gstring.glyph_size = ctx->in->glyph_size;
1519               gstring.glyphs = calloc (1, gstring.glyph_size);
1520               gstring.allocated = 1;
1521               gstring.used = 1;
1522             }
1523           gstring.glyphs[0].code = code;
1524           if (ctx->font->get_glyph_id (ctx->font, &gstring, 0, 1) < 0
1525               || ! gstring.glyphs[0].encoded)
1526             return 0;
1527         }
1528     }
1529   else if (rule->src_type == SRC_OTF_SPEC)
1530     {
1531       MFLTOtfSpec *spec = &rule->src.otf_spec;
1532
1533       if (! ctx->font->check_otf)
1534         {
1535           if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
1536               || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
1537             return 0;
1538         }
1539       else if (! ctx->font->check_otf (ctx->font, spec))
1540         return 0;
1541     }
1542
1543   consumed = 0;
1544   depth++;
1545   for (i = 0; i < rule->n_cmds; i++)
1546     {
1547       int pos;
1548
1549       if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1550         {
1551           if (! consumed)
1552             continue;
1553           i--;
1554         }
1555       pos = run_command (depth, rule->cmd_ids[i], from, to, ctx);
1556       if (pos < 0)
1557         return pos;
1558       consumed = pos > from;
1559       if (consumed)
1560         from = pos;
1561     }
1562
1563   ctx->match_indices = saved_match_indices;
1564   if (MDEBUG_FLAG () > 2)
1565     MDEBUG_PRINT (")");
1566   return (rule->src_type == SRC_INDEX ? orig_from : to);
1567 }
1568
1569 static int
1570 run_cond (int depth,
1571           FontLayoutCmdCond *cond, int from, int to, FontLayoutContext *ctx)
1572 {
1573   int i, pos = 0;
1574
1575   if (MDEBUG_FLAG () > 2)
1576     MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1577   depth++;
1578   for (i = 0; i < cond->n_cmds; i++)
1579     {
1580       /* TODO: Write a code for optimization utilizaing the info
1581          cond->seq_XXX.  */
1582       if ((pos = run_command (depth, cond->cmd_ids[i], from, to, ctx))
1583           != 0)
1584         break;
1585     }
1586   if (pos < 0)
1587     return pos;
1588   if (MDEBUG_FLAG () > 2)
1589     MDEBUG_PRINT (")");
1590   return (pos);
1591 }
1592
1593 static int
1594 run_otf (int depth,
1595          MFLTOtfSpec *otf_spec, int from, int to, FontLayoutContext *ctx)
1596 {
1597   MFLTFont *font = ctx->font;
1598   int from_idx = ctx->out->used;
1599
1600   if (MDEBUG_FLAG () > 2)
1601     MDEBUG_PRINT3 ("\n [FLT] %*s%s", depth, "", MSYMBOL_NAME (otf_spec->sym));
1602
1603   font->get_glyph_id (font, ctx->in, from, to);
1604   if (! font->drive_otf)
1605     {
1606       if (ctx->out->used + (to - from) > ctx->out->allocated)
1607         return -2;
1608       font->get_metrics (font, ctx->in, from, to);
1609       GCPY (ctx->in, from, to - from, ctx->out, ctx->out->used);
1610       ctx->out->used += to - from;
1611     }
1612   else
1613     {
1614       MFLTGlyphAdjustment *adjustment;
1615       int out_len;
1616       int i;
1617
1618       adjustment = alloca ((sizeof *adjustment)
1619                            * (ctx->out->allocated - ctx->out->used));
1620       if (! adjustment)
1621         MERROR (MERROR_FLT, -1);
1622       memset (adjustment, 0,
1623               (sizeof *adjustment) * (ctx->out->allocated - ctx->out->used));
1624       to = font->drive_otf (font, otf_spec, ctx->in, from, to, ctx->out,
1625                             adjustment);
1626       if (to < 0)
1627         return to;
1628       out_len = ctx->out->used - from_idx;
1629       if (otf_spec->features[1])
1630         {
1631           MFLTGlyphAdjustment *a;
1632           MFLTGlyph *g;
1633           
1634           for (i = 0, a = adjustment; i < out_len; i++, a++)
1635             if (a->set)
1636               break;
1637           if (i < out_len)
1638             {
1639               font->get_metrics (font, ctx->out, from_idx, ctx->out->used);
1640               for (g = GREF (ctx->out, from_idx + i);
1641                    i < out_len; i++, a++, g = NEXT (ctx->out, g))
1642                 if (a->set)
1643                   {
1644                     if (a->advance_is_absolute)
1645                       {
1646                         g->xadv = a->xadv;
1647                         g->yadv = a->yadv;
1648                       }
1649                     else if (a->xadv || a->yadv)
1650                       {
1651                         g->xadv += a->xadv;
1652                         g->yadv += a->yadv;
1653                       }
1654                     if (a->xoff || a->yoff)
1655                       {
1656                         int j;
1657                         MFLTGlyph *gg = PREV (ctx->out, g);
1658                         MFLTGlyphAdjustment *aa = a;
1659
1660                         g->xoff = a->xoff;
1661                         g->yoff = a->yoff;
1662                         while (aa->back > 0)
1663                           {
1664                             for (j = 0; j < aa->back;
1665                                  j++, gg = PREV (ctx->out, gg))
1666                               g->xoff -= gg->xadv;
1667                             aa = aa - aa->back;
1668                             g->xoff += aa->xoff;
1669                             g->yoff += aa->yoff;
1670                           }
1671                       }
1672                     g->adjusted = 1;
1673                   }
1674             }
1675         }
1676     }
1677
1678   if (ctx->cluster_begin_idx >= 0)
1679     for (; from_idx < ctx->out->used; from_idx++)
1680       {
1681         MFLTGlyph *g = GREF (ctx->out, from_idx);
1682         UPDATE_CLUSTER_RANGE (ctx, g);
1683       }
1684   return to;
1685 }
1686
1687 static char work[16];
1688
1689 static char *
1690 dump_combining_code (int code)
1691 {
1692   char *vallign = "tcbB";
1693   char *hallign = "lcr";
1694   char *p;
1695   int off_x, off_y;
1696
1697   if (! code)
1698     return "none";
1699   work[0] = vallign[COMBINING_CODE_BASE_Y (code)];
1700   work[1] = hallign[COMBINING_CODE_BASE_X (code)];
1701   off_y = COMBINING_CODE_OFF_Y (code);
1702   off_x = COMBINING_CODE_OFF_X (code);
1703   if (off_y > 0)
1704     sprintf (work + 2, "+%d", off_y);
1705   else if (off_y < 0)
1706     sprintf (work + 2, "%d", off_y);
1707   else if (off_x == 0)
1708     sprintf (work + 2, ".");
1709   p = work + strlen (work);
1710   if (off_x > 0)
1711     sprintf (p, ">%d", off_x);
1712   else if (off_x < 0)
1713     sprintf (p, "<%d", -off_x);
1714   p += strlen (p);
1715   p[0] = vallign[COMBINING_CODE_ADD_Y (code)];
1716   p[1] = hallign[COMBINING_CODE_ADD_X (code)];
1717   p[2] = '\0';
1718   return work;
1719 }
1720
1721 static int
1722 run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
1723 {
1724   MFLTGlyph *g;
1725
1726   if (id >= 0)
1727     {
1728       int i;
1729
1730       /* Direct code (== ctx->code_offset + id) output.
1731          The source is not consumed.  */
1732       if (MDEBUG_FLAG () > 2)
1733         MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "",
1734                        ctx->code_offset + id);
1735       i = (from < to || from == 0) ? from : from - 1;
1736       GDUP (ctx, i);
1737       g = GREF (ctx->out, ctx->out->used - 1);
1738       g->code = ctx->code_offset + id;
1739       SET_ENCODED (g, 0);
1740       SET_MEASURED (g, 0);
1741       if (ctx->combining_code)
1742         SET_COMBINING_CODE (g, ctx, ctx->combining_code);
1743       if (ctx->left_padding)
1744         SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
1745       for (i = from; i < to; i++)
1746         {
1747           MFLTGlyph *tmp = GREF (ctx->in, i);
1748
1749           if (g->from > tmp->from)
1750             g->from = tmp->from;
1751           else if (g->to < tmp->to)
1752             g->to = tmp->to;
1753         }
1754       UPDATE_CLUSTER_RANGE (ctx, g);
1755       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1756       if (MDEBUG_FLAG () > 2)
1757         MDEBUG_PRINT (")");
1758       return (from);
1759     }
1760
1761   if (id <= CMD_ID_OFFSET_INDEX)
1762     {
1763       int idx = CMD_ID_TO_INDEX (id);
1764       FontLayoutCmd *cmd;
1765
1766       if (idx >= ctx->stage->used)
1767         MERROR (MERROR_DRAW, -1);
1768       cmd = ctx->stage->cmds + idx;
1769       if (cmd->type == FontLayoutCmdTypeRule)
1770         to = run_rule (depth, &cmd->body.rule, from, to, ctx);
1771       else if (cmd->type == FontLayoutCmdTypeCond)
1772         to = run_cond (depth, &cmd->body.cond, from, to, ctx);
1773       else if (cmd->type == FontLayoutCmdTypeOTF)
1774         to = run_otf (depth, &cmd->body.otf, from, to, ctx);
1775       return to;
1776     }
1777
1778   if (id <= CMD_ID_OFFSET_COMBINING)
1779     {
1780       ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1781       if (MDEBUG_FLAG () > 2)
1782         MDEBUG_PRINT3 ("\n [FLT] %*s(CMB %s)", depth, "",
1783                        dump_combining_code (ctx->combining_code));
1784       return from;
1785     }
1786
1787   switch (id)
1788     {
1789     case CMD_ID_COPY:
1790       {
1791         if (from >= to)
1792           return from;
1793         GDUP (ctx, from);
1794         g = GREF (ctx->out, ctx->out->used - 1);
1795         if (ctx->combining_code)
1796           SET_COMBINING_CODE (g, ctx, ctx->combining_code);
1797         if (ctx->left_padding)
1798           SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
1799         UPDATE_CLUSTER_RANGE (ctx, g);
1800         if (MDEBUG_FLAG () > 2)
1801           {
1802             if (g->c < 0)
1803               MDEBUG_PRINT2 ("\n [FLT] %*s(COPY |)", depth, "");
1804             else
1805               MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g->code);
1806           }
1807         ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1808         return (from + 1);
1809       }
1810
1811     case CMD_ID_CLUSTER_BEGIN:
1812       if (ctx->cluster_begin_idx < 0)
1813         {
1814           if (MDEBUG_FLAG () > 2)
1815             MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "",
1816                            GREF (ctx->in, from)->from);
1817           ctx->cluster_begin_idx = ctx->out->used;
1818           ctx->cluster_begin_pos = GREF (ctx->in, from)->from;
1819           ctx->cluster_end_pos = GREF (ctx->in, from)->to;
1820         }
1821       return from;
1822
1823     case CMD_ID_CLUSTER_END:
1824       if (ctx->cluster_begin_idx >= 0
1825           && ctx->cluster_begin_idx < ctx->out->used)
1826         {
1827           int i;
1828
1829           if (MDEBUG_FLAG () > 2)
1830             MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos + 1);
1831           for (i = ctx->cluster_begin_idx; i < ctx->out->used; i++)
1832             {
1833               GREF (ctx->out, i)->from = ctx->cluster_begin_pos;
1834               GREF (ctx->out, i)->to = ctx->cluster_end_pos;
1835             }
1836           ctx->cluster_begin_idx = -1;
1837         }
1838       return from;
1839
1840     case CMD_ID_SEPARATOR:
1841       {
1842         int i;
1843
1844         i = from < to ? from : from - 1;
1845         GDUP (ctx, i);
1846         g = GREF (ctx->out, ctx->out->used - 1);
1847         g->c = -1, g->code = 0;
1848         g->xadv = g->yadv = 0;
1849         SET_ENCODED (g, 0);
1850         SET_MEASURED (g, 0);
1851         return from;
1852       }
1853
1854     case CMD_ID_LEFT_PADDING:
1855       if (MDEBUG_FLAG () > 2)
1856         MDEBUG_PRINT2 ("\n [FLT] %*s[", depth, "");
1857       ctx->left_padding = 1;
1858       return from;
1859
1860     case CMD_ID_RIGHT_PADDING:
1861       if (ctx->out->used > 0)
1862         {
1863           if (MDEBUG_FLAG () > 2)
1864             MDEBUG_PRINT2 ("\n [FLT] %*s]", depth, "");
1865           g = GREF (ctx->out, ctx->out->used - 1);
1866           SET_RIGHT_PADDING (g, ctx, RightPaddingMask);
1867         }
1868       return from;
1869     }
1870
1871   MERROR (MERROR_DRAW, -1);
1872 }
1873
1874 static int
1875 run_stages (MFLTGlyphString *gstring, int from, int to,
1876             MFLT *flt, FontLayoutContext *ctx)
1877 {
1878   MFLTGlyphString buf, *temp;
1879   int stage_idx = 0;
1880   int orig_from = from, orig_to = to;
1881   int from_pos, to_pos, len;
1882   int i, j;
1883   MFLTGlyph *g;
1884   MPlist *stages = flt->stages;
1885
1886   from_pos = GREF (ctx->in, from)->from;
1887   to_pos = GREF (ctx->in, to - 1)->to;
1888   len = to_pos - from_pos;
1889
1890   buf = *(ctx->in);
1891   buf.glyphs = NULL;
1892   GINIT (ctx->out, ctx->out->allocated);
1893   ctx->encoded = alloca (ctx->out->allocated);
1894   if (! ctx->out->glyphs || ! ctx->encoded)
1895     return -1;
1896
1897   for (stage_idx = 0; 1; stage_idx++)
1898     {
1899       MCharTable *table;
1900       int result;
1901
1902       ctx->stage = (FontLayoutStage *) MPLIST_VAL (stages);
1903       table = ctx->stage->category;
1904       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1905       for (i = from; i < to; i++)
1906         {
1907           MFLTGlyph *g = GREF (ctx->in, i);
1908           char enc = (GET_ENCODED (g)
1909                       ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
1910                       : g->code
1911                       ? (int) mchartable_lookup (table, g->code)
1912                       : ' ');
1913
1914           ctx->encoded[i] = enc;
1915           if (! enc && stage_idx == 0)
1916             {
1917               to = i;
1918               break;
1919             }
1920         }
1921       ctx->encoded[i] = '\0';
1922       ctx->match_indices[0] = from;
1923       ctx->match_indices[1] = to;
1924       for (i = 2; i < NMATCH; i++)
1925         ctx->match_indices[i] = -1;
1926
1927       if (MDEBUG_FLAG () > 2)
1928         {
1929           MDEBUG_PRINT2 ("\n [FLT]   (STAGE %d \"%s\"", stage_idx,
1930                          ctx->encoded + from);
1931           MDEBUG_PRINT (" (");
1932           for (i = from; i < to; i++)
1933             {
1934               g = GREF (ctx->in, i);
1935               if (g->c == -1)
1936                 MDEBUG_PRINT2 ("%*s|", (i > 0), "");
1937               else
1938                 MDEBUG_PRINT3 ("%*s%04X", (i > 0), "", GREF (ctx->in, i)->code);
1939             }
1940           MDEBUG_PRINT (")");
1941         }
1942       result = run_command (4, INDEX_TO_CMD_ID (0), from, to, ctx);
1943       if (MDEBUG_FLAG () > 2)
1944         MDEBUG_PRINT (")");
1945       if (result < 0)
1946         return result;
1947
1948       stages = MPLIST_NEXT (stages);
1949       /* If this is the last stage, break the loop. */
1950       if (MPLIST_TAIL_P (stages))
1951         break;
1952
1953       /* Otherwise, prepare for the next stage.   */
1954       temp = ctx->in;
1955       ctx->in = ctx->out;
1956       if (buf.glyphs)
1957         ctx->out = temp;
1958       else
1959         {
1960           GINIT (&buf, ctx->out->allocated);
1961           ctx->out = &buf;
1962         }
1963       ctx->out->used = 0;
1964
1965       from = 0;
1966       to = ctx->in->used;
1967     }
1968
1969   if (ctx->out->used > 0)
1970     {
1971       int *g_indices;
1972       int x_ppem = ctx->font->x_ppem << 6, y_ppem = ctx->font->y_ppem << 6;
1973
1974       /* Remove separator glyphs.  */
1975       for (i = 0; i < ctx->out->used;)
1976         {
1977           g = GREF (ctx->out, i);
1978           if (g->c < 0)
1979             GREPLACE (NULL, 0, 0, ctx->out, i, i + 1);
1980           else
1981             i++;
1982         }
1983
1984       /* Get actual glyph IDs of glyphs.  */
1985       ctx->font->get_glyph_id (ctx->font, ctx->out, 0, ctx->out->used);
1986
1987       /* Check if all characters in the range are covered by some
1988          glyph(s).  If not, change <from> and <to> of glyphs to cover
1989          uncovered characters.  */
1990       g_indices = alloca (sizeof (int) * len);
1991       if (! g_indices)
1992         return -1;
1993       for (i = 0; i < len; i++) g_indices[i] = -1;
1994       for (i = 0; i < ctx->out->used; i++)
1995         {
1996           int pos;
1997
1998           g = GREF (ctx->out, i);
1999           for (pos = g->from; pos <= g->to; pos++)
2000             if (g_indices[pos - orig_from] < 0)
2001               g_indices[pos - orig_from] = i;
2002         }
2003       for (i = 0; i < len; i++)
2004         if (g_indices[i] < 0)
2005           {
2006             if (i == 0)
2007               {
2008                 int this_from;
2009
2010                 for (i++; i < len && g_indices[i] < 0; i++);
2011                 j = g_indices[i];
2012                 g = GREF (ctx->out, j);
2013                 this_from = g->from;
2014                 do {
2015                   g->from = orig_from + i;
2016                 } while (++j < ctx->out->used
2017                          && (g = GREF (ctx->out, j))
2018                          && g->from == this_from);
2019               }
2020             else
2021               {
2022                 int this_to;
2023
2024                 j = g_indices[i - 1];
2025                 g = GREF (ctx->out, j);
2026                 this_to = g->to;
2027                 do {
2028                   g->to = orig_from + i + 1;
2029                 } while (--j >= 0
2030                          && (g = GREF (ctx->out, j))
2031                          && g->to == this_to);
2032               }
2033           }
2034
2035       ctx->font->get_metrics (ctx->font, ctx->out, 0, ctx->out->used);
2036
2037       /* Handle combining.  */
2038       if (ctx->check_mask & CombiningCodeMask)
2039         {
2040           MFLTGlyph *base = GREF (ctx->out, 0);
2041           int base_height = base->ascent + base->descent;
2042           int combining_code;
2043
2044           for (i = 1; i < ctx->out->used; i++)
2045             {
2046               if ((g = GREF (ctx->out, i))
2047                   && (combining_code = GET_COMBINING_CODE (g)))
2048                 {
2049                   int height = g->ascent + g->descent;
2050                   int base_x, base_y, add_x, add_y, off_x, off_y;
2051
2052                   if (base->from > g->from)
2053                     base->from = g->from;
2054                   else if (base->to < g->to)
2055                     base->to = g->to;
2056                 
2057                   base_x = COMBINING_CODE_BASE_X (combining_code);
2058                   base_y = COMBINING_CODE_BASE_Y (combining_code);
2059                   add_x = COMBINING_CODE_ADD_X (combining_code);
2060                   add_y = COMBINING_CODE_ADD_Y (combining_code);
2061                   off_x = COMBINING_CODE_OFF_X (combining_code);
2062                   off_y = COMBINING_CODE_OFF_Y (combining_code);
2063
2064                   g->xoff = ((base->xadv * base_x - g->xadv * add_x) / 2
2065                              + x_ppem * off_x / 100 - base->xadv);
2066                   if (base_y < 3)
2067                     g->yoff = base_height * base_y / 2 - base->ascent;
2068                   else
2069                     g->yoff = 0;
2070                   if (add_y < 3)
2071                     g->yoff -= height * add_y / 2 - g->ascent;
2072                   g->yoff -= y_ppem * off_y / 100;
2073                   if (base->lbearing > base->xadv + g->lbearing + g->xoff)
2074                     base->lbearing = base->xadv + g->lbearing + g->xoff;
2075                   if (base->rbearing < base->xadv + g->xadv + g->xoff)
2076                     base->rbearing = base->xadv + g->xadv + g->xoff;
2077                   if (base->ascent < g->ascent - g->yoff)
2078                     base->ascent = g->ascent - g->yoff;
2079                   if (base->descent < g->descent - g->yoff)
2080                     base->descent = g->descent - g->yoff;
2081                   g->xadv = g->yadv = 0;
2082                   if (GET_RIGHT_PADDING (g))
2083                     SET_RIGHT_PADDING (base, ctx, RightPaddingMask);
2084                   g->adjusted = 1;
2085                 }
2086               else
2087                 {
2088                   base = g;
2089                   base_height = g->ascent + g->descent;
2090                 }
2091             }
2092         }
2093
2094       /* Handle padding */
2095       if (ctx->check_mask & (LeftPaddingMask | RightPaddingMask))
2096         for (i = 0; i < ctx->out->used; i++)
2097           {
2098             g = GREF (ctx->out, i);
2099             if (! GET_COMBINING_CODE (g))
2100               {
2101                 if (GET_RIGHT_PADDING (g) && g->rbearing > g->xadv)
2102                   {
2103                     g->xadv = g->rbearing;
2104                     g->adjusted = 1;
2105                   }
2106                 if (GET_LEFT_PADDING (g) && g->lbearing < 0)
2107                   {
2108                     g->xoff += - g->lbearing;
2109                     g->xadv += - g->lbearing;
2110                     g->rbearing += - g->lbearing;
2111                     g->lbearing = 0;
2112                     g->adjusted = 1;
2113                   }
2114               }
2115           }
2116     }
2117
2118   GREPLACE (ctx->out, 0, ctx->out->used, gstring, orig_from, orig_to);
2119   to = orig_from + ctx->out->used;
2120   return to;
2121 }
2122
2123 static void
2124 setup_combining_coverage (int from, int to, void *val, void *arg)
2125 {
2126   int combining_class = (int) val;
2127   int category = 0;
2128
2129   if (combining_class < 200)
2130     category = 'a';
2131   else if (combining_class <= 204)
2132     {
2133       if ((combining_class % 2) == 0)
2134         category = "bcd"[(combining_class - 200) / 2];
2135     }
2136   else if (combining_class <= 232)
2137     {
2138       if ((combining_class % 2) == 0)
2139         category = "efghijklmnopq"[(combining_class - 208) / 2];
2140     }
2141   else if (combining_class == 233)
2142     category = 'r';
2143   else if (combining_class == 234)
2144     category = 's';
2145   else if (combining_class == 240)
2146     category = 't';
2147   mchartable_set_range ((MCharTable *) arg, from, to, (void *) category);
2148 }
2149
2150 static void
2151 setup_combining_flt (MFLT *flt)
2152 {
2153   MSymbol type;
2154   MCharTable *combininig_class_table
2155     = mchar_get_prop_table (Mcombining_class, &type);
2156
2157   mchartable_set_range (flt->coverage, 0, 0x10FFFF, (void *) 'u');
2158   if (combininig_class_table)
2159     mchartable_map (combininig_class_table, (void *) 0,
2160                     setup_combining_coverage, flt->coverage);
2161 }
2162
2163 #define CHECK_FLT_STAGES(flt) ((flt)->stages || load_flt (flt, NULL) == 0)
2164
2165 \f
2166 /* Internal API */
2167
2168 int m17n__flt_initialized;
2169
2170 \f
2171 /* External API */
2172
2173 /* The following two are actually not exposed to a user but concealed
2174    by the macro M17N_INIT (). */
2175
2176 void
2177 m17n_init_flt (void)
2178 {
2179   int mdebug_flag = MDEBUG_INIT;
2180
2181   merror_code = MERROR_NONE;
2182   if (m17n__flt_initialized++)
2183     return;
2184   m17n_init_core ();
2185   if (merror_code != MERROR_NONE)
2186     {
2187       m17n__flt_initialized--;
2188       return;
2189     }
2190
2191   MDEBUG_PUSH_TIME ();
2192
2193   Mcond = msymbol ("cond");
2194   Mrange = msymbol ("range");
2195   Mfont = msymbol ("font");
2196   Mlayouter = msymbol ("layouter");
2197   Mcombining = msymbol ("combining");
2198   Mfont_facility = msymbol ("font-facility");
2199   Mgenerator = msymbol ("generator");
2200   Mend = msymbol ("end");
2201
2202   MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the flt modules."));
2203   MDEBUG_POP_TIME ();
2204 }
2205
2206 void
2207 m17n_fini_flt (void)
2208 {
2209   int mdebug_flag = MDEBUG_FINI;
2210
2211   if (m17n__flt_initialized == 0
2212       || --m17n__flt_initialized > 0)
2213     return;
2214
2215   MDEBUG_PUSH_TIME ();
2216   free_flt_list ();
2217   MDEBUG_PRINT_TIME ("FINI", (stderr, " to finalize the flt modules."));
2218   MDEBUG_POP_TIME ();
2219   m17n_fini_core ();
2220 }
2221
2222 /*** @} */ 
2223 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
2224
2225 /*** @addtogroup m17nFLT */
2226 /*** @{ */
2227 /*=*/
2228
2229 /*=*/
2230 /***en
2231     @brief Return a FLT object whose name is NAME.
2232
2233     The mflt_get () function returns a FLT object whose name is $NAME.
2234
2235     @return
2236     If the operation was successfully, mflt_get () returns a pointer
2237     to a FLT object.  Otherwise, it returns @c NULL.  */
2238
2239 MFLT *
2240 mflt_get (MSymbol name)
2241 {
2242   MFLT *flt;
2243
2244   if (! flt_list && list_flt () < 0)
2245     return NULL;
2246   flt = mplist_get (flt_list, name);
2247   if (! flt || ! CHECK_FLT_STAGES (flt))
2248     return NULL;
2249   if (flt->name == Mcombining
2250       && ! mchartable_lookup (flt->coverage, 0))
2251     setup_combining_flt (flt);
2252
2253   return flt;
2254 }
2255
2256 /*=*/
2257 /***en
2258     @brief Find a FLT suitable for a specified character and font.
2259
2260     The mflt_find () function returns the most appropriate FLT for
2261     rendering the character $C by font $FONT.
2262
2263     @return
2264     If the operation was successfully, mflt_find () returns a pointer
2265     to a FLT object.  Otherwise, it returns @c NULL.  */
2266
2267 MFLT *
2268 mflt_find (int c, MFLTFont *font)
2269 {
2270   MPlist *plist;
2271   MFLT *flt;
2272   static MSymbol unicode_bmp = NULL, unicode_full = NULL;
2273
2274   if (! unicode_bmp)
2275     {
2276       unicode_bmp = msymbol ("unicode-bmp");
2277       unicode_full = msymbol ("unicode-full");
2278     }
2279
2280   if (! flt_list && list_flt () < 0)
2281     return NULL;
2282   if (font)
2283     {
2284       MFLT *best = NULL;
2285
2286       MPLIST_DO (plist, flt_list)
2287         {
2288           flt = MPLIST_VAL (plist);
2289           if (flt->registry != unicode_bmp
2290               && flt->registry != unicode_full)
2291             continue;
2292           if (flt->family && flt->family != font->family)
2293             continue;
2294           if (c >= 0
2295               && ! mchartable_lookup (flt->coverage, c))
2296             continue;
2297           if (flt->otf.sym)
2298             {
2299               MFLTOtfSpec *spec = &flt->otf;
2300
2301               if (! font->check_otf)
2302                 {
2303                   if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
2304                       || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
2305                     continue;
2306                 }
2307               else if (! font->check_otf (font, spec))
2308                 continue;
2309               return flt;
2310             }
2311           best = flt;
2312         }
2313       return best;
2314     }
2315   if (c >= 0)
2316     {
2317       MPLIST_DO (plist, flt_list)
2318         {
2319           flt = MPLIST_VAL (plist);
2320           if (mchartable_lookup (flt->coverage, c))
2321             return flt;
2322         }
2323     }
2324   return NULL;
2325 }
2326
2327 /*=*/
2328 /***en
2329     @brief Return a name of a FLT.
2330
2331     The mflt_name () function returns the name of $FLT.  */
2332
2333 const char *
2334 mflt_name (MFLT *flt)
2335 {
2336   return MSYMBOL_NAME (flt->name);
2337 }
2338
2339 /*=*/
2340 /***en
2341     @brief Return a coverage of a FLT.
2342
2343     The mflt_coverage () function returns a char-table that contains
2344     nonzero value for characters supported by $FLT.  */
2345
2346 MCharTable *
2347 mflt_coverage (MFLT *flt)
2348 {
2349   return flt->coverage;
2350 }
2351
2352 /*=*/
2353 /***en
2354     @brief Layout characters by Font Layout Table.
2355
2356     The mflt_run () function layout characters in $GSTRING between
2357     $FROM (inclusive) and $TO (exclusive) by $FONT.  If $FLT is
2358     nonzero, it is used for all the charaters.  Otherwise, appropriate
2359     FLTs are automatically chosen.
2360
2361     @retval >=0
2362     The operation was successful.  The value is an index to the
2363     $GSTRING->glyphs which was previously indexed by $TO.
2364
2365     @retval -2
2366     $GSTRING->glyphs is too short to store the result.  A caller can
2367     call this fucntion again with the larger $GSTRING->glyphs.
2368
2369     @retval -1
2370     Some other error occurred.  */
2371
2372 int
2373 mflt_run (MFLTGlyphString *gstring, int from, int to,
2374           MFLTFont *font, MFLT *flt)
2375 {
2376   FontLayoutContext ctx;
2377   int match_indices[NMATCH];
2378   MFLTGlyph *g;
2379   MFLTGlyphString out;
2380   int auto_flt = ! flt;
2381   int c, i, j, k;
2382   int this_from, this_to;
2383
2384   out = *gstring;
2385   out.glyphs = NULL;
2386   /* This is usually sufficient, but if not, we retry with the larger
2387      values at most 3 times.  This value is also used for the
2388      allocating size of ctx.encoded.  */
2389   out.allocated = (to - from) * 4;
2390
2391   for (i = from; i < to; i++)
2392     {
2393       g = GREF (gstring, i);
2394       c = g->c;
2395       memset (g, 0, sizeof (MFLTGlyph));
2396       g->code = g->c = c;
2397       g->from = g->to = i;
2398     }
2399
2400   for (this_from = from; this_from < to;)
2401     {
2402       if (! auto_flt)
2403         {
2404           for (this_to = this_from; this_to < to; this_to++)
2405             if (mchartable_lookup (flt->coverage, GREF (gstring, this_to)->c))
2406               break;
2407         }
2408       else
2409         {
2410           if (! flt_list && list_flt () < 0)
2411             {
2412               font->get_glyph_id (font, gstring, this_from, to);
2413               font->get_metrics (font, gstring, this_from, to);
2414               this_from = to;
2415               break;
2416             }
2417           for (this_to = this_from; this_to < to; this_to++)
2418             {
2419               c = GREF (gstring, this_to)->c;
2420               if (c >= flt_min_coverage && c <= flt_max_coverage)
2421                 break;
2422             }
2423           for (; this_to < to; this_to++)
2424             {
2425               c = GREF (gstring, this_to)->c;
2426               if (font->internal
2427                   && mchartable_lookup (((MFLT *) font->internal)->coverage, c))
2428                 {
2429                   flt = font->internal;
2430                   break;
2431                 }
2432               flt = mflt_find (c, font);
2433               if (flt)
2434                 {
2435                   if (CHECK_FLT_STAGES (flt))
2436                     {
2437                       font->internal = flt;
2438                       break;
2439                     }
2440                 }
2441             }
2442         }
2443
2444       if (this_from < this_to)
2445         {
2446           font->get_glyph_id (font, gstring, this_from, this_to);
2447           font->get_metrics (font, gstring, this_from, this_to);
2448           this_from = this_to;
2449         }
2450       if (this_to == to)
2451         break;
2452
2453       MDEBUG_PRINT1 (" [FLT] (%s", MSYMBOL_NAME (flt->name));
2454
2455       for (; this_to < to; this_to++)
2456         if (! mchartable_lookup (flt->coverage, GREF (gstring, this_to)->c))
2457           break;
2458
2459       if (MDEBUG_FLAG ())
2460         {
2461           if (font->family)
2462             MDEBUG_PRINT1 (" (%s)", MSYMBOL_NAME (font->family));
2463           MDEBUG_PRINT ("\n [FLT]   (SOURCE");
2464           for (i = this_from, j = 0; i < this_to; i++, j++)
2465             {
2466               if (j > 0 && j % 8 == 0)
2467                 MDEBUG_PRINT ("\n [FLT]          ");
2468               MDEBUG_PRINT1 (" %04X", GREF (gstring, i)->c);
2469             }
2470           MDEBUG_PRINT (")");
2471         }
2472
2473       for (i = 0; i < 3; i++)
2474         {
2475           /* Setup CTX.  */
2476           memset (&ctx, 0, sizeof ctx);
2477           ctx.match_indices = match_indices;
2478           ctx.font = font;
2479           ctx.cluster_begin_idx = -1;
2480           ctx.in = gstring;
2481           ctx.out = &out;
2482           j = run_stages (gstring, this_from, this_to, flt, &ctx);
2483           if (j != -2)
2484             break;
2485           out.allocated *= 2;
2486         }
2487
2488       if (j < 0)
2489         return j;
2490
2491       to += j - this_to;
2492       this_to = j;
2493
2494       if (MDEBUG_FLAG ())
2495         {
2496           MDEBUG_PRINT ("\n [FLT]   (RESULT");
2497           if (MDEBUG_FLAG () > 1)
2498             for (i = 0; this_from < this_to; this_from++, i++)
2499               {
2500                 if (i > 0 && i % 4 == 0)
2501                   MDEBUG_PRINT ("\n [FLT]          ");
2502                 g = GREF (gstring, this_from);
2503                 MDEBUG_PRINT4 (" (%04X %d %d %d)",
2504                                g->code, g->xadv, g->xoff, g->yoff);
2505               }
2506           else
2507             for (; this_from < this_to; this_from++)
2508               MDEBUG_PRINT1 (" %04X", GREF (gstring, this_from)->code);
2509           MDEBUG_PRINT ("))\n");
2510         }
2511       this_from = this_to;
2512     }
2513
2514   if (gstring->r2l)
2515     {
2516       int len = to - from;
2517
2518       GINIT (&out, len);
2519       memcpy (((char *) out.glyphs),
2520               ((char *) gstring->glyphs) + gstring->glyph_size * from,
2521               gstring->glyph_size * len);
2522       for (i = from, j = to; i < to;)
2523         {
2524           for (k = i + 1, j--; k < to && GREF (&out, k)->xadv == 0;
2525                k++, j--);
2526           GCPY (&out, i, (k - i), gstring, j);
2527           i = k;
2528         }
2529     }
2530
2531   return to;
2532 }
2533
2534 \f
2535 /* for debugging... */
2536
2537 static void
2538 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
2539 {
2540   char *prefix = (char *) alloca (indent + 1);
2541
2542   memset (prefix, 32, indent);
2543   prefix[indent] = 0;
2544
2545   if (id >= 0)
2546     fprintf (stderr, "0x%02X", id);
2547   else if (id <= CMD_ID_OFFSET_INDEX)
2548     {
2549       int idx = CMD_ID_TO_INDEX (id);
2550       FontLayoutCmd *cmd = stage->cmds + idx;
2551
2552       if (cmd->type == FontLayoutCmdTypeRule)
2553         {
2554           FontLayoutCmdRule *rule = &cmd->body.rule;
2555           int i;
2556
2557           fprintf (stderr, "(rule ");
2558           if (rule->src_type == SRC_REGEX)
2559             fprintf (stderr, "\"%s\"", rule->src.re.pattern);
2560           else if (rule->src_type == SRC_INDEX)
2561             fprintf (stderr, "%d", rule->src.match_idx);
2562           else if (rule->src_type == SRC_SEQ)
2563             fprintf (stderr, "(seq)");
2564           else if (rule->src_type == SRC_RANGE)
2565             fprintf (stderr, "(range)");
2566           else
2567             fprintf (stderr, "(invalid src)");
2568
2569           for (i = 0; i < rule->n_cmds; i++)
2570             {
2571               fprintf (stderr, "\n%s  ", prefix);
2572               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
2573             }
2574           fprintf (stderr, ")");
2575         }
2576       else if (cmd->type == FontLayoutCmdTypeCond)
2577         {
2578           FontLayoutCmdCond *cond = &cmd->body.cond;
2579           int i;
2580
2581           fprintf (stderr, "(cond");
2582           for (i = 0; i < cond->n_cmds; i++)
2583             {
2584               fprintf (stderr, "\n%s  ", prefix);
2585               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
2586             }
2587           fprintf (stderr, ")");
2588         }
2589       else if (cmd->type == FontLayoutCmdTypeOTF)
2590         {
2591           fprintf (stderr, "(otf)");
2592         }
2593       else
2594         fprintf (stderr, "(error-command)");
2595     }
2596   else if (id <= CMD_ID_OFFSET_COMBINING)
2597     fprintf (stderr, "cominging-code");
2598   else
2599     fprintf (stderr, "(predefiend %d)", id);
2600 }
2601
2602 void
2603 mdebug_dump_flt (MFLT *flt, int indent)
2604 {
2605   char *prefix = (char *) alloca (indent + 1);
2606   MPlist *plist;
2607   int stage_idx = 0;
2608
2609   memset (prefix, 32, indent);
2610   prefix[indent] = 0;
2611   fprintf (stderr, "(flt");
2612   MPLIST_DO (plist, flt->stages)
2613     {
2614       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
2615       int i;
2616
2617       fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
2618       for (i = 0; i < stage->used; i++)
2619         {
2620           fprintf (stderr, "\n%s    ", prefix);
2621           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
2622         }
2623       fprintf (stderr, ")");
2624       stage_idx++;
2625     }
2626   fprintf (stderr, ")");
2627 }
2628
2629 /*** @} */
2630
2631 /*
2632  Local Variables:
2633  coding: euc-japan
2634  End:
2635 */