07d3a7dc6a878440fcebbe294769fc1ab2a4c72b
[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   to = mfont__ft_drive_otf (gstring, from, to,
1131                             otf_cmd->script, otf_cmd->langsys,
1132                             otf_cmd->gsub_features, otf_cmd->gpos_features);
1133 #endif
1134   return to;
1135 }
1136
1137 static int
1138 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1139              FontLayoutContext *ctx)
1140 {
1141   MGlyph g;
1142
1143   if (id >= 0)
1144     {
1145       int i;
1146
1147       /* Direct code (== id + ctx->code_offset) output.
1148          The source is not consumed.  */
1149       if (from < to)
1150         g = *(MGLYPH (from));
1151       else
1152         g = *(MGLYPH (from - 1));
1153       g.type = GLYPH_CHAR;
1154       g.code = ctx->code_offset + id;
1155       MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "", g.code);
1156       if (ctx->combining_code)
1157         g.combining_code = ctx->combining_code;
1158       if (ctx->left_padding)
1159         g.left_padding = ctx->left_padding;
1160       for (i = from; i < to; i++)
1161         {
1162           MGlyph *tmp = MGLYPH (i);
1163
1164           if (g.pos > tmp->pos)
1165             g.pos = tmp->pos;
1166           else if (g.to < tmp->to)
1167             g.to = tmp->to;
1168         }
1169       APPEND_GLYPH (gstring, g);
1170       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1171       MDEBUG_PRINT (")");
1172       return (from);
1173     }
1174
1175   if (id <= CMD_ID_OFFSET_INDEX)
1176     {
1177       int idx = CMD_ID_TO_INDEX (id);
1178       FontLayoutCmd *cmd;
1179
1180       if (idx >= ctx->stage->used)
1181         MERROR (MERROR_DRAW, -1);
1182       cmd = ctx->stage->cmds + idx;
1183       if (cmd->type == FontLayoutCmdTypeRule)
1184         to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1185       else if (cmd->type == FontLayoutCmdTypeCond)
1186         to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1187       else if (cmd->type == FontLayoutCmdTypeOTF)
1188         to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1189
1190       if (to < 0)
1191         return -1;
1192       return to;
1193     }
1194
1195   if (id <= CMD_ID_OFFSET_COMBINING)
1196     {
1197       ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1198       return from;
1199     }
1200
1201   switch (id)
1202     {
1203     case CMD_ID_COPY:
1204       {
1205         if (from >= to)
1206           return from;
1207         g = *(MGLYPH (from));
1208         if (ctx->combining_code)
1209           g.combining_code = ctx->combining_code;
1210         if (ctx->left_padding)
1211           g.left_padding = ctx->left_padding;
1212         APPEND_GLYPH (gstring, g);
1213         MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g.code);
1214         ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1215         return (from + 1);
1216       }
1217
1218     case CMD_ID_CLUSTER_BEGIN:
1219       if (! ctx->cluster_begin_idx)
1220         {
1221           MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "", MGLYPH (from)->pos);
1222           ctx->cluster_begin_idx = gstring->used;
1223           ctx->cluster_begin_pos = MGLYPH (from)->pos;
1224           ctx->cluster_end_pos = MGLYPH (from)->to;
1225         }
1226       return from;
1227
1228     case CMD_ID_CLUSTER_END:
1229       if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1230         {
1231           int i;
1232
1233           MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1234           for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1235             {
1236               MGLYPH (i)->pos = ctx->cluster_begin_pos;
1237               MGLYPH (i)->to = ctx->cluster_end_pos;
1238             }
1239           ctx->cluster_begin_idx = 0;
1240         }
1241       return from;
1242
1243     case CMD_ID_SEPARATOR:
1244       {
1245         if (from < to)
1246           g = *(MGLYPH (from));
1247         else
1248           g = *(MGLYPH (from - 1));
1249         g.type = GLYPH_PAD;
1250         /* g.c = g.code = 0; */
1251         g.width = 0;
1252         APPEND_GLYPH (gstring, g);
1253         return from;
1254       }
1255
1256     case CMD_ID_LEFT_PADDING:
1257       ctx->left_padding = 1;
1258       return from;
1259
1260     case CMD_ID_RIGHT_PADDING:
1261       if (gstring->used > 0)
1262         gstring->glyphs[gstring->used - 1].right_padding = 1;
1263       return from;
1264     }
1265
1266   MERROR (MERROR_DRAW, -1);
1267 }
1268
1269 \f
1270 /* Internal API */
1271
1272 int
1273 mfont__flt_init (void)
1274 {
1275   Mcond = msymbol ("cond");
1276   Mrange = msymbol ("range");
1277   Mlayouter = msymbol ("layouter");
1278   flt_list = mplist ();
1279   return 0;
1280 }
1281
1282 void
1283 mfont__flt_fini (void)
1284 {
1285   MPlist *plist, *pl;
1286
1287   MPLIST_DO (plist, flt_list)
1288     {
1289       pl = MPLIST_PLIST (plist);
1290       if (pl)
1291         {
1292           MPLIST_DO (pl, MPLIST_NEXT (pl))
1293             free_flt_stage (MPLIST_VAL (pl));
1294           pl = MPLIST_PLIST (plist);
1295           M17N_OBJECT_UNREF (pl);
1296         }
1297     }
1298   M17N_OBJECT_UNREF (flt_list);
1299 }
1300
1301 unsigned
1302 mfont__flt_encode_char (MSymbol layouter_name, int c)
1303 {
1304   MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1305   MCharTable *table;
1306   unsigned code;
1307
1308   if (! layouter)
1309     return MCHAR_INVALID_CODE;
1310   table = MPLIST_VAL (layouter);
1311   code = (unsigned) mchartable_lookup (table, c);
1312   return (code ? code : MCHAR_INVALID_CODE);
1313 }
1314
1315 int
1316 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1317 {
1318   int stage_idx = 0;
1319   int gidx;
1320   int i;
1321   FontLayoutContext ctx;
1322   MCharTable *table;
1323   int encoded_len;
1324   int match_indices[NMATCH];
1325   MSymbol layouter_name = rface->rfont->layouter;
1326   MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1327   MRealizedFace *ascii_rface = rface->ascii_rface;
1328   FontLayoutStage *stage;
1329   int from_pos, to_pos;
1330   MGlyph dummy;
1331
1332   if (! layouter)
1333     {
1334       /* FLT not found.  Make all glyphs invisible.  */
1335       while (from < to)
1336         gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1337       return to;
1338     }
1339
1340   dummy = gstring->glyphs[from];
1341   MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1342
1343   /* Setup CTX.  */
1344   memset (&ctx, 0, sizeof ctx);
1345   table = MPLIST_VAL (layouter);
1346   layouter = MPLIST_NEXT (layouter);
1347   stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1348   gidx = from;
1349   /* Find previous glyphs that are also supported by the layouter.  */
1350   while (gidx > 1
1351          && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1352     gidx--;
1353   /* + 2 is for a separator ' ' and a terminator '\0'.  */
1354   encoded_len = gstring->used - gidx + 2;
1355   ctx.encoded = (char *) alloca (encoded_len);
1356
1357   for (i = 0; gidx < from; i++, gidx++)
1358     ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1359
1360   ctx.encoded[i++] = ' ';
1361   ctx.encoded_offset = from - i;
1362
1363   /* Now each MGlyph->code contains encoded char.  Set it in
1364      ctx.encoded[], and set MGlyph->c to MGlyph->code.  */
1365   for (gidx = from; gidx < to ; i++, gidx++)
1366     {
1367       ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1368       MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1369     }
1370   ctx.encoded[i++] = '\0';
1371
1372   match_indices[0] = from;
1373   match_indices[1] = to;
1374   for (i = 2; i < NMATCH; i++)
1375     match_indices[i] = -1;
1376   ctx.match_indices = match_indices;
1377
1378   from_pos = MGLYPH (from)->pos;
1379   to_pos = MGLYPH (to)->pos;
1380
1381   for (stage_idx = 0; 1; stage_idx++)
1382     {
1383       int len = to - from;
1384       int result;
1385
1386       MDEBUG_PRINT2 ("\n [FLT]   (STAGE %d \"%s\"", stage_idx, ctx.encoded);
1387       if (mdebug__flag & mdebug_mask
1388           && ctx.encoded_offset < to)
1389         {
1390           if (gstring->glyphs[ctx.encoded_offset].type == GLYPH_PAD)
1391             fprintf (stderr, " (|");
1392           else
1393             fprintf (stderr, " (%X", gstring->glyphs[ctx.encoded_offset].code);
1394           for (i = ctx.encoded_offset + 1; i < to; i++)
1395             {
1396               if (gstring->glyphs[i].type == GLYPH_PAD)
1397                 fprintf (stderr, " |");
1398               else
1399                 fprintf (stderr, " %X", gstring->glyphs[i].code);
1400             }
1401           fprintf (stderr, ")");
1402         }
1403
1404       gidx = gstring->used;
1405       ctx.stage = stage;
1406
1407       result = run_command (4, INDEX_TO_CMD_ID (0), gstring,
1408                             ctx.encoded_offset, to, &ctx);
1409       MDEBUG_PRINT (")");
1410       if (result < 0)
1411         return -1;
1412       to = from + (gstring->used - gidx);
1413       REPLACE_GLYPHS (gstring, gidx, from, len);
1414
1415       layouter = MPLIST_NEXT (layouter);
1416       /* If this is the last stage, break the loop. */
1417       if (MPLIST_TAIL_P (layouter))
1418         break;
1419
1420       /* Otherwise, prepare for the next iteration.   */
1421       stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1422       table = stage->category;
1423       if (to - from >= encoded_len)
1424         {
1425           encoded_len = to + 1;
1426           ctx.encoded = (char *) alloca (encoded_len);
1427         }
1428
1429       for (i = from; i < to; i++)
1430         {
1431           MGlyph *g = MGLYPH (i);
1432
1433           if (g->type == GLYPH_PAD)
1434             ctx.encoded[i - from] = ' ';
1435           else if (! g->otf_encoded)
1436             ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1437 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1438           else
1439             {
1440               int c = mfont__ft_decode_otf (g);
1441
1442               if (c >= 0)
1443                 {
1444                   c = (int) mchartable_lookup (table, c);
1445                   if (! c)
1446                     c = -1;
1447                 }
1448               ctx.encoded[i - from] = (c >= 0 ? c : 1);
1449             }
1450 #endif  /* HAVE_FREETYPE && HAVE_OTF */
1451         }
1452       ctx.encoded[i - from] = '\0';
1453       ctx.encoded_offset = from;
1454       ctx.match_indices[0] = from;
1455       ctx.match_indices[1] = to;
1456     }
1457
1458   MDEBUG_PRINT (")\n");
1459
1460   if (from == to)
1461     {
1462       /* Somehow there's no glyph contributing to characters between
1463          FROM_POS and TO_POS.  We must add one dummy space glyph for
1464          those characters.  */
1465       MGlyph g;
1466
1467       g.type = GLYPH_SPACE;
1468       g.c = ' ', g.code = ' ';
1469       g.pos = from_pos, g.to = to_pos;
1470       g.rface = ascii_rface;
1471       INSERT_GLYPH (gstring, from, g);
1472       to = from + 1;
1473     }
1474   else
1475     {
1476       /* Get actual glyph IDs of glyphs.  Also check if all characters
1477          in the range is covered by some glyph(s).  If not, change
1478          <pos> and <to> of glyphs to cover uncovered characters.  */
1479       int len = to_pos - from_pos;
1480       int pos;
1481       MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1482       MGlyph *g, *gend = MGLYPH (to);
1483       MGlyph *latest = gend;
1484
1485       for (g = MGLYPH (from); g != gend; g++)
1486         if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1487           g->code
1488             = (rface->rfont->driver->encode_char) (rface->rfont, g->code);
1489       for (i = 0; i < len; i++)
1490         glyphs[i] = NULL;
1491       for (g = MGLYPH (from); g != gend; g++)
1492         {
1493           if (g->pos < latest->pos)
1494             latest = g;
1495           if (! glyphs[g->pos - from_pos])
1496             {
1497               for (i = g->pos; i < g->to; i++)
1498                 glyphs[i - from_pos] = g;
1499             }
1500         }
1501       i = 0;
1502       if (! glyphs[0])
1503         {
1504           pos = latest->pos;
1505           for (g = latest; g->pos == pos; g++)
1506             g->pos = from_pos;
1507           i++;
1508         }
1509       for (; i < len; i++)
1510         {
1511           if (! glyphs[i])
1512             {
1513               for (g = latest; g->pos == latest->pos; g++)
1514                 g->to = from_pos + i + 1;
1515             }
1516           else
1517             latest = glyphs[i];
1518         }
1519     }
1520   return to;
1521 }
1522
1523 \f
1524 /* for debugging... */
1525
1526 static void
1527 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1528 {
1529   char *prefix = (char *) alloca (indent + 1);
1530
1531   memset (prefix, 32, indent);
1532   prefix[indent] = 0;
1533
1534   if (id >= 0)
1535     fprintf (stderr, "0x%02X", id);
1536   else if (id <= CMD_ID_OFFSET_INDEX)
1537     {
1538       int idx = CMD_ID_TO_INDEX (id);
1539       FontLayoutCmd *cmd = stage->cmds + idx;
1540
1541       if (cmd->type == FontLayoutCmdTypeRule)
1542         {
1543           FontLayoutCmdRule *rule = &cmd->body.rule;
1544           int i;
1545
1546           fprintf (stderr, "(rule ");
1547           if (rule->src_type == SRC_REGEX)
1548             fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1549           else if (rule->src_type == SRC_INDEX)
1550             fprintf (stderr, "%d", rule->src.match_idx);
1551           else if (rule->src_type == SRC_SEQ)
1552             fprintf (stderr, "(seq)");
1553           else if (rule->src_type == SRC_RANGE)
1554             fprintf (stderr, "(range)");
1555           else
1556             fprintf (stderr, "(invalid src)");
1557
1558           for (i = 0; i < rule->n_cmds; i++)
1559             {
1560               fprintf (stderr, "\n%s  ", prefix);
1561               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1562             }
1563           fprintf (stderr, ")");
1564         }
1565       else if (cmd->type == FontLayoutCmdTypeCond)
1566         {
1567           FontLayoutCmdCond *cond = &cmd->body.cond;
1568           int i;
1569
1570           fprintf (stderr, "(cond");
1571           for (i = 0; i < cond->n_cmds; i++)
1572             {
1573               fprintf (stderr, "\n%s  ", prefix);
1574               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1575             }
1576           fprintf (stderr, ")");
1577         }
1578       else if (cmd->type == FontLayoutCmdTypeOTF)
1579         {
1580           fprintf (stderr, "(otf)");
1581         }
1582       else
1583         fprintf (stderr, "(error-command)");
1584     }
1585   else if (id <= CMD_ID_OFFSET_COMBINING)
1586     fprintf (stderr, "cominging-code");
1587   else
1588     fprintf (stderr, "(predefiend %d)", id);
1589 }
1590
1591 void
1592 dump_flt (MFontLayoutTable *flt, int indent)
1593 {
1594   char *prefix = (char *) alloca (indent + 1);
1595   MPlist *plist;
1596   int stage_idx = 0;
1597
1598   memset (prefix, 32, indent);
1599   prefix[indent] = 0;
1600   fprintf (stderr, "(flt");
1601   MPLIST_DO (plist, flt)
1602     {
1603       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1604       int i;
1605
1606       fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
1607       for (i = 0; i < stage->used; i++)
1608         {
1609           fprintf (stderr, "\n%s    ", prefix);
1610           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1611         }
1612       fprintf (stderr, ")");
1613       stage_idx++;
1614     }
1615   fprintf (stderr, ")");
1616 }