*** empty log message ***
[m17n/m17n-lib.git] / src / font-flt.c
1 /* font-flt.c -- Font Layout Table sub-module.
2    Copyright (C) 2003, 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 #include "config.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <regex.h>
31
32 #include "m17n-gui.h"
33 #include "m17n-misc.h"
34 #include "internal.h"
35 #include "mtext.h"
36 #include "symbol.h"
37 #include "plist.h"
38 #include "internal-gui.h"
39 #include "font.h"
40 #include "face.h"
41
42 /* Font Layouter */
43
44 /* Font Layout Table (FLT)
45
46 Predefined terms: SYMBOL, INTEGER, STRING
47
48 FLT ::= '(' STAGE + ')'
49
50 STAGE ::= CATEGORY-TABLE ? FONT-LAYOUT-RULE
51
52 ;; Each STAGE consumes a source (code sequence) and produces another
53 ;; code sequence that is given to the next STAGE as a source.  The
54 ;; source given to the first stage is a sequence of character codes
55 ;; that are assigned category codes by CATEGORY-TABLE.  The output of
56 ;; the last stage is a glyph code sequence given to the renderer.
57
58 CATEGORY-TABLE ::=
59         '(' 'category' CATEGORY-SPEC + ')'
60 CATEGORY-SPEC ::=
61         '(' CODE [ CODE ] CATEGORY ')'
62 CODE ::= INTEGER
63 CATEGORY ::= INTEGER
64 ;; ASCII character codes of alphabet ('A' .. 'Z' 'a' .. 'z').
65 ;; Ex: CATEGORY-TABLE
66 ;; (category
67 ;;   (0x0900 0x097F     ?E)     ; All Devanagari characters
68 ;;   (0x093C            ?N))    ; DEVANAGARI-LETTER NUKTA
69 ;;      Assign the category 'E' to all Devanagari characters but 0x093C,
70 ;;      assign the category 'N' to 0x093C.
71
72 FONT-LAYOUT-RULE ::=
73         '(' 'generator' RULE MACRO-DEF * ')'
74
75 RULE ::= COMMAND | REGEXP-RULE | MATCH-RULE | MAP-RULE
76          | COND-STRUCT | MACRO-NAME
77
78 COMMAND ::=
79         DIRECT-CODE | COMBINING | PREDEFIND-COMMAND | OTF-COMMAND
80
81 DIRECT-CODE ::= INTEGER
82 ;; Always succeed.  Produce the code.  Consume no source.
83
84 PREDEFIND-COMMAND ::=
85         '=' | '*' | '<' | '>' | '|'
86
87 ;; '=': Succeed when the current run contains at least one code.
88 ;; Consume the first code in the current run, and produce it as is.
89
90 ;; '*': If the the previous command succeeded, repeat it until it
91 ;; fails.  
92
93 ;; '<': Produce a special code that indicates the start of grapheme
94 ;; cluster.  Succeed always, consume nothing.
95
96 ;; '>': Produce a special code that indicates the end of grapheme
97 ;; cluster.  Succeed always, consume nothing.
98
99 ;; '|': Produce a special code whose category is ' '.  Succeed always,
100 ;; consume nothing.
101
102 OTF-COMMAND ::=
103         'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]]
104 ;; Run the Open Type Layout Table on the current run.  Succeed always,
105 ;; consume nothing.
106
107 SCRIPT ::= OTF-TAG
108 ;;      OTF's ScriptTag name (four letters) listed at:
109 ;;      <http://www.microsoft.om/typograph/otspec/scripttags.htm>
110 LANGSYS ::= OTF-TAG
111 ;;      OTF's Language System name (four letters) listed at:
112 ;;      <http://www.microsoft.om/typograph/otspec/languagetags.htm>
113
114 GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
115 GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
116 FEATURE ::= OTF-TAG
117 ;;      OTF's Feature name (four letters) listed at:
118 ;;      <http://www.microsoft.om/typograph/otspec/???.htm>
119
120 OTF-TAG ::= PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR
121
122 ;; Ex. OTF-COMMAND
123 ;; 'otf:deva'
124 ;;      Run all features in the default langsys of 'deva' script.
125 ;; 'otf:deva::nukt:haln'
126 ;;      Run all GSUB features, run 'nukt' and 'haln' GPOS features.
127 ;; 'otf:deva:: :'
128 ;;      Run all GSUB features, run no GPOS features.
129
130 REGEXP-RULE ::=
131         '(' REGEXP RULE * ')'
132
133 ;; Succeed if REGXP matches the head of source.  Run RULEs while
134 ;; limiting the source to the matching part.  Consume that part.
135
136 REGEXP ::= STRING
137 ;; Must be composed only from ASCII characters.  'A' - 'Z', 'a' - 'z'
138 ;; correspond to CATEGORY.
139
140 ;; Ex: REGEXP-RULE
141 ;; ("VA?"
142 ;;   < | vowel * | >)
143
144 MATCH-RULE ::=
145         '(' MATCH-IDX RULE * ')'
146
147 ;; Succeed if the previous REGEXP-RULE found a matching part for
148 ;; MATCH-IDX.  Run RULEs while limiting the source to the matching
149 ;; part.  If MATCH-IDX is zero, consume the whole part, else consume
150 ;; nothing.
151
152 MATCH-IDX ::= INTEGER
153 ;; Must be 0..20.
154
155 ;; Ex. MATCH-RULE
156 ;; (2 consonant *)
157
158 MAP-RULE ::=
159         '(' ( SOURCE-SEQ | SOURCE-RANGE ) RULE * ')'
160
161 ;; Succeed if the source matches SOURCE-SEQ or SOURCE-RANGE.  Run
162 ;; RULEs while limiting the source to the matching part.  Consume that
163 ;; part.
164
165 SOURCE-SEQ ::=
166         '(' CODE + ')'
167 SOURCE-RANGE ::=
168         '(' 'range' CODE CODE ')'
169 ;; Ex. MAP-RULE
170 ;; ((0x0915 0x094D)             0x43)
171 ;;      If the source code sequence is 0x0915 0x094D, produce 0x43.
172 ;; ((range 0x0F40 0x0F6A)       0x2221)
173 ;;      If the first source code CODE is in the range 0x0F40..0x0F6A, 
174 ;;      produce (0x2221 + (CODE - 0x0F40)).
175
176 COND-STRUCT ::=
177         '(' 'cond' RULE + ')'
178
179 ;; Try each rule in sequence until one succeeds.  Succeed if one
180 ;; succeeds.  Consume nothing.
181
182 ;; Ex. COND-STRUCT
183 ;; (cond
184 ;;  ((0x0915 0x094D)            0x43)
185 ;;  ((range 0x0F40 0x0F6A)      0x2221)
186 ;;  = )
187
188 COMBINING ::= 'V''H''O''V''H'
189 V ::= ( 't' | 'c' | 'b' | 'B' )
190 H ::= ( 'l' | 'c' | 'r' )
191 O ::= ( '.' | XOFF | YOFF | XOFF YOFF )
192 XOFF ::= '<'INTEGER | '>'INTEGER 
193 YOFF ::= '+'INTEGER | '-'INTEGER
194 ;; INTEGER must be integer 0..127
195
196 ;; VH pair indicates 12 reference points of a glyph as below:
197 ;;
198 ;;   0----1----2 <---- ascent    0:tl (top-left)
199 ;;   |         |                 1:tc (top-center)
200 ;;   |         |                 2:tr (top-right)
201 ;;   |         |                 3:Bl (base-left)
202 ;;   9   10   11 <---- center    4:Bc (base-center)
203 ;;   |         |                 5:Br (base-right)
204 ;; --3----4----5-- <-- baseline  6:bl (bottom-left)
205 ;;   |         |                 7:bc (bottom-center)
206 ;;   6----7----8 <---- descent   8:br (bottom-right)
207 ;;                               9:cl (center-left)
208 ;;   |    |    |                10:cc (center-center)
209 ;; left center right            11:cr (center-right)
210 ;;
211 ;; Ex. COMBINING
212 ;; 'tc.bc':
213 ;;      Align top-left point of the previous glyph and bottom-center
214 ;;      point of the current glyph.
215 ;; 'Bl<20-10Br'
216 ;;      Align 20% left and 10% below of base-left point of the previous
217 ;;      glyph and base-right point of the current glyph.
218
219 MACRO-DEF ::=
220         '(' MACRO-NAME RULE + ')'
221 MACRO-NAME ::= SYMBOL
222
223 */
224
225 static int mdebug_mask = MDEBUG_FONT_FLT;
226
227 MSymbol Mlayouter;
228
229 static MPlist *flt_list;
230
231 /* Command ID:
232                  0 ...          : direct code
233                    -1           : invalid
234              -0x0F .. -2        : builtin commands
235         -0x100000F .. -0x10     : combining code
236                   ... -0x1000010: index to FontLayoutStage->cmds
237  */
238
239 #define INVALID_CMD_ID -1
240 #define CMD_ID_OFFSET_BUILTIN   -2
241 #define CMD_ID_OFFSET_COMBINING -0x10
242 #define CMD_ID_OFFSET_INDEX     -0x1000010
243
244 /* Builtin commands. */
245 #define CMD_ID_COPY             -2 /* '=' */
246 #define CMD_ID_REPEAT           -3 /* '*' */
247 #define CMD_ID_CLUSTER_BEGIN    -4 /* '<' */
248 #define CMD_ID_CLUSTER_END      -5 /* '>' */
249 #define CMD_ID_SEPARATOR        -6 /* '|' */
250 #define CMD_ID_LEFT_PADDING     -7 /* '[' */
251 #define CMD_ID_RIGHT_PADDING    -8 /* ']' */
252
253 #define CMD_ID_TO_COMBINING_CODE(id) (CMD_ID_OFFSET_COMBINING - (id))
254 #define COMBINING_CODE_TO_CMD_ID(code) (CMD_ID_OFFSET_COMBINING - (code))
255
256 #define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id))
257 #define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx))
258
259 static MSymbol Mcond, Mrange;
260
261 #define GLYPH_CODE_P(code)      \
262   ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX)
263
264 #define GLYPH_CODE_INDEX(code) ((code) - GLYPH_CODE_MIN)
265
266 enum FontLayoutCmdRuleSrcType
267   {
268     SRC_REGEX,
269     SRC_INDEX,
270     SRC_SEQ,
271     SRC_RANGE
272   };
273
274 typedef struct
275 {
276   enum FontLayoutCmdRuleSrcType src_type;
277   union {
278     struct {
279       char *pattern;
280       regex_t preg;
281     } re;
282     int match_idx;
283     struct {
284       int n_codes;
285       int *codes;
286     } seq;
287     struct {
288       int from, to;
289     } range;
290   } src;
291
292   int n_cmds;
293   int *cmd_ids;
294 } FontLayoutCmdRule;
295
296 typedef struct
297 {
298   /* Beginning and end indices of series of SEQ commands.  */
299   int seq_beg, seq_end;
300   /* Range of the first character appears in the above series.  */
301   int seq_from, seq_to;
302
303   int n_cmds;
304   int *cmd_ids;
305 } FontLayoutCmdCond;
306
307 typedef struct
308 {
309   MSymbol script;
310   MSymbol langsys;
311   MSymbol gsub_features;
312   MSymbol gpos_features;
313 } FontLayoutCmdOTF;
314
315 enum FontLayoutCmdType
316   {
317     FontLayoutCmdTypeRule,
318     FontLayoutCmdTypeCond,
319     FontLayoutCmdTypeOTF,
320     FontLayoutCmdTypeMAX
321   };
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   MFontLayoutTable *layouter = NULL;
860   MCharTable *category = NULL;
861
862   if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
863     MERROR_GOTO (MERROR_FONT, finish);
864   if (! (top = (MPlist *) mdatabase_load (mdb)))
865     MERROR_GOTO (0, finish);
866   if (! MPLIST_PLIST_P (top))
867     MERROR_GOTO (MERROR_FONT, finish);
868
869   MPLIST_DO (plist, top)
870     {
871       MSymbol sym;
872       MPlist *elt;
873
874       if (! MPLIST_PLIST (plist))
875         MERROR_GOTO (MERROR_FONT, finish);
876       elt = MPLIST_PLIST (plist);
877       if (! MPLIST_SYMBOL_P (elt))
878         MERROR_GOTO (MERROR_FONT, finish);
879       sym = MPLIST_SYMBOL (elt);
880       elt = MPLIST_NEXT (elt);
881       if (! elt)
882         MERROR_GOTO (MERROR_FONT, finish);
883       if (sym == Mcategory)
884         {
885           if (category)
886             M17N_OBJECT_UNREF (category);
887           category = load_category_table (elt);
888         }
889       else if (sym == Mgenerator)
890         {
891           FontLayoutStage *stage;
892
893           if (! category)
894             MERROR_GOTO (MERROR_FONT, finish);
895           stage = load_generator (elt);
896           if (! stage)
897             MERROR_GOTO (MERROR_FONT, finish);
898           stage->category = category;
899           M17N_OBJECT_REF (category);
900           if (! layouter)
901             {
902               layouter = mplist ();
903               /* Here don't do M17N_OBJECT_REF (category) because we
904                  don't unref the value of the element added below.  */
905               mplist_add (layouter, Mcategory, category);
906             }
907           mplist_add (layouter, Mt, stage);
908         }
909       else
910         MERROR_GOTO (MERROR_FONT, finish);
911     }
912
913   if (category)
914     M17N_OBJECT_UNREF (category);
915
916  finish:
917   M17N_OBJECT_UNREF (top);
918   mplist_add (flt_list, layouter_name, layouter);
919   return layouter;
920 }
921
922
923 static void
924 free_flt_stage (FontLayoutStage *stage)
925 {
926   int i;
927
928   M17N_OBJECT_UNREF (stage->category);
929   for (i = 0; i < stage->used; i++)
930     free_flt_command (stage->cmds + i);
931   MLIST_FREE1 (stage, cmds);
932   free (stage);
933 }
934
935
936 static MFontLayoutTable *
937 get_font_layout_table (MSymbol layouter_name)
938 {
939   MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
940
941   return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
942 }
943
944
945 /* FLS (Font Layout Service) */
946
947 /* Structure to hold information about a context of FLS.  */
948
949 typedef struct
950 {
951   /* Pointer to the current stage.  */
952   FontLayoutStage *stage;
953
954   /* Encode each MGlyph->code by the current category table into this
955      array.  An element is a category.  */
956   char *encoded;
957   /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
958      index GIDX.  */
959   int encoded_offset;
960   int *match_indices;
961   int code_offset;
962   int cluster_begin_idx;
963   int cluster_begin_pos;
964   int cluster_end_pos;
965   int combining_code;
966   int left_padding;
967   MRealizedFont *rfont;
968 } FontLayoutContext;
969
970 static int run_command (int depth,
971                         int, MGlyphString *, int, int, FontLayoutContext *);
972
973 #define NMATCH 20
974
975 static int
976 run_rule (int depth,
977           FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
978           FontLayoutContext *ctx)
979 {
980   int *saved_match_indices = ctx->match_indices;
981   int match_indices[NMATCH * 2];
982   int consumed;
983   int i;
984   int orig_from = from;
985
986   if (ctx->cluster_begin_idx)
987     {
988       if (ctx->cluster_begin_pos > MGLYPH (from)->pos)
989         ctx->cluster_begin_pos = MGLYPH (from)->pos;
990       if (ctx->cluster_end_pos < MGLYPH (to)->pos)
991         ctx->cluster_end_pos = MGLYPH (to)->pos;
992     }
993
994   if (rule->src_type == SRC_SEQ)
995     {
996       int len;
997
998       len = rule->src.seq.n_codes;
999       if (len > (to - from))
1000         return 0;
1001       for (i = 0; i < len; i++)
1002         if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
1003           break;
1004       if (i < len)
1005         return 0;
1006       to = from + len;
1007       MDEBUG_PRINT1 (" (SEQ 0x%X", rule->src.seq.codes[0]);
1008     }
1009   else if (rule->src_type == SRC_RANGE)
1010     {
1011       int head;
1012
1013       if (from >= to)
1014         return 0;
1015       head = gstring->glyphs[from].code;
1016       if (head < rule->src.range.from || head > rule->src.range.to)
1017         return 0;
1018       ctx->code_offset = head - rule->src.range.from;
1019       to = from + 1;
1020       MDEBUG_PRINT2 (" (RANGE 0x%X-0x%X",
1021                      rule->src.range.from, rule->src.range.to);
1022     }
1023   else if (rule->src_type == SRC_REGEX)
1024     {
1025       regmatch_t pmatch[NMATCH];
1026       char saved_code;
1027       int result;
1028
1029       if (from > to)
1030         return 0;
1031       saved_code = ctx->encoded[to - ctx->encoded_offset];
1032       ctx->encoded[to - ctx->encoded_offset] = '\0';
1033       result = regexec (&(rule->src.re.preg),
1034                         ctx->encoded + from - ctx->encoded_offset,
1035                         NMATCH, pmatch, 0);
1036       if (result == 0 && pmatch[0].rm_so == 0)
1037         {
1038           MDEBUG_PRINT3 (" (REGEX \"%s\" \"%s\" %d",
1039                          rule->src.re.pattern,
1040                          ctx->encoded + from - ctx->encoded_offset,
1041                          pmatch[0].rm_eo);
1042           ctx->encoded[to - ctx->encoded_offset] = saved_code;
1043           for (i = 0; i < NMATCH; i++)
1044             {
1045               if (pmatch[i].rm_so < 0)
1046                 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1047               else
1048                 {
1049                   match_indices[i * 2] = from + pmatch[i].rm_so;
1050                   match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1051                 }
1052             }
1053           ctx->match_indices = match_indices;
1054           to = match_indices[1];
1055         }
1056       else
1057         {
1058           ctx->encoded[to - ctx->encoded_offset] = saved_code;
1059           return 0;
1060         }
1061     }
1062   else if (rule->src_type == SRC_INDEX)
1063     {
1064       if (rule->src.match_idx >= NMATCH)
1065         return 0;
1066       from = ctx->match_indices[rule->src.match_idx * 2];
1067       if (from < 0)
1068         return 0;
1069       to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1070       MDEBUG_PRINT1 (" (INDEX %d", rule->src.match_idx);
1071     }
1072
1073   consumed = 0;
1074   depth++;
1075   for (i = 0; i < rule->n_cmds; i++)
1076     {
1077       int pos;
1078
1079       if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1080         {
1081           if (! consumed)
1082             continue;
1083           i--;
1084         }
1085       pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1086       if (pos < 0)
1087         MERROR (MERROR_DRAW, -1);
1088       consumed = pos > from;
1089       if (consumed)
1090         from = pos;
1091     }
1092
1093   ctx->match_indices = saved_match_indices;
1094   MDEBUG_PRINT (")");
1095   return (rule->src_type == SRC_INDEX ? orig_from : to);
1096 }
1097
1098 static int
1099 run_cond (int depth,
1100           FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1101           FontLayoutContext *ctx)
1102 {
1103   int i, pos = 0;
1104
1105   MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1106   depth++;
1107   for (i = 0; i < cond->n_cmds; i++)
1108     {
1109       /* TODO: Write a code for optimization utilizaing the info
1110          cond->seq_XXX.  */
1111       if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1112           != 0)
1113         break;
1114     }
1115   if (pos < 0)
1116     MERROR (MERROR_DRAW, -1);
1117   MDEBUG_PRINT (")");
1118   return (pos);
1119 }
1120
1121 static int
1122 run_otf (int depth,
1123          FontLayoutCmdOTF *otf_cmd, MGlyphString *gstring, int from, int to,
1124          FontLayoutContext *ctx)
1125 {
1126 #ifdef HAVE_OTF
1127   int gidx = gstring->used;
1128
1129   to = mfont__ft_drive_otf (gstring, from, to, ctx->rfont,
1130                             otf_cmd->script, otf_cmd->langsys,
1131                             otf_cmd->gsub_features, otf_cmd->gpos_features);
1132   if (gidx < gstring->used)
1133     MGLYPH (gidx)->left_padding = ctx->left_padding;
1134 #endif
1135   return to;
1136 }
1137
1138 static int
1139 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1140              FontLayoutContext *ctx)
1141 {
1142   MGlyph g;
1143
1144   if (id >= 0)
1145     {
1146       int i;
1147
1148       /* Direct code (== id + ctx->code_offset) output.
1149          The source is not consumed.  */
1150       if (from < to)
1151         g = *(MGLYPH (from));
1152       else
1153         g = *(MGLYPH (from - 1));
1154       g.type = GLYPH_CHAR;
1155       g.code = ctx->code_offset + id;
1156       MDEBUG_PRINT1 (" (DIRECT 0x%X", g.code);
1157       if (ctx->combining_code)
1158         g.combining_code = ctx->combining_code;
1159       if (ctx->left_padding)
1160         g.left_padding = ctx->left_padding;
1161       for (i = from; i < to; i++)
1162         {
1163           MGlyph *tmp = MGLYPH (i);
1164
1165           if (g.pos > tmp->pos)
1166             g.pos = tmp->pos;
1167           else if (g.to < tmp->to)
1168             g.to = tmp->to;
1169         }
1170       APPEND_GLYPH (gstring, g);
1171       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1172       MDEBUG_PRINT (")");
1173       return (from);
1174     }
1175
1176   if (id <= CMD_ID_OFFSET_INDEX)
1177     {
1178       int idx = CMD_ID_TO_INDEX (id);
1179       FontLayoutCmd *cmd;
1180
1181       if (idx >= ctx->stage->used)
1182         MERROR (MERROR_DRAW, -1);
1183       cmd = ctx->stage->cmds + idx;
1184       if (cmd->type == FontLayoutCmdTypeRule)
1185         to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1186       else if (cmd->type == FontLayoutCmdTypeCond)
1187         to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1188       else if (cmd->type == FontLayoutCmdTypeOTF)
1189         to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1190
1191       if (to < 0)
1192         return -1;
1193       return to;
1194     }
1195
1196   if (id <= CMD_ID_OFFSET_COMBINING)
1197     {
1198       ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1199       return from;
1200     }
1201
1202   switch (id)
1203     {
1204     case CMD_ID_COPY:
1205       {
1206         if (from >= to)
1207           return from;
1208         g = *(MGLYPH (from));
1209         if (ctx->combining_code)
1210           g.combining_code = ctx->combining_code;
1211         if (ctx->left_padding)
1212           g.left_padding = ctx->left_padding;
1213         APPEND_GLYPH (gstring, g);
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_PRINT1 (" <%d", 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   ctx.rfont = rface->rfont;
1346   table = MPLIST_VAL (layouter);
1347   layouter = MPLIST_NEXT (layouter);
1348   stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1349   gidx = from;
1350   /* Find previous glyphs that are also supported by the layouter.  */
1351   while (gidx > 1
1352          && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1353     gidx--;
1354   /* + 2 is for a separator ' ' and a terminator '\0'.  */
1355   encoded_len = gstring->used - gidx + 2;
1356   ctx.encoded = (char *) alloca (encoded_len);
1357
1358   for (i = 0; gidx < from; i++, gidx++)
1359     ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1360
1361   ctx.encoded[i++] = ' ';
1362   ctx.encoded_offset = from - i;
1363
1364   /* Now each MGlyph->code contains encoded char.  Set it in
1365      ctx.encoded[], and set MGlyph->c to MGlyph->code.  */
1366   for (gidx = from; gidx < to ; i++, gidx++)
1367     {
1368       ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1369       MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1370     }
1371   ctx.encoded[i++] = '\0';
1372
1373   match_indices[0] = from;
1374   match_indices[1] = to;
1375   for (i = 2; i < NMATCH; i++)
1376     match_indices[i] = -1;
1377   ctx.match_indices = match_indices;
1378
1379   from_pos = MGLYPH (from)->pos;
1380   to_pos = MGLYPH (to)->pos;
1381
1382   for (stage_idx = 0; 1; stage_idx++)
1383     {
1384       int len = to - from;
1385       int result;
1386
1387       MDEBUG_PRINT1 ("\n [FLT]   (STAGE %d", stage_idx);
1388       gidx = gstring->used;
1389       ctx.stage = stage;
1390
1391       result = run_command (2, INDEX_TO_CMD_ID (0), gstring,
1392                             ctx.encoded_offset, to, &ctx);
1393       MDEBUG_PRINT (")");
1394       if (result < 0)
1395         return -1;
1396       to = from + (gstring->used - gidx);
1397       REPLACE_GLYPHS (gstring, gidx, from, len);
1398
1399       layouter = MPLIST_NEXT (layouter);
1400       /* If this is the last stage, break the loop. */
1401       if (MPLIST_TAIL_P (layouter))
1402         break;
1403
1404       /* Otherwise, prepare for the next iteration.   */
1405       stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1406       table = stage->category;
1407       if (to - from >= encoded_len)
1408         {
1409           encoded_len = to + 1;
1410           ctx.encoded = (char *) alloca (encoded_len);
1411         }
1412
1413       for (i = from; i < to; i++)
1414         {
1415           MGlyph *g = MGLYPH (i);
1416
1417           if (g->type == GLYPH_PAD)
1418             ctx.encoded[i - from] = ' ';
1419           else if (! g->otf_encoded)
1420             ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1421 #ifdef HAVE_FREETYPE
1422           else
1423             {
1424               int c = mfont__ft_decode_otf (g);
1425
1426               if (c >= 0)
1427                 {
1428                   c = (int) mchartable_lookup (table, c);
1429                   if (! c)
1430                     c = -1;
1431                 }
1432               ctx.encoded[i - from] = (c >= 0 ? c : 1);
1433             }
1434 #endif  /* HAVE_FREETYPE */
1435         }
1436       ctx.encoded[i - from] = '\0';
1437       ctx.encoded_offset = from;
1438       ctx.match_indices[0] = from;
1439       ctx.match_indices[1] = to;
1440     }
1441
1442   MDEBUG_PRINT (")\n");
1443
1444   if (from == to)
1445     {
1446       /* Somehow there's no glyph contributing to characters between
1447          FROM_POS and TO_POS.  We must add one dummy space glyph for
1448          those characters.  */
1449       MGlyph g;
1450
1451       g.type = GLYPH_SPACE;
1452       g.c = ' ', g.code = ' ';
1453       g.pos = from_pos, g.to = to_pos;
1454       g.rface = ascii_rface;
1455       INSERT_GLYPH (gstring, from, g);
1456       to = from + 1;
1457     }
1458   else
1459     {
1460       /* Here we must check if all characters in the range is covered
1461          by some glyph(s).  If not, change <pos> and <to> of glyphs to
1462          cover uncovered characters.  */
1463       int len = to_pos - from_pos;
1464       int pos;
1465       MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1466       MGlyph *g, *gend = MGLYPH (to);
1467       MGlyph *latest = gend;
1468
1469       for (i = 0; i < len; i++)
1470         glyphs[i] = NULL;
1471       for (g = MGLYPH (from); g != gend; g++)
1472         {
1473           if (g->pos < latest->pos)
1474             latest = g;
1475           if (! glyphs[g->pos - from_pos])
1476             {
1477               for (i = g->pos; i < g->to; i++)
1478                 glyphs[i - from_pos] = g;
1479             }
1480         }
1481       i = 0;
1482       if (! glyphs[0])
1483         {
1484           pos = latest->pos;
1485           for (g = latest; g->pos == pos; g++)
1486             g->pos = from_pos;
1487           i++;
1488         }
1489       for (; i < len; i++)
1490         {
1491           if (! glyphs[i])
1492             {
1493               for (g = latest; g->pos == latest->pos; g++)
1494                 g->to = from_pos + i + 1;
1495             }
1496           else
1497             latest = glyphs[i];
1498         }
1499     }
1500   return to;
1501 }
1502
1503 \f
1504 /* for debugging... */
1505
1506 static void
1507 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1508 {
1509   char *prefix = (char *) alloca (indent + 1);
1510
1511   memset (prefix, 32, indent);
1512   prefix[indent] = 0;
1513
1514   if (id >= 0)
1515     fprintf (stderr, "0x%02X", id);
1516   else if (id <= CMD_ID_OFFSET_INDEX)
1517     {
1518       int idx = CMD_ID_TO_INDEX (id);
1519       FontLayoutCmd *cmd = stage->cmds + idx;
1520
1521       if (cmd->type == FontLayoutCmdTypeRule)
1522         {
1523           FontLayoutCmdRule *rule = &cmd->body.rule;
1524           int i;
1525
1526           fprintf (stderr, "(rule ");
1527           if (rule->src_type == SRC_REGEX)
1528             fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1529           else if (rule->src_type == SRC_INDEX)
1530             fprintf (stderr, "%d", rule->src.match_idx);
1531           else if (rule->src_type == SRC_SEQ)
1532             fprintf (stderr, "(seq)");
1533           else if (rule->src_type == SRC_RANGE)
1534             fprintf (stderr, "(range)");
1535           else
1536             fprintf (stderr, "(invalid src)");
1537
1538           for (i = 0; i < rule->n_cmds; i++)
1539             {
1540               fprintf (stderr, "\n%s  ", prefix);
1541               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1542             }
1543           fprintf (stderr, ")");
1544         }
1545       else if (cmd->type == FontLayoutCmdTypeCond)
1546         {
1547           FontLayoutCmdCond *cond = &cmd->body.cond;
1548           int i;
1549
1550           fprintf (stderr, "(cond");
1551           for (i = 0; i < cond->n_cmds; i++)
1552             {
1553               fprintf (stderr, "\n%s  ", prefix);
1554               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1555             }
1556           fprintf (stderr, ")");
1557         }
1558       else if (cmd->type == FontLayoutCmdTypeOTF)
1559         {
1560           fprintf (stderr, "(otf)");
1561         }
1562       else
1563         fprintf (stderr, "(error-command)");
1564     }
1565   else if (id <= CMD_ID_OFFSET_COMBINING)
1566     fprintf (stderr, "cominging-code");
1567   else
1568     fprintf (stderr, "(predefiend %d)", id);
1569 }
1570
1571 void
1572 dump_flt (MFontLayoutTable *flt, int indent)
1573 {
1574   char *prefix = (char *) alloca (indent + 1);
1575   MPlist *plist;
1576   int stage_idx = 0;
1577
1578   memset (prefix, 32, indent);
1579   prefix[indent] = 0;
1580   fprintf (stderr, "(flt");
1581   MPLIST_DO (plist, flt)
1582     {
1583       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1584       int i;
1585
1586       fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
1587       for (i = 0; i < stage->used; i++)
1588         {
1589           fprintf (stderr, "\n%s    ", prefix);
1590           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1591         }
1592       fprintf (stderr, ")");
1593       stage_idx++;
1594     }
1595   fprintf (stderr, ")");
1596 }