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