ba88e275ad0d6f2055bf39c19502c11459bdf22e
[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   enum FontLayoutCmdType type;
318   union {
319     FontLayoutCmdRule rule;
320     FontLayoutCmdCond cond;
321     FontLayoutCmdOTF otf;
322   } body;
323 } FontLayoutCmd;
324
325 typedef struct 
326 {
327   MCharTable *category;
328   int size, inc, used;
329   FontLayoutCmd *cmds;
330 } FontLayoutStage;
331
332 typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */
333
334 /* Font layout table loader */
335
336 /* Load a category table from PLIST.  PLIST has this form:
337       PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
338 */
339
340 static MCharTable *
341 load_category_table (MPlist *plist)
342 {
343   MCharTable *table;
344
345   table = mchartable (Minteger, (void *) 0);
346
347   MPLIST_DO (plist, plist)
348     {
349       MPlist *elt;
350       int from, to, category_code;
351
352       if (! MPLIST_PLIST (plist))
353         MERROR (MERROR_FONT, NULL);
354       elt = MPLIST_PLIST (plist);
355       if (! MPLIST_INTEGER_P (elt))
356         MERROR (MERROR_FONT, NULL);
357       from = MPLIST_INTEGER (elt);
358       elt = MPLIST_NEXT (elt);
359       if (! MPLIST_INTEGER_P (elt))
360         MERROR (MERROR_FONT, NULL);
361       to = MPLIST_INTEGER (elt);
362       elt = MPLIST_NEXT (elt);
363       if (MPLIST_TAIL_P (elt))
364         {
365           category_code = to;
366           to = from;
367         }
368       else
369         {
370           if (! MPLIST_INTEGER_P (elt))
371             MERROR (MERROR_FONT, NULL);
372           category_code = MPLIST_INTEGER (elt);
373         }
374       if (! isalpha (category_code))
375         MERROR (MERROR_FONT, NULL);
376
377       if (from == to)
378         mchartable_set (table, from, (void *) category_code);
379       else
380         mchartable_set_range (table, from, to, (void *) category_code);
381     }
382
383   return table;
384 }
385
386
387 /* Parse OTF command name NAME and store the result in CMD.
388    NAME has this form:
389         :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
390    where GSUB-FEATURES and GPOS-FEATURES have this form:
391         [FEATURE[,FEATURE]*] | ' '  */
392
393 static int
394 load_otf_command (FontLayoutCmd *cmd, char *name)
395 {
396   char *p = name, *beg;
397
398   cmd->type = FontLayoutCmdTypeOTF;
399   cmd->body.otf.script = cmd->body.otf.langsys = Mnil;
400   cmd->body.otf.gsub_features = cmd->body.otf.gpos_features = Mt;
401
402   while (*p)
403     {
404       if (*p == ':')
405         {
406           for (beg = ++p; *p && *p != '/' && *p != '=' && *p != '+'; p++);
407           if (beg < p)
408             cmd->body.otf.script = msymbol__with_len (beg, p - beg);
409         }
410       else if (*p == '/')
411         {
412           for (beg = ++p; *p && *p != '=' && *p != '+'; p++);
413           if (beg < p)
414             cmd->body.otf.langsys = msymbol__with_len (beg, p - beg);
415         }
416       else if (*p == '=')
417         {
418           for (beg = ++p; *p && *p != '+'; p++);
419           if (beg < p)
420             cmd->body.otf.gsub_features = msymbol__with_len (beg, p - beg);
421           else
422             cmd->body.otf.gsub_features = Mnil;
423         }
424       else if (*p == '+')
425         {
426           for (beg = ++p; *p && *p != '+'; p++);
427           if (beg < p)
428             cmd->body.otf.gpos_features = msymbol__with_len (beg, p - beg);
429           else
430             cmd->body.otf.gpos_features = Mnil;
431         }
432       else
433         p++;
434     }
435
436   return (cmd->body.otf.script == Mnil ? -1 : 0);
437 }
438
439
440 /* Read a decimal number from STR preceded by one of "+-><".  '+' and
441    '>' means a plus sign, '-' and '<' means a minus sign.  If the
442    number is greater than 127, limit it to 127.  */
443
444 static int
445 read_decimal_number (char **str)
446 {
447   char *p = *str;
448   int sign = (*p == '-' || *p == '<') ? -1 : 1;
449   int n = 0;
450
451   p++;
452   while (*p >= '0' && *p <= '9')
453     n = n * 10 + *p++ - '0';
454   *str = p;
455   if (n == 0)
456     n = 5;
457   return (n < 127 ? n * sign : 127 * sign);
458 }
459
460
461 /* Read a horizontal and vertical combining positions from STR, and
462    store them in the place pointed by X and Y.  The horizontal
463    position left, center, and right are represented by 0, 1, and 2
464    respectively.  The vertical position top, center, bottom, and base
465    are represented by 0, 1, 2, and 3 respectively.  If successfully
466    read, return 0, else return -1.  */
467
468 static int
469 read_combining_position (char *str, int *x, int *y)
470 {
471   int c = *str++;
472   int i;
473
474   /* Vertical position comes first.  */
475   for (i = 0; i < 4; i++)
476     if (c == "tcbB"[i])
477       {
478         *y = i;
479         break;
480       }
481   if (i == 4)
482     return -1;
483   c = *str;
484   /* Then comse horizontal position.  */
485   for (i = 0; i < 3; i++)
486     if (c == "lcr"[i])
487       {
488         *x = i;
489         return 0;
490       }
491   return -1;
492 }
493
494
495 /* Return a combining code corresponding to SYM.  */
496
497 static int
498 get_combining_command (MSymbol sym)
499 {
500   char *str = msymbol_name (sym);
501   int base_x, base_y, add_x, add_y, off_x, off_y;
502   int c;
503
504   if (read_combining_position (str, &base_x, &base_y) < 0)
505     return 0;
506   str += 2;
507   c = *str;
508   if (c == '.')
509     {
510       off_x = off_y = 128;
511       str++;
512     }
513   else
514     {
515       if (c == '+' || c == '-')
516         {
517           off_y = read_decimal_number (&str) + 128;
518           c = *str;
519         }
520       else
521         off_y = 128;
522       if (c == '<' || c == '>')
523         off_x = read_decimal_number (&str) + 128;
524       else
525         off_x = 128;
526     }
527   if (read_combining_position (str, &add_x, &add_y) < 0)
528     return 0;
529
530   c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
531   return (COMBINING_CODE_TO_CMD_ID (c));
532 }
533
534
535 /* Load a command from PLIST into STAGE, and return that
536    identification number.  If ID is not INVALID_CMD_ID, that means we
537    are loading a top level command or a macro.  In that case, use ID
538    as the identification number of the command.  Otherwise, generate a
539    new id number for the command.  MACROS is a list of raw macros.  */
540
541 static int
542 load_command (FontLayoutStage *stage, MPlist *plist,
543               MPlist *macros, int id)
544 {
545   int i;
546
547   if (MPLIST_INTEGER_P (plist))
548     {
549       int code = MPLIST_INTEGER (plist);
550
551       if (code < 0)
552         MERROR (MERROR_DRAW, INVALID_CMD_ID);
553       return code;
554     }
555   else if (MPLIST_PLIST_P (plist))
556     {
557       /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
558                    | ( ( INTEGER INTEGER ) ... )
559                    | ( ( range INTEGER INTEGER ) ... )  */
560       MPlist *elt = MPLIST_PLIST (plist);
561       int len = MPLIST_LENGTH (elt) - 1;
562       FontLayoutCmd *cmd;
563
564       if (id == INVALID_CMD_ID)
565         {
566           FontLayoutCmd dummy;
567           id = INDEX_TO_CMD_ID (stage->used);
568           MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
569         }
570       cmd = stage->cmds + CMD_ID_TO_INDEX (id);
571
572       if (MPLIST_SYMBOL_P (elt))
573         {
574           FontLayoutCmdCond *cond;
575
576           if (MPLIST_SYMBOL (elt) != Mcond)
577             MERROR (MERROR_DRAW, INVALID_CMD_ID);
578           elt = MPLIST_NEXT (elt);
579           cmd->type = FontLayoutCmdTypeCond;
580           cond = &cmd->body.cond;
581           cond->seq_beg = cond->seq_end = -1;
582           cond->seq_from = cond->seq_to = 0;
583           cond->n_cmds = len;
584           MTABLE_CALLOC (cond->cmd_ids, len, MERROR_DRAW);
585           for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
586             {
587               int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
588
589               if (this_id == INVALID_CMD_ID)
590                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
591               /* The above load_command may relocate stage->cmds.  */
592               cmd = stage->cmds + CMD_ID_TO_INDEX (id);
593               cond = &cmd->body.cond;
594               cond->cmd_ids[i] = this_id;
595               if (this_id <= CMD_ID_OFFSET_INDEX)
596                 {
597                   FontLayoutCmd *this_cmd
598                     = stage->cmds + CMD_ID_TO_INDEX (this_id);
599
600                   if (this_cmd->type == FontLayoutCmdTypeRule
601                       && this_cmd->body.rule.src_type == SRC_SEQ)
602                     {
603                       int first_char = this_cmd->body.rule.src.seq.codes[0];
604
605                       if (cond->seq_beg < 0)
606                         {
607                           /* The first SEQ command.  */
608                           cond->seq_beg = i;
609                           cond->seq_from = cond->seq_to = first_char;
610                         }
611                       else if (cond->seq_end < 0)
612                         {
613                           /* The following SEQ command.  */
614                           if (cond->seq_from > first_char)
615                             cond->seq_from = first_char;
616                           else if (cond->seq_to < first_char)
617                             cond->seq_to = first_char;
618                         }
619                     }
620                   else
621                     {
622                       if (cond->seq_beg >= 0 && cond->seq_end < 0)
623                         /* The previous one is the last SEQ command.  */
624                         cond->seq_end = i;
625                     }
626                 }
627               else
628                 {
629                   if (cond->seq_beg >= 0 && cond->seq_end < 0)
630                     /* The previous one is the last SEQ command.  */
631                     cond->seq_end = i;
632                 }
633             }
634           if (cond->seq_beg >= 0 && cond->seq_end < 0)
635             /* The previous one is the last SEQ command.  */
636             cond->seq_end = i;
637         }
638       else
639         {
640           cmd->type = FontLayoutCmdTypeRule;
641           if (MPLIST_MTEXT_P (elt))
642             {
643               char *str = (char *) MTEXT_DATA (MPLIST_MTEXT (elt));
644
645               if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
646                 MERROR (MERROR_FONT, INVALID_CMD_ID);
647               cmd->body.rule.src_type = SRC_REGEX;
648               cmd->body.rule.src.re.pattern = strdup (str);
649             }
650           else if (MPLIST_INTEGER_P (elt))
651             {
652               cmd->body.rule.src_type = SRC_INDEX;
653               cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
654             }
655           else if (MPLIST_PLIST_P (elt))
656             {
657               MPlist *pl = MPLIST_PLIST (elt);
658               int size = MPLIST_LENGTH (pl);
659
660               if (MPLIST_INTEGER_P (pl))
661                 {
662                   int i;
663
664                   cmd->body.rule.src_type = SRC_SEQ;
665                   cmd->body.rule.src.seq.n_codes = size;
666                   MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
667                                  MERROR_FONT);
668                   for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
669                     {
670                       if (! MPLIST_INTEGER_P (pl))
671                         MERROR (MERROR_DRAW, INVALID_CMD_ID);
672                       cmd->body.rule.src.seq.codes[i]
673                         = (unsigned) MPLIST_INTEGER (pl);
674                     }
675                 }
676               else if (MPLIST_SYMBOL_P (pl) && size == 3)
677                 {
678                   cmd->body.rule.src_type = SRC_RANGE;
679                   pl = MPLIST_NEXT (pl);
680                   if (! MPLIST_INTEGER_P (pl))
681                     MERROR (MERROR_DRAW, INVALID_CMD_ID);
682                   cmd->body.rule.src.range.from
683                     = (unsigned) MPLIST_INTEGER (pl);
684                   pl = MPLIST_NEXT (pl);
685                   if (! MPLIST_INTEGER_P (pl))
686                     MERROR (MERROR_DRAW, INVALID_CMD_ID);
687                   cmd->body.rule.src.range.to
688                     = (unsigned) MPLIST_INTEGER (pl);
689                 }
690               else
691                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
692             }
693           else
694             MERROR (MERROR_DRAW, INVALID_CMD_ID);
695
696           elt = MPLIST_NEXT (elt);
697           cmd->body.rule.n_cmds = len;
698           MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
699           for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
700             {
701               int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
702
703               if (this_id == INVALID_CMD_ID)
704                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
705               /* The above load_command may relocate stage->cmds.  */
706               cmd = stage->cmds + CMD_ID_TO_INDEX (id);
707               cmd->body.rule.cmd_ids[i] = this_id;
708             }
709         }
710     }
711   else if (MPLIST_SYMBOL_P (plist))
712     {
713       MPlist *elt;
714       MSymbol sym = MPLIST_SYMBOL (plist);
715       char *name = msymbol_name (sym);
716       int len = strlen (name);
717       FontLayoutCmd cmd;
718
719       if (len > 4
720           && ! strncmp (name, "otf:", 4)
721           && load_otf_command (&cmd, name + 3) >= 0)
722         {
723           if (id == INVALID_CMD_ID)
724             {
725               id = INDEX_TO_CMD_ID (stage->used);
726               MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
727             }
728           else
729             stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
730           return id;
731         }
732
733       if (len == 1)
734         {
735           if (*name == '=')
736             return CMD_ID_COPY;
737           else if (*name == '*')
738             return CMD_ID_REPEAT;
739           else if (*name == '<')
740             return CMD_ID_CLUSTER_BEGIN;
741           else if (*name == '>')
742             return CMD_ID_CLUSTER_END;
743           else if (*name == '|')
744             return CMD_ID_SEPARATOR;
745           else if (*name == '[')
746             return CMD_ID_LEFT_PADDING;
747           else if (*name == ']')
748             return CMD_ID_RIGHT_PADDING;
749           else
750             id = 0;
751         }
752       else
753         {
754           id = get_combining_command (sym);
755           if (id)
756             return id;
757         }
758
759       i = 1;
760       MPLIST_DO (elt, macros)
761         {
762           if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
763             {
764               id = INDEX_TO_CMD_ID (i);
765               if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
766                 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
767                                    macros, id);
768               return id;
769             }
770           i++;
771         }
772       MERROR (MERROR_DRAW, INVALID_CMD_ID);
773     }
774   else
775     MERROR (MERROR_DRAW, INVALID_CMD_ID);
776
777   return id;
778 }
779
780 static void
781 free_flt_command (FontLayoutCmd *cmd)
782 {
783   if (cmd->type == FontLayoutCmdTypeRule)
784     {
785       FontLayoutCmdRule *rule = &cmd->body.rule;
786
787       if (rule->src_type == SRC_REGEX)
788         {
789           free (rule->src.re.pattern);
790           regfree (&rule->src.re.preg);
791         }
792       else if (rule->src_type == SRC_SEQ)
793         free (rule->src.seq.codes);
794       free (rule->cmd_ids);
795     }
796   else if (cmd->type == FontLayoutCmdTypeCond)
797     free (cmd->body.cond.cmd_ids);
798 }
799
800 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
801    and return it.  PLIST has this form:
802       PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
803 */
804
805 static FontLayoutStage *
806 load_generator (MPlist *plist)
807 {
808   FontLayoutStage *stage;
809   MPlist *elt, *pl;
810   FontLayoutCmd dummy;
811
812   MSTRUCT_CALLOC (stage, MERROR_DRAW);
813   MLIST_INIT1 (stage, cmds, 32);
814   dummy.type = FontLayoutCmdTypeMAX;
815   MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
816   MPLIST_DO (elt, MPLIST_NEXT (plist))
817     {
818       if (! MPLIST_PLIST_P (elt))
819         MERROR (MERROR_FONT, NULL);
820       pl = MPLIST_PLIST (elt);
821       if (! MPLIST_SYMBOL_P (pl))
822         MERROR (MERROR_FONT, NULL);
823       MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
824     }
825
826   /* Load the first command from PLIST into STAGE->cmds[0].  Macros
827      called in the first command are also loaded from MPLIST_NEXT
828      (PLIST) into STAGE->cmds[n].  */
829   if (load_command (stage, plist, MPLIST_NEXT (plist), INDEX_TO_CMD_ID (0))
830       == INVALID_CMD_ID)
831     {
832       MLIST_FREE1 (stage, cmds);
833       free (stage);
834       MERROR (MERROR_DRAW, NULL);
835     }
836
837   return stage;
838 }
839
840
841 /* Load FLT of name LAYOUTER_NAME from the m17n database into a newly
842    allocated memory, and return it.  */
843
844 static MFontLayoutTable *
845 load_flt (MSymbol layouter_name)
846 {
847   MDatabase *mdb;
848   MPlist *top = NULL, *plist;
849   MSymbol Mcategory = msymbol ("category");
850   MSymbol Mgenerator = msymbol ("generator");
851   MFontLayoutTable *layouter = NULL;
852   MCharTable *category = NULL;
853
854   if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
855     MERROR_GOTO (MERROR_FONT, finish);
856   if (! (top = (MPlist *) mdatabase_load (mdb)))
857     MERROR_GOTO (0, finish);
858   if (! MPLIST_PLIST_P (top))
859     MERROR_GOTO (MERROR_FONT, finish);
860
861   MPLIST_DO (plist, top)
862     {
863       MSymbol sym;
864       MPlist *elt;
865
866       if (! MPLIST_PLIST (plist))
867         MERROR_GOTO (MERROR_FONT, finish);
868       elt = MPLIST_PLIST (plist);
869       if (! MPLIST_SYMBOL_P (elt))
870         MERROR_GOTO (MERROR_FONT, finish);
871       sym = MPLIST_SYMBOL (elt);
872       elt = MPLIST_NEXT (elt);
873       if (! elt)
874         MERROR_GOTO (MERROR_FONT, finish);
875       if (sym == Mcategory)
876         {
877           if (category)
878             M17N_OBJECT_UNREF (category);
879           category = load_category_table (elt);
880         }
881       else if (sym == Mgenerator)
882         {
883           FontLayoutStage *stage;
884
885           if (! category)
886             MERROR_GOTO (MERROR_FONT, finish);
887           stage = load_generator (elt);
888           if (! stage)
889             MERROR_GOTO (MERROR_FONT, finish);
890           stage->category = category;
891           M17N_OBJECT_REF (category);
892           if (! layouter)
893             {
894               layouter = mplist ();
895               /* Here don't do M17N_OBJECT_REF (category) because we
896                  don't unref the value of the element added below.  */
897               mplist_add (layouter, Mcategory, category);
898             }
899           mplist_add (layouter, Mt, stage);
900         }
901       else
902         MERROR_GOTO (MERROR_FONT, finish);
903     }
904
905   if (category)
906     M17N_OBJECT_UNREF (category);
907
908  finish:
909   M17N_OBJECT_UNREF (top);
910   mplist_add (flt_list, layouter_name, layouter);
911   return layouter;
912 }
913
914
915 static void
916 free_flt_stage (FontLayoutStage *stage)
917 {
918   int i;
919
920   M17N_OBJECT_UNREF (stage->category);
921   for (i = 0; i < stage->used; i++)
922     free_flt_command (stage->cmds + i);
923   MLIST_FREE1 (stage, cmds);
924   free (stage);
925 }
926
927
928 static MFontLayoutTable *
929 get_font_layout_table (MSymbol layouter_name)
930 {
931   MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
932
933   return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
934 }
935
936
937 /* FLS (Font Layout Service) */
938
939 /* Structure to hold information about a context of FLS.  */
940
941 typedef struct
942 {
943   /* Pointer to the current stage.  */
944   FontLayoutStage *stage;
945
946   /* Encode each MGlyph->code by the current category table into this
947      array.  An element is a category.  */
948   char *encoded;
949   /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
950      index GIDX.  */
951   int encoded_offset;
952   int *match_indices;
953   int code_offset;
954   int cluster_begin_idx;
955   int cluster_begin_pos;
956   int cluster_end_pos;
957   int combining_code;
958   int left_padding;
959 } FontLayoutContext;
960
961 static int run_command (int depth,
962                         int, MGlyphString *, int, int, FontLayoutContext *);
963
964 #define NMATCH 20
965
966 static int
967 run_rule (int depth,
968           FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
969           FontLayoutContext *ctx)
970 {
971   int *saved_match_indices = ctx->match_indices;
972   int match_indices[NMATCH * 2];
973   int consumed;
974   int i;
975   int orig_from = from;
976
977   if (ctx->cluster_begin_idx)
978     {
979       if (ctx->cluster_begin_pos > MGLYPH (from)->pos)
980         ctx->cluster_begin_pos = MGLYPH (from)->pos;
981       if (ctx->cluster_end_pos < MGLYPH (to)->pos)
982         ctx->cluster_end_pos = MGLYPH (to)->pos;
983     }
984
985   if (rule->src_type == SRC_SEQ)
986     {
987       int len;
988
989       len = rule->src.seq.n_codes;
990       if (len > (to - from))
991         return 0;
992       for (i = 0; i < len; i++)
993         if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
994           break;
995       if (i < len)
996         return 0;
997       to = from + len;
998       MDEBUG_PRINT1 (" (SEQ 0x%X", rule->src.seq.codes[0]);
999     }
1000   else if (rule->src_type == SRC_RANGE)
1001     {
1002       int head;
1003
1004       if (from >= to)
1005         return 0;
1006       head = gstring->glyphs[from].code;
1007       if (head < rule->src.range.from || head > rule->src.range.to)
1008         return 0;
1009       ctx->code_offset = head - rule->src.range.from;
1010       to = from + 1;
1011       MDEBUG_PRINT2 (" (RANGE 0x%X-0x%X",
1012                      rule->src.range.from, rule->src.range.to);
1013     }
1014   else if (rule->src_type == SRC_REGEX)
1015     {
1016       regmatch_t pmatch[NMATCH];
1017       char saved_code;
1018       int result;
1019
1020       if (from > to)
1021         return 0;
1022       saved_code = ctx->encoded[to - ctx->encoded_offset];
1023       ctx->encoded[to - ctx->encoded_offset] = '\0';
1024       result = regexec (&(rule->src.re.preg),
1025                         ctx->encoded + from - ctx->encoded_offset,
1026                         NMATCH, pmatch, 0);
1027       if (result == 0 && pmatch[0].rm_so == 0)
1028         {
1029           MDEBUG_PRINT3 (" (REGEX \"%s\" \"%s\" %d",
1030                          rule->src.re.pattern,
1031                          ctx->encoded + from - ctx->encoded_offset,
1032                          pmatch[0].rm_eo);
1033           ctx->encoded[to - ctx->encoded_offset] = saved_code;
1034           for (i = 0; i < NMATCH; i++)
1035             {
1036               if (pmatch[i].rm_so < 0)
1037                 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1038               else
1039                 {
1040                   match_indices[i * 2] = from + pmatch[i].rm_so;
1041                   match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1042                 }
1043             }
1044           ctx->match_indices = match_indices;
1045           to = match_indices[1];
1046         }
1047       else
1048         {
1049           ctx->encoded[to - ctx->encoded_offset] = saved_code;
1050           return 0;
1051         }
1052     }
1053   else if (rule->src_type == SRC_INDEX)
1054     {
1055       if (rule->src.match_idx >= NMATCH)
1056         return 0;
1057       from = ctx->match_indices[rule->src.match_idx * 2];
1058       if (from < 0)
1059         return 0;
1060       to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1061       MDEBUG_PRINT1 (" (INDEX %d", rule->src.match_idx);
1062     }
1063
1064   consumed = 0;
1065   depth++;
1066   for (i = 0; i < rule->n_cmds; i++)
1067     {
1068       int pos;
1069
1070       if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1071         {
1072           if (! consumed)
1073             continue;
1074           i--;
1075         }
1076       pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1077       if (pos < 0)
1078         MERROR (MERROR_DRAW, -1);
1079       consumed = pos > from;
1080       if (consumed)
1081         from = pos;
1082     }
1083
1084   ctx->match_indices = saved_match_indices;
1085   MDEBUG_PRINT (")");
1086   return (rule->src_type == SRC_INDEX ? orig_from : to);
1087 }
1088
1089 static int
1090 run_cond (int depth,
1091           FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1092           FontLayoutContext *ctx)
1093 {
1094   int i, pos = 0;
1095
1096   MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1097   depth++;
1098   for (i = 0; i < cond->n_cmds; i++)
1099     {
1100       /* TODO: Write a code for optimization utilizaing the info
1101          cond->seq_XXX.  */
1102       if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1103           != 0)
1104         break;
1105     }
1106   if (pos < 0)
1107     MERROR (MERROR_DRAW, -1);
1108   MDEBUG_PRINT (")");
1109   return (pos);
1110 }
1111
1112 static int
1113 run_otf (int depth,
1114          FontLayoutCmdOTF *otf_cmd, MGlyphString *gstring, int from, int to,
1115          FontLayoutContext *ctx)
1116 {
1117 #ifdef HAVE_OTF
1118   int gidx = gstring->used;
1119   MGlyph *g = MGLYPH (from), *gend = MGLYPH (to);
1120
1121   for (; g < gend; g++)
1122     g->otf_cmd = otf_cmd;
1123
1124   to = mfont__ft_drive_gsub (gstring, from, to);
1125   if (gidx < gstring->used)
1126     MGLYPH (gidx)->left_padding = ctx->left_padding;
1127 #endif
1128   return to;
1129 }
1130
1131 static int
1132 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1133              FontLayoutContext *ctx)
1134 {
1135   MGlyph g;
1136
1137   if (id >= 0)
1138     {
1139       int i;
1140
1141       /* Direct code (== id + ctx->code_offset) output.
1142          The source is not consumed.  */
1143       if (from < to)
1144         g = *(MGLYPH (from));
1145       else
1146         g = *(MGLYPH (from - 1));
1147       g.type = GLYPH_CHAR;
1148       g.code = ctx->code_offset + id;
1149       MDEBUG_PRINT1 (" (DIRECT 0x%X", g.code);
1150       if (ctx->combining_code)
1151         g.combining_code = ctx->combining_code;
1152       if (ctx->left_padding)
1153         g.left_padding = ctx->left_padding;
1154       for (i = from; i < to; i++)
1155         {
1156           MGlyph *tmp = MGLYPH (i);
1157
1158           if (g.pos > tmp->pos)
1159             g.pos = tmp->pos;
1160           else if (g.to < tmp->to)
1161             g.to = tmp->to;
1162         }
1163       APPEND_GLYPH (gstring, g);
1164       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1165       MDEBUG_PRINT (")");
1166       return (from);
1167     }
1168
1169   if (id <= CMD_ID_OFFSET_INDEX)
1170     {
1171       int idx = CMD_ID_TO_INDEX (id);
1172       FontLayoutCmd *cmd;
1173
1174       if (idx >= ctx->stage->used)
1175         MERROR (MERROR_DRAW, -1);
1176       cmd = ctx->stage->cmds + idx;
1177       if (cmd->type == FontLayoutCmdTypeRule)
1178         to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1179       else if (cmd->type == FontLayoutCmdTypeCond)
1180         to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1181       else if (cmd->type == FontLayoutCmdTypeOTF)
1182         to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1183
1184       if (to < 0)
1185         return -1;
1186       return to;
1187     }
1188
1189   if (id <= CMD_ID_OFFSET_COMBINING)
1190     {
1191       ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1192       return from;
1193     }
1194
1195   switch (id)
1196     {
1197     case CMD_ID_COPY:
1198       {
1199         if (from >= to)
1200           return from;
1201         g = *(MGLYPH (from));
1202         if (ctx->combining_code)
1203           g.combining_code = ctx->combining_code;
1204         if (ctx->left_padding)
1205           g.left_padding = ctx->left_padding;
1206         APPEND_GLYPH (gstring, g);
1207         ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1208         return (from + 1);
1209       }
1210
1211     case CMD_ID_CLUSTER_BEGIN:
1212       if (! ctx->cluster_begin_idx)
1213         {
1214           MDEBUG_PRINT1 (" <%d", MGLYPH (from)->pos);
1215           ctx->cluster_begin_idx = gstring->used;
1216           ctx->cluster_begin_pos = MGLYPH (from)->pos;
1217           ctx->cluster_end_pos = MGLYPH (from)->to;
1218         }
1219       return from;
1220
1221     case CMD_ID_CLUSTER_END:
1222       if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1223         {
1224           int i;
1225
1226           MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1227           for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1228             {
1229               MGLYPH (i)->pos = ctx->cluster_begin_pos;
1230               MGLYPH (i)->to = ctx->cluster_end_pos;
1231             }
1232           ctx->cluster_begin_idx = 0;
1233         }
1234       return from;
1235
1236     case CMD_ID_SEPARATOR:
1237       {
1238         if (from < to)
1239           g = *(MGLYPH (from));
1240         else
1241           g = *(MGLYPH (from - 1));
1242         g.type = GLYPH_PAD;
1243         /* g.c = g.code = 0; */
1244         g.width = 0;
1245         APPEND_GLYPH (gstring, g);
1246         return from;
1247       }
1248
1249     case CMD_ID_LEFT_PADDING:
1250       ctx->left_padding = 1;
1251       return from;
1252
1253     case CMD_ID_RIGHT_PADDING:
1254       if (gstring->used > 0)
1255         gstring->glyphs[gstring->used - 1].right_padding = 1;
1256       return from;
1257     }
1258
1259   MERROR (MERROR_DRAW, -1);
1260 }
1261
1262 \f
1263 /* Internal API */
1264
1265 int
1266 mfont__flt_init (void)
1267 {
1268   Mcond = msymbol ("cond");
1269   Mrange = msymbol ("range");
1270   Mlayouter = msymbol ("layouter");
1271   flt_list = mplist ();
1272   return 0;
1273 }
1274
1275 void
1276 mfont__flt_fini (void)
1277 {
1278   MPlist *plist, *pl;
1279
1280   MPLIST_DO (plist, flt_list)
1281     {
1282       pl = MPLIST_PLIST (plist);
1283       if (pl)
1284         {
1285           MPLIST_DO (pl, MPLIST_NEXT (pl))
1286             free_flt_stage (MPLIST_VAL (pl));
1287           pl = MPLIST_PLIST (plist);
1288           M17N_OBJECT_UNREF (pl);
1289         }
1290     }
1291   M17N_OBJECT_UNREF (flt_list);
1292 }
1293
1294 unsigned
1295 mfont__flt_encode_char (MSymbol layouter_name, int c)
1296 {
1297   MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1298   MCharTable *table;
1299   unsigned code;
1300
1301   if (! layouter)
1302     return MCHAR_INVALID_CODE;
1303   table = MPLIST_VAL (layouter);
1304   code = (unsigned) mchartable_lookup (table, c);
1305   return (code ? code : MCHAR_INVALID_CODE);
1306 }
1307
1308 int
1309 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1310 {
1311   int stage_idx = 0;
1312   int gidx;
1313   int i;
1314   FontLayoutContext ctx;
1315   MCharTable *table;
1316   int encoded_len;
1317   int match_indices[NMATCH];
1318   MSymbol layouter_name = rface->rfont->layouter;
1319   MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1320   MRealizedFace *ascii_rface = rface->ascii_rface;
1321   FontLayoutStage *stage;
1322   int from_pos, to_pos;
1323   MGlyph dummy;
1324
1325   if (! layouter)
1326     {
1327       /* FLT not found.  Make all glyphs invisible.  */
1328       while (from < to)
1329         gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1330       return to;
1331     }
1332
1333   dummy = gstring->glyphs[from];
1334   MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1335
1336   /* Setup CTX.  */
1337   memset (&ctx, 0, sizeof ctx);
1338   table = MPLIST_VAL (layouter);
1339   layouter = MPLIST_NEXT (layouter);
1340   stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1341   gidx = from;
1342   /* Find previous glyphs that are also supported by the layouter.  */
1343   while (gidx > 1
1344          && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1345     gidx--;
1346   /* + 2 is for a separator ' ' and a terminator '\0'.  */
1347   encoded_len = gstring->used - gidx + 2;
1348   ctx.encoded = (char *) alloca (encoded_len);
1349
1350   for (i = 0; gidx < from; i++, gidx++)
1351     ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1352
1353   ctx.encoded[i++] = ' ';
1354   ctx.encoded_offset = from - i;
1355
1356   /* Now each MGlyph->code contains encoded char.  Set it in
1357      ctx.encoded[], and set MGlyph->c to MGlyph->code.  */
1358   for (gidx = from; gidx < to ; i++, gidx++)
1359     {
1360       ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1361       MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1362     }
1363   ctx.encoded[i++] = '\0';
1364
1365   match_indices[0] = from;
1366   match_indices[1] = to;
1367   for (i = 2; i < NMATCH; i++)
1368     match_indices[i] = -1;
1369   ctx.match_indices = match_indices;
1370
1371   from_pos = MGLYPH (from)->pos;
1372   to_pos = MGLYPH (to)->pos;
1373
1374   for (stage_idx = 0; 1; stage_idx++)
1375     {
1376       int len = to - from;
1377       int result;
1378
1379       MDEBUG_PRINT1 ("\n [FLT]   (STAGE %d", stage_idx);
1380       gidx = gstring->used;
1381       ctx.stage = stage;
1382
1383       result = run_command (2, INDEX_TO_CMD_ID (0), gstring,
1384                             ctx.encoded_offset, to, &ctx);
1385       MDEBUG_PRINT (")");
1386       if (result < 0)
1387         return -1;
1388       to = from + (gstring->used - gidx);
1389       REPLACE_GLYPHS (gstring, gidx, from, len);
1390
1391       layouter = MPLIST_NEXT (layouter);
1392       /* If this is the last stage, break the loop. */
1393       if (MPLIST_TAIL_P (layouter))
1394         break;
1395
1396       /* Otherwise, prepare for the next iteration.   */
1397       stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1398       table = stage->category;
1399       if (to - from >= encoded_len)
1400         {
1401           encoded_len = to + 1;
1402           ctx.encoded = (char *) alloca (encoded_len);
1403         }
1404
1405       for (i = from; i < to; i++)
1406         {
1407           MGlyph *g = MGLYPH (i);
1408
1409           if (g->type == GLYPH_PAD)
1410             ctx.encoded[i - from] = ' ';
1411           else if (! g->otf_encoded)
1412             ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1413 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1414           else
1415             {
1416               int c = mfont__ft_decode_otf (g);
1417
1418               if (c >= 0)
1419                 {
1420                   c = (int) mchartable_lookup (table, c);
1421                   if (! c)
1422                     c = -1;
1423                 }
1424               ctx.encoded[i - from] = (c >= 0 ? c : 1);
1425             }
1426 #endif  /* HAVE_FREETYPE && HAVE_OTF */
1427         }
1428       ctx.encoded[i - from] = '\0';
1429       ctx.encoded_offset = from;
1430       ctx.match_indices[0] = from;
1431       ctx.match_indices[1] = to;
1432     }
1433
1434   MDEBUG_PRINT (")\n");
1435
1436   if (from == to)
1437     {
1438       /* Somehow there's no glyph contributing to characters between
1439          FROM_POS and TO_POS.  We must add one dummy space glyph for
1440          those characters.  */
1441       MGlyph g;
1442
1443       g.type = GLYPH_SPACE;
1444       g.c = ' ', g.code = ' ';
1445       g.pos = from_pos, g.to = to_pos;
1446       g.rface = ascii_rface;
1447       INSERT_GLYPH (gstring, from, g);
1448       to = from + 1;
1449     }
1450   else
1451     {
1452       /* Get actual glyph IDs of glyphs.  Also check if all characters
1453          in the range is covered by some glyph(s).  If not, change
1454          <pos> and <to> of glyphs to cover uncovered characters.  */
1455       int len = to_pos - from_pos;
1456       int pos;
1457       MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1458       MGlyph *g, *gend = MGLYPH (to);
1459       MGlyph *latest = gend;
1460
1461       for (g = MGLYPH (from); g != gend; g++)
1462         if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1463           g->code
1464             = (rface->rfont->driver->encode_char) (rface->rfont, g->code);
1465       for (i = 0; i < len; i++)
1466         glyphs[i] = NULL;
1467       for (g = MGLYPH (from); g != gend; g++)
1468         {
1469           if (g->pos < latest->pos)
1470             latest = g;
1471           if (! glyphs[g->pos - from_pos])
1472             {
1473               for (i = g->pos; i < g->to; i++)
1474                 glyphs[i - from_pos] = g;
1475             }
1476         }
1477       i = 0;
1478       if (! glyphs[0])
1479         {
1480           pos = latest->pos;
1481           for (g = latest; g->pos == pos; g++)
1482             g->pos = from_pos;
1483           i++;
1484         }
1485       for (; i < len; i++)
1486         {
1487           if (! glyphs[i])
1488             {
1489               for (g = latest; g->pos == latest->pos; g++)
1490                 g->to = from_pos + i + 1;
1491             }
1492           else
1493             latest = glyphs[i];
1494         }
1495     }
1496   return to;
1497 }
1498
1499 \f
1500 /* for debugging... */
1501
1502 static void
1503 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1504 {
1505   char *prefix = (char *) alloca (indent + 1);
1506
1507   memset (prefix, 32, indent);
1508   prefix[indent] = 0;
1509
1510   if (id >= 0)
1511     fprintf (stderr, "0x%02X", id);
1512   else if (id <= CMD_ID_OFFSET_INDEX)
1513     {
1514       int idx = CMD_ID_TO_INDEX (id);
1515       FontLayoutCmd *cmd = stage->cmds + idx;
1516
1517       if (cmd->type == FontLayoutCmdTypeRule)
1518         {
1519           FontLayoutCmdRule *rule = &cmd->body.rule;
1520           int i;
1521
1522           fprintf (stderr, "(rule ");
1523           if (rule->src_type == SRC_REGEX)
1524             fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1525           else if (rule->src_type == SRC_INDEX)
1526             fprintf (stderr, "%d", rule->src.match_idx);
1527           else if (rule->src_type == SRC_SEQ)
1528             fprintf (stderr, "(seq)");
1529           else if (rule->src_type == SRC_RANGE)
1530             fprintf (stderr, "(range)");
1531           else
1532             fprintf (stderr, "(invalid src)");
1533
1534           for (i = 0; i < rule->n_cmds; i++)
1535             {
1536               fprintf (stderr, "\n%s  ", prefix);
1537               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1538             }
1539           fprintf (stderr, ")");
1540         }
1541       else if (cmd->type == FontLayoutCmdTypeCond)
1542         {
1543           FontLayoutCmdCond *cond = &cmd->body.cond;
1544           int i;
1545
1546           fprintf (stderr, "(cond");
1547           for (i = 0; i < cond->n_cmds; i++)
1548             {
1549               fprintf (stderr, "\n%s  ", prefix);
1550               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1551             }
1552           fprintf (stderr, ")");
1553         }
1554       else if (cmd->type == FontLayoutCmdTypeOTF)
1555         {
1556           fprintf (stderr, "(otf)");
1557         }
1558       else
1559         fprintf (stderr, "(error-command)");
1560     }
1561   else if (id <= CMD_ID_OFFSET_COMBINING)
1562     fprintf (stderr, "cominging-code");
1563   else
1564     fprintf (stderr, "(predefiend %d)", id);
1565 }
1566
1567 void
1568 dump_flt (MFontLayoutTable *flt, int indent)
1569 {
1570   char *prefix = (char *) alloca (indent + 1);
1571   MPlist *plist;
1572   int stage_idx = 0;
1573
1574   memset (prefix, 32, indent);
1575   prefix[indent] = 0;
1576   fprintf (stderr, "(flt");
1577   MPLIST_DO (plist, flt)
1578     {
1579       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1580       int i;
1581
1582       fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
1583       for (i = 0; i < stage->used; i++)
1584         {
1585           fprintf (stderr, "\n%s    ", prefix);
1586           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1587         }
1588       fprintf (stderr, ")");
1589       stage_idx++;
1590     }
1591   fprintf (stderr, ")");
1592 }