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