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