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