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