*** empty log message ***
[m17n/m17n-lib.git] / src / font-flt.c
1 /* font-flt.c -- Font Layout Table sub-module.
2    Copyright (C) 2003, 2004
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 #include "config.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <regex.h>
31
32 #include "m17n-gui.h"
33 #include "m17n-misc.h"
34 #include "internal.h"
35 #include "mtext.h"
36 #include "symbol.h"
37 #include "plist.h"
38 #include "internal-flt.h"
39 #include "internal-gui.h"
40 #include "font.h"
41 #include "face.h"
42
43 /* Font Layouter */
44
45 /* Font Layout Table (FLT)
46
47 Predefined terms: SYMBOL, INTEGER, STRING
48
49 FLT ::= '(' STAGE + ')'
50
51 STAGE ::= CATEGORY-TABLE ? FONT-LAYOUT-RULE
52
53 ;; Each STAGE consumes a source (code sequence) and produces another
54 ;; code sequence that is given to the next STAGE as a source.  The
55 ;; source given to the first stage is a sequence of character codes
56 ;; that are assigned category codes by CATEGORY-TABLE.  The output of
57 ;; the last stage is a glyph code sequence given to the renderer.
58
59 CATEGORY-TABLE ::=
60         '(' 'category' CATEGORY-SPEC + ')'
61 CATEGORY-SPEC ::=
62         '(' CODE [ CODE ] CATEGORY ')'
63 CODE ::= INTEGER
64 CATEGORY ::= INTEGER
65 ;; ASCII character codes of alphabet ('A' .. 'Z' 'a' .. 'z').
66 ;; Ex: CATEGORY-TABLE
67 ;; (category
68 ;;   (0x0900 0x097F     ?E)     ; All Devanagari characters
69 ;;   (0x093C            ?N))    ; DEVANAGARI-LETTER NUKTA
70 ;;      Assign the category 'E' to all Devanagari characters but 0x093C,
71 ;;      assign the category 'N' to 0x093C.
72
73 FONT-LAYOUT-RULE ::=
74         '(' 'generator' RULE MACRO-DEF * ')'
75
76 RULE ::= COMMAND | REGEXP-RULE | MATCH-RULE | MAP-RULE
77          | COND-STRUCT | MACRO-NAME
78
79 COMMAND ::=
80         DIRECT-CODE | COMBINING | PREDEFIND-COMMAND | OTF-COMMAND
81
82 DIRECT-CODE ::= INTEGER
83 ;; Always succeed.  Produce the code.  Consume no source.
84
85 PREDEFIND-COMMAND ::=
86         '=' | '*' | '<' | '>' | '|'
87
88 ;; '=': Succeed when the current run contains at least one code.
89 ;; Consume the first code in the current run, and produce it as is.
90
91 ;; '*': If the the previous command succeeded, repeat it until it
92 ;; fails.  
93
94 ;; '<': Produce a special code that indicates the start of grapheme
95 ;; cluster.  Succeed always, consume nothing.
96
97 ;; '>': Produce a special code that indicates the end of grapheme
98 ;; cluster.  Succeed always, consume nothing.
99
100 ;; '|': Produce a special code whose category is ' '.  Succeed always,
101 ;; consume nothing.
102
103 OTF-COMMAND ::=
104         'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]]
105 ;; Run the Open Type Layout Table on the current run.  Succeed always,
106 ;; consume nothing.
107
108 SCRIPT ::= OTF-TAG
109 ;;      OTF's ScriptTag name (four letters) listed at:
110 ;;      <http://www.microsoft.om/typograph/otspec/scripttags.htm>
111 LANGSYS ::= OTF-TAG
112 ;;      OTF's Language System name (four letters) listed at:
113 ;;      <http://www.microsoft.om/typograph/otspec/languagetags.htm>
114
115 GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
116 GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
117 FEATURE ::= OTF-TAG
118 ;;      OTF's Feature name (four letters) listed at:
119 ;;      <http://www.microsoft.om/typograph/otspec/???.htm>
120
121 OTF-TAG ::= PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR
122
123 ;; Ex. OTF-COMMAND
124 ;; 'otf:deva'
125 ;;      Run all features in the default langsys of 'deva' script.
126 ;; 'otf:deva::nukt:haln'
127 ;;      Run all GSUB features, run 'nukt' and 'haln' GPOS features.
128 ;; 'otf:deva:: :'
129 ;;      Run all GSUB features, run no GPOS features.
130
131 REGEXP-RULE ::=
132         '(' REGEXP RULE * ')'
133
134 ;; Succeed if REGXP matches the head of source.  Run RULEs while
135 ;; limiting the source to the matching part.  Consume that part.
136
137 REGEXP ::= STRING
138 ;; Must be composed only from ASCII characters.  'A' - 'Z', 'a' - 'z'
139 ;; correspond to CATEGORY.
140
141 ;; Ex: REGEXP-RULE
142 ;; ("VA?"
143 ;;   < | vowel * | >)
144
145 MATCH-RULE ::=
146         '(' MATCH-IDX RULE * ')'
147
148 ;; Succeed if the previous REGEXP-RULE found a matching part for
149 ;; MATCH-IDX.  Run RULEs while limiting the source to the matching
150 ;; part.  If MATCH-IDX is zero, consume the whole part, else consume
151 ;; nothing.
152
153 MATCH-IDX ::= INTEGER
154 ;; Must be 0..20.
155
156 ;; Ex. MATCH-RULE
157 ;; (2 consonant *)
158
159 MAP-RULE ::=
160         '(' ( SOURCE-SEQ | SOURCE-RANGE ) RULE * ')'
161
162 ;; Succeed if the source matches SOURCE-SEQ or SOURCE-RANGE.  Run
163 ;; RULEs while limiting the source to the matching part.  Consume that
164 ;; part.
165
166 SOURCE-SEQ ::=
167         '(' CODE + ')'
168 SOURCE-RANGE ::=
169         '(' 'range' CODE CODE ')'
170 ;; Ex. MAP-RULE
171 ;; ((0x0915 0x094D)             0x43)
172 ;;      If the source code sequence is 0x0915 0x094D, produce 0x43.
173 ;; ((range 0x0F40 0x0F6A)       0x2221)
174 ;;      If the first source code CODE is in the range 0x0F40..0x0F6A, 
175 ;;      produce (0x2221 + (CODE - 0x0F40)).
176
177 COND-STRUCT ::=
178         '(' 'cond' RULE + ')'
179
180 ;; Try each rule in sequence until one succeeds.  Succeed if one
181 ;; succeeds.  Consume nothing.
182
183 ;; Ex. COND-STRUCT
184 ;; (cond
185 ;;  ((0x0915 0x094D)            0x43)
186 ;;  ((range 0x0F40 0x0F6A)      0x2221)
187 ;;  = )
188
189 COMBINING ::= 'V''H''O''V''H'
190 V ::= ( 't' | 'c' | 'b' | 'B' )
191 H ::= ( 'l' | 'c' | 'r' )
192 O ::= ( '.' | XOFF | YOFF | XOFF YOFF )
193 XOFF ::= '<'INTEGER | '>'INTEGER 
194 YOFF ::= '+'INTEGER | '-'INTEGER
195 ;; INTEGER must be integer 0..127
196
197 ;; VH pair indicates 12 reference points of a glyph as below:
198 ;;
199 ;;   0----1----2 <---- ascent    0:tl (top-left)
200 ;;   |         |                 1:tc (top-center)
201 ;;   |         |                 2:tr (top-right)
202 ;;   |         |                 3:Bl (base-left)
203 ;;   9   10   11 <---- center    4:Bc (base-center)
204 ;;   |         |                 5:Br (base-right)
205 ;; --3----4----5-- <-- baseline  6:bl (bottom-left)
206 ;;   |         |                 7:bc (bottom-center)
207 ;;   6----7----8 <---- descent   8:br (bottom-right)
208 ;;                               9:cl (center-left)
209 ;;   |    |    |                10:cc (center-center)
210 ;; left center right            11:cr (center-right)
211 ;;
212 ;; Ex. COMBINING
213 ;; 'tc.bc':
214 ;;      Align top-left point of the previous glyph and bottom-center
215 ;;      point of the current glyph.
216 ;; 'Bl<20-10Br'
217 ;;      Align 20% left and 10% below of base-left point of the previous
218 ;;      glyph and base-right point of the current glyph.
219
220 MACRO-DEF ::=
221         '(' MACRO-NAME RULE + ')'
222 MACRO-NAME ::= SYMBOL
223
224 */
225
226 static int mdebug_flag = MDEBUG_FONT_FLT;
227
228 MSymbol Mlayouter;
229
230 static MPlist *flt_list;
231
232 /* Command ID:
233                  0 ...          : direct code
234                    -1           : invalid
235              -0x0F .. -2        : builtin commands
236         -0x100000F .. -0x10     : combining code
237                   ... -0x1000010: index to FontLayoutStage->cmds
238  */
239
240 #define INVALID_CMD_ID -1
241 #define CMD_ID_OFFSET_BUILTIN   -2
242 #define CMD_ID_OFFSET_COMBINING -0x10
243 #define CMD_ID_OFFSET_INDEX     -0x1000010
244
245 /* Builtin commands. */
246 #define CMD_ID_COPY             -2 /* '=' */
247 #define CMD_ID_REPEAT           -3 /* '*' */
248 #define CMD_ID_CLUSTER_BEGIN    -4 /* '<' */
249 #define CMD_ID_CLUSTER_END      -5 /* '>' */
250 #define CMD_ID_SEPARATOR        -6 /* '|' */
251 #define CMD_ID_LEFT_PADDING     -7 /* '[' */
252 #define CMD_ID_RIGHT_PADDING    -8 /* ']' */
253
254 #define CMD_ID_TO_COMBINING_CODE(id) (CMD_ID_OFFSET_COMBINING - (id))
255 #define COMBINING_CODE_TO_CMD_ID(code) (CMD_ID_OFFSET_COMBINING - (code))
256
257 #define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id))
258 #define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx))
259
260 static MSymbol Mcond, Mrange, Mexist;
261
262 #define GLYPH_CODE_P(code)      \
263   ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX)
264
265 #define GLYPH_CODE_INDEX(code) ((code) - GLYPH_CODE_MIN)
266
267 #define UPDATE_CLUSTER_RANGE(ctx, g)            \
268   do {                                          \
269     if ((ctx)->cluster_begin_idx)               \
270       {                                         \
271         if (ctx->cluster_begin_pos > (g).pos)   \
272           ctx->cluster_begin_pos = (g).pos;     \
273         if (ctx->cluster_end_pos < (g).to)      \
274           ctx->cluster_end_pos = (g).to;        \
275       }                                         \
276   } while (0);
277
278 enum FontLayoutCmdRuleSrcType
279   {
280     SRC_REGEX,
281     SRC_INDEX,
282     SRC_SEQ,
283     SRC_RANGE,
284     SRC_EXIST
285   };
286
287 typedef struct
288 {
289   enum FontLayoutCmdRuleSrcType src_type;
290   union {
291     struct {
292       char *pattern;
293       regex_t preg;
294     } re;
295     int match_idx;
296     struct {
297       int n_codes;
298       int *codes;
299     } seq;
300     struct {
301       int from, to;
302     } range;
303     struct {
304       int c;
305     } exist;
306   } src;
307
308   int n_cmds;
309   int *cmd_ids;
310 } FontLayoutCmdRule;
311
312 typedef struct
313 {
314   /* Beginning and end indices of series of SEQ commands.  */
315   int seq_beg, seq_end;
316   /* Range of the first character appears in the above series.  */
317   int seq_from, seq_to;
318
319   int n_cmds;
320   int *cmd_ids;
321 } FontLayoutCmdCond;
322
323 enum FontLayoutCmdType
324   {
325     FontLayoutCmdTypeRule,
326     FontLayoutCmdTypeCond,
327     FontLayoutCmdTypeOTF,
328     FontLayoutCmdTypeMAX
329   };
330
331 typedef struct
332 {
333   enum FontLayoutCmdType type;
334   union {
335     FontLayoutCmdRule rule;
336     FontLayoutCmdCond cond;
337     MFontCapability *otf;
338   } body;
339 } FontLayoutCmd;
340
341 typedef struct 
342 {
343   MCharTable *category;
344   int size, inc, used;
345   FontLayoutCmd *cmds;
346 } FontLayoutStage;
347
348 typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */
349
350 /* Font layout table loader */
351
352 /* Load a category table from PLIST.  PLIST has this form:
353       PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
354 */
355
356 static MCharTable *
357 load_category_table (MPlist *plist)
358 {
359   MCharTable *table;
360
361   table = mchartable (Minteger, (void *) 0);
362
363   MPLIST_DO (plist, plist)
364     {
365       MPlist *elt;
366       int from, to, category_code;
367
368       if (! MPLIST_PLIST (plist))
369         MERROR (MERROR_FONT, NULL);
370       elt = MPLIST_PLIST (plist);
371       if (! MPLIST_INTEGER_P (elt))
372         MERROR (MERROR_FONT, NULL);
373       from = MPLIST_INTEGER (elt);
374       elt = MPLIST_NEXT (elt);
375       if (! MPLIST_INTEGER_P (elt))
376         MERROR (MERROR_FONT, NULL);
377       to = MPLIST_INTEGER (elt);
378       elt = MPLIST_NEXT (elt);
379       if (MPLIST_TAIL_P (elt))
380         {
381           category_code = to;
382           to = from;
383         }
384       else
385         {
386           if (! MPLIST_INTEGER_P (elt))
387             MERROR (MERROR_FONT, NULL);
388           category_code = MPLIST_INTEGER (elt);
389         }
390       if (! isalnum (category_code))
391         MERROR (MERROR_FONT, NULL);
392
393       if (from == to)
394         mchartable_set (table, from, (void *) category_code);
395       else
396         mchartable_set_range (table, from, to, (void *) category_code);
397     }
398
399   return table;
400 }
401
402
403 /* Parse OTF command name NAME and store the result in CMD.
404    NAME has this form:
405         :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
406    where GSUB-FEATURES and GPOS-FEATURES have this form:
407         [FEATURE[,FEATURE]*] | ' '  */
408
409 static int
410 load_otf_command (FontLayoutCmd *cmd, MSymbol sym)
411 {
412   char *name = MSYMBOL_NAME (sym);
413
414   if (name[0] != ':')
415     {
416       /* This is old format of "otf:...".  Change it to ":otf=...".  */
417       char *str = alloca (MSYMBOL_NAMELEN (sym) + 2);
418
419       sprintf (str, ":otf=");
420       strcat (str, name + 4);
421       sym = msymbol (str);
422     }
423
424   cmd->body.otf = mfont__get_capability (sym);
425   if (! cmd->body.otf)
426     return -1;
427   if (cmd->body.otf->script == Mnil)
428     {
429       cmd->body.otf = NULL;
430       return -1;
431     }
432   M17N_OBJECT_REF (cmd->body.otf);
433   cmd->type = FontLayoutCmdTypeOTF;
434   return 0;
435 }
436
437
438 /* Read a decimal number from STR preceded by one of "+-><".  '+' and
439    '>' means a plus sign, '-' and '<' means a minus sign.  If the
440    number is greater than 127, limit it to 127.  */
441
442 static int
443 read_decimal_number (char **str)
444 {
445   char *p = *str;
446   int sign = (*p == '-' || *p == '<') ? -1 : 1;
447   int n = 0;
448
449   p++;
450   while (*p >= '0' && *p <= '9')
451     n = n * 10 + *p++ - '0';
452   *str = p;
453   if (n == 0)
454     n = 5;
455   return (n < 127 ? n * sign : 127 * sign);
456 }
457
458
459 /* Read a horizontal and vertical combining positions from STR, and
460    store them in the place pointed by X and Y.  The horizontal
461    position left, center, and right are represented by 0, 1, and 2
462    respectively.  The vertical position top, center, bottom, and base
463    are represented by 0, 1, 2, and 3 respectively.  If successfully
464    read, return 0, else return -1.  */
465
466 static int
467 read_combining_position (char *str, int *x, int *y)
468 {
469   int c = *str++;
470   int i;
471
472   /* Vertical position comes first.  */
473   for (i = 0; i < 4; i++)
474     if (c == "tcbB"[i])
475       {
476         *y = i;
477         break;
478       }
479   if (i == 4)
480     return -1;
481   c = *str;
482   /* Then comse horizontal position.  */
483   for (i = 0; i < 3; i++)
484     if (c == "lcr"[i])
485       {
486         *x = i;
487         return 0;
488       }
489   return -1;
490 }
491
492
493 /* Return a combining code corresponding to SYM.  */
494
495 static int
496 get_combining_command (MSymbol sym)
497 {
498   char *str = msymbol_name (sym);
499   int base_x, base_y, add_x, add_y, off_x, off_y;
500   int c;
501
502   if (read_combining_position (str, &base_x, &base_y) < 0)
503     return 0;
504   str += 2;
505   c = *str;
506   if (c == '.')
507     {
508       off_x = off_y = 128;
509       str++;
510     }
511   else
512     {
513       if (c == '+' || c == '-')
514         {
515           off_y = read_decimal_number (&str) + 128;
516           c = *str;
517         }
518       else
519         off_y = 128;
520       if (c == '<' || c == '>')
521         off_x = read_decimal_number (&str) + 128;
522       else
523         off_x = 128;
524     }
525   if (read_combining_position (str, &add_x, &add_y) < 0)
526     return 0;
527
528   c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
529   return (COMBINING_CODE_TO_CMD_ID (c));
530 }
531
532
533 /* Load a command from PLIST into STAGE, and return that
534    identification number.  If ID is not INVALID_CMD_ID, that means we
535    are loading a top level command or a macro.  In that case, use ID
536    as the identification number of the command.  Otherwise, generate a
537    new id number for the command.  MACROS is a list of raw macros.  */
538
539 static int
540 load_command (FontLayoutStage *stage, MPlist *plist,
541               MPlist *macros, int id)
542 {
543   int i;
544
545   if (MPLIST_INTEGER_P (plist))
546     {
547       int code = MPLIST_INTEGER (plist);
548
549       if (code < 0)
550         MERROR (MERROR_DRAW, INVALID_CMD_ID);
551       return code;
552     }
553   else if (MPLIST_PLIST_P (plist))
554     {
555       /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
556                    | ( ( INTEGER INTEGER ) ... )
557                    | ( ( range INTEGER INTEGER ) ... )  */
558       MPlist *elt = MPLIST_PLIST (plist);
559       int len = MPLIST_LENGTH (elt) - 1;
560       FontLayoutCmd *cmd;
561
562       if (id == INVALID_CMD_ID)
563         {
564           FontLayoutCmd dummy;
565           id = INDEX_TO_CMD_ID (stage->used);
566           MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
567         }
568       cmd = stage->cmds + CMD_ID_TO_INDEX (id);
569
570       if (MPLIST_SYMBOL_P (elt))
571         {
572           FontLayoutCmdCond *cond;
573
574           if (MPLIST_SYMBOL (elt) != Mcond)
575             MERROR (MERROR_DRAW, INVALID_CMD_ID);
576           elt = MPLIST_NEXT (elt);
577           cmd->type = FontLayoutCmdTypeCond;
578           cond = &cmd->body.cond;
579           cond->seq_beg = cond->seq_end = -1;
580           cond->seq_from = cond->seq_to = 0;
581           cond->n_cmds = len;
582           MTABLE_CALLOC (cond->cmd_ids, len, MERROR_DRAW);
583           for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
584             {
585               int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
586
587               if (this_id == INVALID_CMD_ID)
588                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
589               /* The above load_command may relocate stage->cmds.  */
590               cmd = stage->cmds + CMD_ID_TO_INDEX (id);
591               cond = &cmd->body.cond;
592               cond->cmd_ids[i] = this_id;
593               if (this_id <= CMD_ID_OFFSET_INDEX)
594                 {
595                   FontLayoutCmd *this_cmd
596                     = stage->cmds + CMD_ID_TO_INDEX (this_id);
597
598                   if (this_cmd->type == FontLayoutCmdTypeRule
599                       && this_cmd->body.rule.src_type == SRC_SEQ)
600                     {
601                       int first_char = this_cmd->body.rule.src.seq.codes[0];
602
603                       if (cond->seq_beg < 0)
604                         {
605                           /* The first SEQ command.  */
606                           cond->seq_beg = i;
607                           cond->seq_from = cond->seq_to = first_char;
608                         }
609                       else if (cond->seq_end < 0)
610                         {
611                           /* The following SEQ command.  */
612                           if (cond->seq_from > first_char)
613                             cond->seq_from = first_char;
614                           else if (cond->seq_to < first_char)
615                             cond->seq_to = first_char;
616                         }
617                     }
618                   else
619                     {
620                       if (cond->seq_beg >= 0 && cond->seq_end < 0)
621                         /* The previous one is the last SEQ command.  */
622                         cond->seq_end = i;
623                     }
624                 }
625               else
626                 {
627                   if (cond->seq_beg >= 0 && cond->seq_end < 0)
628                     /* The previous one is the last SEQ command.  */
629                     cond->seq_end = i;
630                 }
631             }
632           if (cond->seq_beg >= 0 && cond->seq_end < 0)
633             /* The previous one is the last SEQ command.  */
634             cond->seq_end = i;
635         }
636       else
637         {
638           cmd->type = FontLayoutCmdTypeRule;
639           if (MPLIST_MTEXT_P (elt))
640             {
641               MText *mt = MPLIST_MTEXT (elt);
642               char *str = (char *) MTEXT_DATA (mt);
643
644               if (str[0] != '^')
645                 {
646                   mtext_ins_char (mt, 0, '^', 1);
647                   str = (char *) MTEXT_DATA (mt);
648                 }
649               if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
650                 MERROR (MERROR_FONT, INVALID_CMD_ID);
651               cmd->body.rule.src_type = SRC_REGEX;
652               cmd->body.rule.src.re.pattern = strdup (str);
653             }
654           else if (MPLIST_INTEGER_P (elt))
655             {
656               cmd->body.rule.src_type = SRC_INDEX;
657               cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
658             }
659           else if (MPLIST_PLIST_P (elt))
660             {
661               MPlist *pl = MPLIST_PLIST (elt);
662               int size = MPLIST_LENGTH (pl);
663
664               if (MPLIST_INTEGER_P (pl))
665                 {
666                   int i;
667
668                   cmd->body.rule.src_type = SRC_SEQ;
669                   cmd->body.rule.src.seq.n_codes = size;
670                   MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
671                                  MERROR_FONT);
672                   for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
673                     {
674                       if (! MPLIST_INTEGER_P (pl))
675                         MERROR (MERROR_DRAW, INVALID_CMD_ID);
676                       cmd->body.rule.src.seq.codes[i]
677                         = (unsigned) MPLIST_INTEGER (pl);
678                     }
679                 }
680               else if (MPLIST_SYMBOL_P (pl) && size == 3)
681                 {
682                   cmd->body.rule.src_type = SRC_RANGE;
683                   pl = MPLIST_NEXT (pl);
684                   if (! MPLIST_INTEGER_P (pl))
685                     MERROR (MERROR_DRAW, INVALID_CMD_ID);
686                   cmd->body.rule.src.range.from
687                     = (unsigned) MPLIST_INTEGER (pl);
688                   pl = MPLIST_NEXT (pl);
689                   if (! MPLIST_INTEGER_P (pl))
690                     MERROR (MERROR_DRAW, INVALID_CMD_ID);
691                   cmd->body.rule.src.range.to
692                     = (unsigned) MPLIST_INTEGER (pl);
693                 }
694               else if (MPLIST_SYMBOL_P (pl) && size <= 2)
695                 {
696                   if (MPLIST_SYMBOL (pl) != Mexist)
697                     MERROR (MERROR_FLT, INVALID_CMD_ID);
698                   cmd->body.rule.src_type = SRC_EXIST;
699                   if (size == 1)
700                     cmd->body.rule.src.exist.c = -1;
701                   else
702                     {
703                       pl = MPLIST_NEXT (pl);
704                       if (! MPLIST_INTEGER_P (pl))
705                         MERROR (MERROR_DRAW, INVALID_CMD_ID);
706                       cmd->body.rule.src.exist.c = MPLIST_INTEGER (pl);
707                     }
708                 }
709               else
710                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
711             }
712           else
713             MERROR (MERROR_DRAW, INVALID_CMD_ID);
714
715           elt = MPLIST_NEXT (elt);
716           cmd->body.rule.n_cmds = len;
717           MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
718           for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
719             {
720               int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
721
722               if (this_id == INVALID_CMD_ID)
723                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
724               /* The above load_command may relocate stage->cmds.  */
725               cmd = stage->cmds + CMD_ID_TO_INDEX (id);
726               cmd->body.rule.cmd_ids[i] = this_id;
727             }
728         }
729     }
730   else if (MPLIST_SYMBOL_P (plist))
731     {
732       MPlist *elt;
733       MSymbol sym = MPLIST_SYMBOL (plist);
734       char *name = msymbol_name (sym);
735       int len = strlen (name);
736       FontLayoutCmd cmd;
737
738       if (len > 4
739           && ((name[0] == 'o' && name[1] == 't'
740                && name[2] == 'f' && name[3] == ':')
741               || (name[0] == ':' && name[1] == 'o' && name[2] == 't'
742                   && name[3] == 'f' && name[4] == '='))
743           && load_otf_command (&cmd, sym) >= 0)
744         {
745           if (id == INVALID_CMD_ID)
746             {
747               id = INDEX_TO_CMD_ID (stage->used);
748               MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
749             }
750           else
751             stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
752           return id;
753         }
754
755       if (len == 1)
756         {
757           if (*name == '=')
758             return CMD_ID_COPY;
759           else if (*name == '*')
760             return CMD_ID_REPEAT;
761           else if (*name == '<')
762             return CMD_ID_CLUSTER_BEGIN;
763           else if (*name == '>')
764             return CMD_ID_CLUSTER_END;
765           else if (*name == '|')
766             return CMD_ID_SEPARATOR;
767           else if (*name == '[')
768             return CMD_ID_LEFT_PADDING;
769           else if (*name == ']')
770             return CMD_ID_RIGHT_PADDING;
771           else
772             id = 0;
773         }
774       else
775         {
776           id = get_combining_command (sym);
777           if (id)
778             return id;
779         }
780
781       i = 1;
782       MPLIST_DO (elt, macros)
783         {
784           if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
785             {
786               id = INDEX_TO_CMD_ID (i);
787               if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
788                 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
789                                    macros, id);
790               return id;
791             }
792           i++;
793         }
794       MERROR (MERROR_DRAW, INVALID_CMD_ID);
795     }
796   else
797     MERROR (MERROR_DRAW, INVALID_CMD_ID);
798
799   return id;
800 }
801
802 static void
803 free_flt_command (FontLayoutCmd *cmd)
804 {
805   if (cmd->type == FontLayoutCmdTypeRule)
806     {
807       FontLayoutCmdRule *rule = &cmd->body.rule;
808
809       if (rule->src_type == SRC_REGEX)
810         {
811           free (rule->src.re.pattern);
812           regfree (&rule->src.re.preg);
813         }
814       else if (rule->src_type == SRC_SEQ)
815         free (rule->src.seq.codes);
816       free (rule->cmd_ids);
817     }
818   else if (cmd->type == FontLayoutCmdTypeCond)
819     free (cmd->body.cond.cmd_ids);
820   else if (cmd->type == FontLayoutCmdTypeOTF)
821     M17N_OBJECT_UNREF (cmd->body.otf);
822 }
823
824 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
825    and return it.  PLIST has this form:
826       PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
827 */
828
829 static FontLayoutStage *
830 load_generator (MPlist *plist)
831 {
832   FontLayoutStage *stage;
833   MPlist *elt, *pl;
834   FontLayoutCmd dummy;
835
836   MSTRUCT_CALLOC (stage, MERROR_DRAW);
837   MLIST_INIT1 (stage, cmds, 32);
838   dummy.type = FontLayoutCmdTypeMAX;
839   MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
840   MPLIST_DO (elt, MPLIST_NEXT (plist))
841     {
842       if (! MPLIST_PLIST_P (elt))
843         MERROR (MERROR_FONT, NULL);
844       pl = MPLIST_PLIST (elt);
845       if (! MPLIST_SYMBOL_P (pl))
846         MERROR (MERROR_FONT, NULL);
847       MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
848     }
849
850   /* Load the first command from PLIST into STAGE->cmds[0].  Macros
851      called in the first command are also loaded from MPLIST_NEXT
852      (PLIST) into STAGE->cmds[n].  */
853   if (load_command (stage, plist, MPLIST_NEXT (plist), INDEX_TO_CMD_ID (0))
854       == INVALID_CMD_ID)
855     {
856       MLIST_FREE1 (stage, cmds);
857       free (stage);
858       MERROR (MERROR_DRAW, NULL);
859     }
860
861   return stage;
862 }
863
864
865 /* Load FLT of name LAYOUTER_NAME from the m17n database into a newly
866    allocated memory, and return it.  */
867
868 static MFontLayoutTable *
869 load_flt (MSymbol layouter_name)
870 {
871   MDatabase *mdb;
872   MPlist *top = NULL, *plist;
873   MSymbol Mcategory = msymbol ("category");
874   MSymbol Mgenerator = msymbol ("generator");
875   MSymbol Mend = msymbol ("end");
876   MFontLayoutTable *layouter = NULL;
877   MCharTable *category = NULL;
878
879   if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
880     MERROR_GOTO (MERROR_FONT, finish);
881   if (! (top = (MPlist *) mdatabase_load (mdb)))
882     MERROR_GOTO (0, finish);
883   if (! MPLIST_PLIST_P (top))
884     MERROR_GOTO (MERROR_FONT, finish);
885
886   MPLIST_DO (plist, top)
887     {
888       MSymbol sym;
889       MPlist *elt;
890
891       if (MPLIST_SYMBOL_P (plist)
892           && MPLIST_SYMBOL (plist) == Mend)
893         break;
894       if (! MPLIST_PLIST (plist))
895         MERROR_GOTO (MERROR_FONT, finish);
896       elt = MPLIST_PLIST (plist);
897       if (! MPLIST_SYMBOL_P (elt))
898         MERROR_GOTO (MERROR_FONT, finish);
899       sym = MPLIST_SYMBOL (elt);
900       elt = MPLIST_NEXT (elt);
901       if (! elt)
902         MERROR_GOTO (MERROR_FONT, finish);
903       if (sym == Mcategory)
904         {
905           if (category)
906             M17N_OBJECT_UNREF (category);
907           category = load_category_table (elt);
908         }
909       else if (sym == Mgenerator)
910         {
911           FontLayoutStage *stage;
912
913           if (! category)
914             MERROR_GOTO (MERROR_FONT, finish);
915           stage = load_generator (elt);
916           if (! stage)
917             MERROR_GOTO (MERROR_FONT, finish);
918           stage->category = category;
919           M17N_OBJECT_REF (category);
920           if (! layouter)
921             {
922               layouter = mplist ();
923               /* Here don't do M17N_OBJECT_REF (category) because we
924                  don't unref the value of the element added below.  */
925               mplist_add (layouter, Mcategory, category);
926             }
927           mplist_add (layouter, Mt, stage);
928         }
929     }
930
931   if (category)
932     M17N_OBJECT_UNREF (category);
933
934  finish:
935   M17N_OBJECT_UNREF (top);
936   mplist_add (flt_list, layouter_name, layouter);
937   return layouter;
938 }
939
940
941 static void
942 free_flt_stage (FontLayoutStage *stage)
943 {
944   int i;
945
946   M17N_OBJECT_UNREF (stage->category);
947   for (i = 0; i < stage->used; i++)
948     free_flt_command (stage->cmds + i);
949   MLIST_FREE1 (stage, cmds);
950   free (stage);
951 }
952
953
954 static MFontLayoutTable *
955 get_font_layout_table (MSymbol layouter_name)
956 {
957   MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
958
959   return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
960 }
961
962
963 /* FLS (Font Layout Service) */
964
965 /* Structure to hold information about a context of FLS.  */
966
967 typedef struct
968 {
969   /* Pointer to the current stage.  */
970   FontLayoutStage *stage;
971
972   /* Encode each MGlyph->code by the current category table into this
973      array.  An element is a category.  */
974   char *encoded;
975   /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
976      index GIDX.  */
977   int encoded_offset;
978   int *match_indices;
979   int code_offset;
980   int cluster_begin_idx;
981   int cluster_begin_pos;
982   int cluster_end_pos;
983   int combining_code;
984   int left_padding;
985 } FontLayoutContext;
986
987 static int run_command (int depth,
988                         int, MGlyphString *, int, int, FontLayoutContext *);
989
990 #define NMATCH 20
991
992 static int
993 run_rule (int depth,
994           FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
995           FontLayoutContext *ctx)
996 {
997   int *saved_match_indices = ctx->match_indices;
998   int match_indices[NMATCH * 2];
999   int consumed;
1000   int i;
1001   int orig_from = from;
1002
1003   if (rule->src_type == SRC_SEQ)
1004     {
1005       int len;
1006
1007       len = rule->src.seq.n_codes;
1008       if (len > (to - from))
1009         return 0;
1010       for (i = 0; i < len; i++)
1011         if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
1012           break;
1013       if (i < len)
1014         return 0;
1015       to = from + len;
1016       if (MDEBUG_FLAG () > 2)
1017         MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "",
1018                        rule->src.seq.codes[0]);
1019     }
1020   else if (rule->src_type == SRC_RANGE)
1021     {
1022       int head;
1023
1024       if (from >= to)
1025         return 0;
1026       head = gstring->glyphs[from].code;
1027       if (head < rule->src.range.from || head > rule->src.range.to)
1028         return 0;
1029       ctx->code_offset = head - rule->src.range.from;
1030       to = from + 1;
1031       if (MDEBUG_FLAG () > 2)
1032         MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "",
1033                        rule->src.range.from, rule->src.range.to);
1034     }
1035   else if (rule->src_type == SRC_REGEX)
1036     {
1037       regmatch_t pmatch[NMATCH];
1038       char saved_code;
1039       int result;
1040
1041       if (from > to)
1042         return 0;
1043       saved_code = ctx->encoded[to - ctx->encoded_offset];
1044       ctx->encoded[to - ctx->encoded_offset] = '\0';
1045       result = regexec (&(rule->src.re.preg),
1046                         ctx->encoded + from - ctx->encoded_offset,
1047                         NMATCH, pmatch, 0);
1048       if (result == 0 && pmatch[0].rm_so == 0)
1049         {
1050           if (MDEBUG_FLAG () > 2)
1051             MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "",
1052                            rule->src.re.pattern,
1053                            ctx->encoded + from - ctx->encoded_offset,
1054                            pmatch[0].rm_eo);
1055           ctx->encoded[to - ctx->encoded_offset] = saved_code;
1056           for (i = 0; i < NMATCH; i++)
1057             {
1058               if (pmatch[i].rm_so < 0)
1059                 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1060               else
1061                 {
1062                   match_indices[i * 2] = from + pmatch[i].rm_so;
1063                   match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1064                 }
1065             }
1066           ctx->match_indices = match_indices;
1067           to = match_indices[1];
1068         }
1069       else
1070         {
1071           ctx->encoded[to - ctx->encoded_offset] = saved_code;
1072           return 0;
1073         }
1074     }
1075   else if (rule->src_type == SRC_INDEX)
1076     {
1077       if (rule->src.match_idx >= NMATCH)
1078         return 0;
1079       from = ctx->match_indices[rule->src.match_idx * 2];
1080       if (from < 0)
1081         return 0;
1082       to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1083       if (MDEBUG_FLAG () > 2)
1084         MDEBUG_PRINT3 ("\n [FLT] %*s(INDEX %d", depth, "", rule->src.match_idx);
1085     }
1086   else if (rule->src_type == SRC_EXIST)
1087     {
1088       MGlyph *g = MGLYPH (from);
1089       int encoded = g->otf_encoded;
1090       unsigned code;
1091
1092       if (rule->src.exist.c < 0)
1093         {
1094           if (from >= to)
1095             return 0;
1096           code = g->code;
1097           to = from + 1;
1098         }
1099       else
1100         {
1101           code = rule->src.exist.c;
1102           to = from;
1103           encoded = 0;
1104         }
1105       if (! encoded)
1106         {
1107           code = (g->rface->rfont->driver->encode_char
1108                   (NULL, (MFont *) g->rface->rfont, NULL, code));
1109           if (code == MCHAR_INVALID_CODE)
1110             return 0;
1111         }
1112     }
1113
1114   consumed = 0;
1115   depth++;
1116   for (i = 0; i < rule->n_cmds; i++)
1117     {
1118       int pos;
1119
1120       if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1121         {
1122           if (! consumed)
1123             continue;
1124           i--;
1125         }
1126       pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1127       if (pos < 0)
1128         MERROR (MERROR_DRAW, -1);
1129       consumed = pos > from;
1130       if (consumed)
1131         from = pos;
1132     }
1133
1134   ctx->match_indices = saved_match_indices;
1135   if (MDEBUG_FLAG () > 2)
1136     MDEBUG_PRINT (")");
1137   return (rule->src_type == SRC_INDEX ? orig_from : to);
1138 }
1139
1140 static int
1141 run_cond (int depth,
1142           FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1143           FontLayoutContext *ctx)
1144 {
1145   int i, pos = 0;
1146
1147   if (MDEBUG_FLAG () > 2)
1148     MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1149   depth++;
1150   for (i = 0; i < cond->n_cmds; i++)
1151     {
1152       /* TODO: Write a code for optimization utilizaing the info
1153          cond->seq_XXX.  */
1154       if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1155           != 0)
1156         break;
1157     }
1158   if (pos < 0)
1159     MERROR (MERROR_DRAW, -1);
1160   if (MDEBUG_FLAG () > 2)
1161     MDEBUG_PRINT (")");
1162   return (pos);
1163 }
1164
1165 static int
1166 run_otf (int depth,
1167          MFontCapability *otf_cmd, MGlyphString *gstring, int from, int to,
1168          FontLayoutContext *ctx)
1169 {
1170 #ifdef HAVE_OTF
1171   int from_idx = gstring->used;
1172
1173   if (MDEBUG_FLAG () > 2)
1174 #if 0
1175     MDEBUG_PRINT4 ("\n [FLT] %*s(OTF %s,%s)", depth, "",
1176                    (! otf_cmd->features[MFONT_OTT_GSUB].str ? ""
1177                     : otf_cmd->features[MFONT_OTT_GSUB].str),
1178                    (! otf_cmd->features[MFONT_OTT_GPOS].str ? ""
1179                     : otf_cmd->features[MFONT_OTT_GPOS].str));
1180 #else
1181     MDEBUG_PRINT3 ("\n [FLT] %*s:otf=%s", depth, "",
1182                    MSYMBOL_NAME (otf_cmd->otf));
1183 #endif
1184   to = mfont__ft_drive_otf (gstring, from, to, otf_cmd);
1185   if (ctx->cluster_begin_idx)
1186     for (; from_idx < gstring->used; from_idx++)
1187       UPDATE_CLUSTER_RANGE (ctx, gstring->glyphs[from_idx]);
1188 #endif
1189   return to;
1190 }
1191
1192 extern char *dump_combining_code (int code);
1193
1194 static int
1195 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1196              FontLayoutContext *ctx)
1197 {
1198   MGlyph g;
1199
1200   if (id >= 0)
1201     {
1202       int i;
1203
1204       /* Direct code (== id + ctx->code_offset) output.
1205          The source is not consumed.  */
1206       if (from < to || from == 1)
1207         g = *(MGLYPH (from));
1208       else
1209         g = *(MGLYPH (from - 1));
1210       g.type = GLYPH_CHAR;
1211       g.code = ctx->code_offset + id;
1212       if (MDEBUG_FLAG () > 2)
1213         MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "", g.code);
1214       if (ctx->combining_code)
1215         g.combining_code = ctx->combining_code;
1216       if (ctx->left_padding)
1217         g.left_padding = ctx->left_padding;
1218       for (i = from; i < to; i++)
1219         {
1220           MGlyph *tmp = MGLYPH (i);
1221
1222           if (g.pos > tmp->pos)
1223             g.pos = tmp->pos;
1224           else if (g.to < tmp->to)
1225             g.to = tmp->to;
1226         }
1227       APPEND_GLYPH (gstring, g);
1228       UPDATE_CLUSTER_RANGE (ctx, g);
1229       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1230       if (MDEBUG_FLAG () > 2)
1231         MDEBUG_PRINT (")");
1232       return (from);
1233     }
1234
1235   if (id <= CMD_ID_OFFSET_INDEX)
1236     {
1237       int idx = CMD_ID_TO_INDEX (id);
1238       FontLayoutCmd *cmd;
1239
1240       if (idx >= ctx->stage->used)
1241         MERROR (MERROR_DRAW, -1);
1242       cmd = ctx->stage->cmds + idx;
1243       if (cmd->type == FontLayoutCmdTypeRule)
1244         to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1245       else if (cmd->type == FontLayoutCmdTypeCond)
1246         to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1247       else if (cmd->type == FontLayoutCmdTypeOTF)
1248         to = run_otf (depth, cmd->body.otf, gstring, from, to, ctx);
1249
1250       if (to < 0)
1251         return -1;
1252       return to;
1253     }
1254
1255   if (id <= CMD_ID_OFFSET_COMBINING)
1256     {
1257       ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1258       if (MDEBUG_FLAG () > 2)
1259         MDEBUG_PRINT3 ("\n [FLT] %*s(CMB %s)", depth, "",
1260                        dump_combining_code (ctx->combining_code));
1261       return from;
1262     }
1263
1264   switch (id)
1265     {
1266     case CMD_ID_COPY:
1267       {
1268         if (from >= to)
1269           return from;
1270         g = *(MGLYPH (from));
1271         if (ctx->combining_code)
1272           g.combining_code = ctx->combining_code;
1273         if (ctx->left_padding)
1274           g.left_padding = ctx->left_padding;
1275         APPEND_GLYPH (gstring, g);
1276         UPDATE_CLUSTER_RANGE (ctx, g);
1277         if (MDEBUG_FLAG () > 2)
1278           {
1279             if (g.type == GLYPH_PAD)
1280               MDEBUG_PRINT2 ("\n [FLT] %*s(COPY |)", depth, "");
1281             else
1282               MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g.code);
1283           }
1284         ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1285         return (from + 1);
1286       }
1287
1288     case CMD_ID_CLUSTER_BEGIN:
1289       if (! ctx->cluster_begin_idx)
1290         {
1291           if (MDEBUG_FLAG () > 2)
1292             MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "", MGLYPH (from)->pos);
1293           ctx->cluster_begin_idx = gstring->used;
1294           ctx->cluster_begin_pos = MGLYPH (from)->pos;
1295           ctx->cluster_end_pos = MGLYPH (from)->to;
1296         }
1297       return from;
1298
1299     case CMD_ID_CLUSTER_END:
1300       if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1301         {
1302           int i;
1303
1304           if (MDEBUG_FLAG () > 2)
1305             MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1306           for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1307             {
1308               MGLYPH (i)->pos = ctx->cluster_begin_pos;
1309               MGLYPH (i)->to = ctx->cluster_end_pos;
1310             }
1311           ctx->cluster_begin_idx = 0;
1312         }
1313       return from;
1314
1315     case CMD_ID_SEPARATOR:
1316       {
1317         if (from < to)
1318           g = *(MGLYPH (from));
1319         else
1320           g = *(MGLYPH (from - 1));
1321         g.type = GLYPH_PAD;
1322         /* g.c = g.code = 0; */
1323         g.width = 0;
1324         APPEND_GLYPH (gstring, g);
1325         return from;
1326       }
1327
1328     case CMD_ID_LEFT_PADDING:
1329       if (MDEBUG_FLAG () > 2)
1330         MDEBUG_PRINT2 ("\n [FLT] %*s[", depth, "");
1331       ctx->left_padding = 1;
1332       return from;
1333
1334     case CMD_ID_RIGHT_PADDING:
1335       if (gstring->used > 0)
1336         {
1337           if (MDEBUG_FLAG () > 2)
1338             MDEBUG_PRINT2 ("\n [FLT] %*s]", depth, "");
1339           gstring->glyphs[gstring->used - 1].right_padding = 1;
1340         }
1341       return from;
1342     }
1343
1344   MERROR (MERROR_DRAW, -1);
1345 }
1346
1347 \f
1348 /* Internal API */
1349
1350 int
1351 mfont__flt_init (void)
1352 {
1353   Mcond = msymbol ("cond");
1354   Mrange = msymbol ("range");
1355   Mlayouter = msymbol ("layouter");
1356   Mexist = msymbol ("exist");
1357   flt_list = mplist ();
1358   return 0;
1359 }
1360
1361 void
1362 mfont__flt_fini (void)
1363 {
1364   MPlist *plist, *pl;
1365
1366   MPLIST_DO (plist, flt_list)
1367     {
1368       pl = MPLIST_PLIST (plist);
1369       if (pl)
1370         {
1371           MPLIST_DO (pl, MPLIST_NEXT (pl))
1372             free_flt_stage (MPLIST_VAL (pl));
1373           pl = MPLIST_PLIST (plist);
1374           M17N_OBJECT_UNREF (pl);
1375         }
1376     }
1377   M17N_OBJECT_UNREF (flt_list);
1378 }
1379
1380 unsigned
1381 mfont__flt_encode_char (MSymbol layouter_name, int c)
1382 {
1383   MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1384   MCharTable *table;
1385   unsigned code;
1386
1387   if (! layouter)
1388     return MCHAR_INVALID_CODE;
1389   table = MPLIST_VAL (layouter);
1390   code = (unsigned) mchartable_lookup (table, c);
1391   return (code ? code : MCHAR_INVALID_CODE);
1392 }
1393
1394 int
1395 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1396 {
1397   int stage_idx = 0;
1398   int gidx;
1399   int i;
1400   FontLayoutContext ctx;
1401   MCharTable *table;
1402   int encoded_len;
1403   int match_indices[NMATCH];
1404   MSymbol layouter_name = rface->layouter;
1405   MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1406   MRealizedFace *ascii_rface = rface->ascii_rface;
1407   FontLayoutStage *stage;
1408   int from_pos, to_pos;
1409   MGlyph dummy;
1410
1411   if (! layouter)
1412     {
1413       /* FLT not found.  Make all glyphs invisible.  */
1414       while (from < to)
1415         gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1416       return to;
1417     }
1418
1419   dummy = gstring->glyphs[from];
1420   MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1421
1422   /* Setup CTX.  */
1423   memset (&ctx, 0, sizeof ctx);
1424   table = MPLIST_VAL (layouter);
1425   layouter = MPLIST_NEXT (layouter);
1426   stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1427   gidx = from;
1428   /* Find previous glyphs that are also supported by the layouter.  */
1429   while (gidx > 1
1430          && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1431     gidx--;
1432   /* + 2 is for a separator ' ' and a terminator '\0'.  */
1433   encoded_len = gstring->used - gidx + 2;
1434   ctx.encoded = (char *) alloca (encoded_len);
1435
1436   for (i = 0; gidx < from; i++, gidx++)
1437     ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1438
1439   ctx.encoded[i++] = ' ';
1440   ctx.encoded_offset = from - i;
1441
1442   /* Now each MGlyph->code contains encoded char.  Set it in
1443      ctx.encoded[], and set MGlyph->c to MGlyph->code.  */
1444   for (gidx = from; gidx < to ; i++, gidx++)
1445     {
1446       ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1447       MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1448     }
1449   ctx.encoded[i++] = '\0';
1450
1451   match_indices[0] = from;
1452   match_indices[1] = to;
1453   for (i = 2; i < NMATCH; i++)
1454     match_indices[i] = -1;
1455   ctx.match_indices = match_indices;
1456
1457   from_pos = MGLYPH (from)->pos;
1458   to_pos = MGLYPH (to)->pos;
1459
1460   if (MDEBUG_FLAG ())
1461     {
1462       MDEBUG_PRINT ("\n [FLT]   (SOURCE");
1463       for (i = from; i < to; i++)
1464         {
1465           if (i > from && (i - from) % 8 == 0)
1466             MDEBUG_PRINT ("\n [FLT]          ");
1467           MDEBUG_PRINT1 (" %04X", MGLYPH (i)->c);
1468         }
1469       MDEBUG_PRINT (")");
1470     }
1471
1472   for (stage_idx = 0; 1; stage_idx++)
1473     {
1474       int len = to - from;
1475       int result;
1476
1477       ctx.code_offset = ctx.combining_code = ctx.left_padding = 0;
1478       if (MDEBUG_FLAG () > 2)
1479         {
1480           MDEBUG_PRINT2 ("\n [FLT]   (STAGE %d \"%s\"", stage_idx,
1481                          ctx.encoded + from - ctx.encoded_offset);
1482           fprintf (stderr, " (");
1483           for (i = from; i < to ; i++)
1484             {
1485               if (gstring->glyphs[i].type == GLYPH_PAD)
1486                 fprintf (stderr, "%*s|", (i > from), "");
1487               else
1488                 fprintf (stderr, "%*s%04X", (i > from), "",
1489                          gstring->glyphs[i].code);
1490             }
1491           fprintf (stderr, ")");
1492         }
1493
1494       gidx = gstring->used;
1495       ctx.stage = stage;
1496
1497       result = run_command (4, INDEX_TO_CMD_ID (0), gstring,
1498                             ctx.encoded_offset, to, &ctx);
1499       if (MDEBUG_FLAG () > 2)
1500         MDEBUG_PRINT (")");
1501       if (result < 0)
1502         return -1;
1503       to = from + (gstring->used - gidx);
1504       REPLACE_GLYPHS (gstring, gidx, from, len);
1505
1506       layouter = MPLIST_NEXT (layouter);
1507       /* If this is the last stage, break the loop. */
1508       if (MPLIST_TAIL_P (layouter))
1509         break;
1510
1511       /* Otherwise, prepare for the next iteration.   */
1512       stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1513       table = stage->category;
1514       if (to - from >= encoded_len)
1515         {
1516           encoded_len = to + 1;
1517           ctx.encoded = (char *) alloca (encoded_len);
1518         }
1519
1520       for (i = from; i < to; i++)
1521         {
1522           MGlyph *g = MGLYPH (i);
1523
1524           if (g->type == GLYPH_PAD)
1525             ctx.encoded[i - from] = ' ';
1526           else if (! g->otf_encoded)
1527             ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1528 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1529           else
1530             {
1531               int c = mfont__ft_decode_otf (g);
1532
1533               if (c >= 0)
1534                 {
1535                   c = (int) mchartable_lookup (table, c);
1536                   if (! c)
1537                     c = -1;
1538                 }
1539               ctx.encoded[i - from] = (c >= 0 ? c : 1);
1540             }
1541 #endif  /* HAVE_FREETYPE && HAVE_OTF */
1542         }
1543       ctx.encoded[i - from] = '\0';
1544       ctx.encoded_offset = from;
1545       ctx.match_indices[0] = from;
1546       ctx.match_indices[1] = to;
1547     }
1548
1549   if (from == to)
1550     {
1551       /* Somehow there's no glyph contributing to characters between
1552          FROM_POS and TO_POS.  We must add one dummy space glyph for
1553          those characters.  */
1554       MGlyph g;
1555
1556       g.type = GLYPH_SPACE;
1557       g.c = ' ', g.code = ' ';
1558       g.pos = from_pos, g.to = to_pos;
1559       g.rface = ascii_rface;
1560       INSERT_GLYPH (gstring, from, g);
1561       to = from + 1;
1562     }
1563   else
1564     {
1565       /* Get actual glyph IDs of glyphs.  Also check if all characters
1566          in the range is covered by some glyph(s).  If not, change
1567          <pos> and <to> of glyphs to cover uncovered characters.  */
1568       int len = to_pos - from_pos;
1569       int pos;
1570       MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1571       MGlyph *g, *gend = MGLYPH (to);
1572       MGlyph *latest = gend;
1573
1574       for (g = MGLYPH (from); g != gend; g++)
1575         if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1576           g->code = ((rface->rfont->driver->encode_char)
1577                      (NULL, (MFont *) rface->rfont, NULL, g->code));
1578       for (i = 0; i < len; i++)
1579         glyphs[i] = NULL;
1580       for (g = MGLYPH (from); g != gend; g++)
1581         {
1582           if (g->pos < latest->pos)
1583             latest = g;
1584           if (! glyphs[g->pos - from_pos])
1585             {
1586               for (i = g->pos; i < g->to; i++)
1587                 glyphs[i - from_pos] = g;
1588             }
1589         }
1590       i = 0;
1591       if (! glyphs[0])
1592         {
1593           pos = latest->pos;
1594           for (g = latest; g->pos == pos; g++)
1595             g->pos = from_pos;
1596           i++;
1597         }
1598       for (; i < len; i++)
1599         {
1600           if (! glyphs[i])
1601             {
1602               for (g = latest; g->pos == latest->pos; g++)
1603                 g->to = from_pos + i + 1;
1604             }
1605           else
1606             latest = glyphs[i];
1607         }
1608     }
1609   if (MDEBUG_FLAG ())
1610     {
1611       MDEBUG_PRINT ("\n [FLT]   (RESULT");
1612       for (i = from; i < to;)
1613         {
1614           if (MGLYPH (i)->otf_encoded)
1615             i++;
1616           else
1617             {
1618               int j = i++;
1619
1620               while (i < to && ! MGLYPH (i)->otf_encoded) i++;
1621               mfont__get_metric (gstring, j, i);
1622             }
1623         }
1624       if (MDEBUG_FLAG () > 1)
1625         for (i = 0; from < to; from++, i++)
1626           {
1627             MGlyph *g;
1628             int width, xoff, yoff;
1629
1630             if (i > 0 && i % 4 == 0)
1631               MDEBUG_PRINT ("\n [FLT]          ");
1632             g = MGLYPH (from);
1633             g->otf_encoded = 1;
1634             width = g->width, xoff = g->xoff, yoff = g->yoff;
1635             if (g->right_padding && g->rbearing > g->width)
1636               width = g->rbearing;
1637             if (g->left_padding && g->lbearing < 0)
1638               {
1639                 width += - g->lbearing;
1640                 xoff += - g->lbearing;
1641               }
1642             MDEBUG_PRINT4 (" (%04X %d %d %d)", g->code, width, xoff, yoff);
1643           }
1644       else
1645         for (; from < to; from++)
1646           {
1647             MGLYPH (from)->otf_encoded = 1;
1648             MDEBUG_PRINT1 (" %04X", MGLYPH (from)->code);
1649           }
1650       MDEBUG_PRINT ("))\n");
1651     }
1652
1653   return to;
1654 }
1655
1656 \f
1657 /* for debugging... */
1658
1659 static void
1660 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1661 {
1662   char *prefix = (char *) alloca (indent + 1);
1663
1664   memset (prefix, 32, indent);
1665   prefix[indent] = 0;
1666
1667   if (id >= 0)
1668     fprintf (stderr, "0x%02X", id);
1669   else if (id <= CMD_ID_OFFSET_INDEX)
1670     {
1671       int idx = CMD_ID_TO_INDEX (id);
1672       FontLayoutCmd *cmd = stage->cmds + idx;
1673
1674       if (cmd->type == FontLayoutCmdTypeRule)
1675         {
1676           FontLayoutCmdRule *rule = &cmd->body.rule;
1677           int i;
1678
1679           fprintf (stderr, "(rule ");
1680           if (rule->src_type == SRC_REGEX)
1681             fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1682           else if (rule->src_type == SRC_INDEX)
1683             fprintf (stderr, "%d", rule->src.match_idx);
1684           else if (rule->src_type == SRC_SEQ)
1685             fprintf (stderr, "(seq)");
1686           else if (rule->src_type == SRC_RANGE)
1687             fprintf (stderr, "(range)");
1688           else
1689             fprintf (stderr, "(invalid src)");
1690
1691           for (i = 0; i < rule->n_cmds; i++)
1692             {
1693               fprintf (stderr, "\n%s  ", prefix);
1694               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1695             }
1696           fprintf (stderr, ")");
1697         }
1698       else if (cmd->type == FontLayoutCmdTypeCond)
1699         {
1700           FontLayoutCmdCond *cond = &cmd->body.cond;
1701           int i;
1702
1703           fprintf (stderr, "(cond");
1704           for (i = 0; i < cond->n_cmds; i++)
1705             {
1706               fprintf (stderr, "\n%s  ", prefix);
1707               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1708             }
1709           fprintf (stderr, ")");
1710         }
1711       else if (cmd->type == FontLayoutCmdTypeOTF)
1712         {
1713           fprintf (stderr, "(otf)");
1714         }
1715       else
1716         fprintf (stderr, "(error-command)");
1717     }
1718   else if (id <= CMD_ID_OFFSET_COMBINING)
1719     fprintf (stderr, "cominging-code");
1720   else
1721     fprintf (stderr, "(predefiend %d)", id);
1722 }
1723
1724 void
1725 dump_flt (MFontLayoutTable *flt, int indent)
1726 {
1727   char *prefix = (char *) alloca (indent + 1);
1728   MPlist *plist;
1729   int stage_idx = 0;
1730
1731   memset (prefix, 32, indent);
1732   prefix[indent] = 0;
1733   fprintf (stderr, "(flt");
1734   MPLIST_DO (plist, flt)
1735     {
1736       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1737       int i;
1738
1739       fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
1740       for (i = 0; i < stage->used; i++)
1741         {
1742           fprintf (stderr, "\n%s    ", prefix);
1743           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1744         }
1745       fprintf (stderr, ")");
1746       stage_idx++;
1747     }
1748   fprintf (stderr, ")");
1749 }