*** 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 #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       ctx->left_padding = 1;
1255       return from;
1256
1257     case CMD_ID_RIGHT_PADDING:
1258       if (gstring->used > 0)
1259         gstring->glyphs[gstring->used - 1].right_padding = 1;
1260       return from;
1261     }
1262
1263   MERROR (MERROR_DRAW, -1);
1264 }
1265
1266 \f
1267 /* Internal API */
1268
1269 int
1270 mfont__flt_init (void)
1271 {
1272   Mcond = msymbol ("cond");
1273   Mrange = msymbol ("range");
1274   Mlayouter = msymbol ("layouter");
1275   flt_list = mplist ();
1276   return 0;
1277 }
1278
1279 void
1280 mfont__flt_fini (void)
1281 {
1282   MPlist *plist, *pl;
1283
1284   MPLIST_DO (plist, flt_list)
1285     {
1286       pl = MPLIST_PLIST (plist);
1287       if (pl)
1288         {
1289           MPLIST_DO (pl, MPLIST_NEXT (pl))
1290             free_flt_stage (MPLIST_VAL (pl));
1291           pl = MPLIST_PLIST (plist);
1292           M17N_OBJECT_UNREF (pl);
1293         }
1294     }
1295   M17N_OBJECT_UNREF (flt_list);
1296 }
1297
1298 unsigned
1299 mfont__flt_encode_char (MSymbol layouter_name, int c)
1300 {
1301   MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1302   MCharTable *table;
1303   unsigned code;
1304
1305   if (! layouter)
1306     return MCHAR_INVALID_CODE;
1307   table = MPLIST_VAL (layouter);
1308   code = (unsigned) mchartable_lookup (table, c);
1309   return (code ? code : MCHAR_INVALID_CODE);
1310 }
1311
1312 int
1313 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1314 {
1315   int stage_idx = 0;
1316   int gidx;
1317   int i;
1318   FontLayoutContext ctx;
1319   MCharTable *table;
1320   int encoded_len;
1321   int match_indices[NMATCH];
1322   MSymbol layouter_name = rface->layouter;
1323   MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1324   MRealizedFace *ascii_rface = rface->ascii_rface;
1325   FontLayoutStage *stage;
1326   int from_pos, to_pos;
1327   MGlyph dummy;
1328
1329   if (! layouter)
1330     {
1331       /* FLT not found.  Make all glyphs invisible.  */
1332       while (from < to)
1333         gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1334       return to;
1335     }
1336
1337   dummy = gstring->glyphs[from];
1338   MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1339
1340   /* Setup CTX.  */
1341   memset (&ctx, 0, sizeof ctx);
1342   table = MPLIST_VAL (layouter);
1343   layouter = MPLIST_NEXT (layouter);
1344   stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1345   gidx = from;
1346   /* Find previous glyphs that are also supported by the layouter.  */
1347   while (gidx > 1
1348          && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1349     gidx--;
1350   /* + 2 is for a separator ' ' and a terminator '\0'.  */
1351   encoded_len = gstring->used - gidx + 2;
1352   ctx.encoded = (char *) alloca (encoded_len);
1353
1354   for (i = 0; gidx < from; i++, gidx++)
1355     ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1356
1357   ctx.encoded[i++] = ' ';
1358   ctx.encoded_offset = from - i;
1359
1360   /* Now each MGlyph->code contains encoded char.  Set it in
1361      ctx.encoded[], and set MGlyph->c to MGlyph->code.  */
1362   for (gidx = from; gidx < to ; i++, gidx++)
1363     {
1364       ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1365       MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1366     }
1367   ctx.encoded[i++] = '\0';
1368
1369   match_indices[0] = from;
1370   match_indices[1] = to;
1371   for (i = 2; i < NMATCH; i++)
1372     match_indices[i] = -1;
1373   ctx.match_indices = match_indices;
1374
1375   from_pos = MGLYPH (from)->pos;
1376   to_pos = MGLYPH (to)->pos;
1377
1378   for (stage_idx = 0; 1; stage_idx++)
1379     {
1380       int len = to - from;
1381       int result;
1382
1383       ctx.code_offset = ctx.combining_code = ctx.left_padding = 0;
1384       MDEBUG_PRINT2 ("\n [FLT]   (STAGE %d \"%s\"", stage_idx, ctx.encoded);
1385       if (mdebug__flag & mdebug_mask
1386           && ctx.encoded_offset < to)
1387         {
1388           if (gstring->glyphs[ctx.encoded_offset].type == GLYPH_PAD)
1389             fprintf (stderr, " (|");
1390           else
1391             fprintf (stderr, " (%X", gstring->glyphs[ctx.encoded_offset].code);
1392           for (i = ctx.encoded_offset + 1; i < to; i++)
1393             {
1394               if (gstring->glyphs[i].type == GLYPH_PAD)
1395                 fprintf (stderr, " |");
1396               else
1397                 fprintf (stderr, " %X", gstring->glyphs[i].code);
1398             }
1399           fprintf (stderr, ")");
1400         }
1401
1402       gidx = gstring->used;
1403       ctx.stage = stage;
1404
1405       result = run_command (4, INDEX_TO_CMD_ID (0), gstring,
1406                             ctx.encoded_offset, to, &ctx);
1407       MDEBUG_PRINT (")");
1408       if (result < 0)
1409         return -1;
1410       to = from + (gstring->used - gidx);
1411       REPLACE_GLYPHS (gstring, gidx, from, len);
1412
1413       layouter = MPLIST_NEXT (layouter);
1414       /* If this is the last stage, break the loop. */
1415       if (MPLIST_TAIL_P (layouter))
1416         break;
1417
1418       /* Otherwise, prepare for the next iteration.   */
1419       stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1420       table = stage->category;
1421       if (to - from >= encoded_len)
1422         {
1423           encoded_len = to + 1;
1424           ctx.encoded = (char *) alloca (encoded_len);
1425         }
1426
1427       for (i = from; i < to; i++)
1428         {
1429           MGlyph *g = MGLYPH (i);
1430
1431           if (g->type == GLYPH_PAD)
1432             ctx.encoded[i - from] = ' ';
1433           else if (! g->otf_encoded)
1434             ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1435 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1436           else
1437             {
1438               int c = mfont__ft_decode_otf (g);
1439
1440               if (c >= 0)
1441                 {
1442                   c = (int) mchartable_lookup (table, c);
1443                   if (! c)
1444                     c = -1;
1445                 }
1446               ctx.encoded[i - from] = (c >= 0 ? c : 1);
1447             }
1448 #endif  /* HAVE_FREETYPE && HAVE_OTF */
1449         }
1450       ctx.encoded[i - from] = '\0';
1451       ctx.encoded_offset = from;
1452       ctx.match_indices[0] = from;
1453       ctx.match_indices[1] = to;
1454     }
1455
1456   if (from == to)
1457     {
1458       /* Somehow there's no glyph contributing to characters between
1459          FROM_POS and TO_POS.  We must add one dummy space glyph for
1460          those characters.  */
1461       MGlyph g;
1462
1463       g.type = GLYPH_SPACE;
1464       g.c = ' ', g.code = ' ';
1465       g.pos = from_pos, g.to = to_pos;
1466       g.rface = ascii_rface;
1467       INSERT_GLYPH (gstring, from, g);
1468       to = from + 1;
1469     }
1470   else
1471     {
1472       /* Get actual glyph IDs of glyphs.  Also check if all characters
1473          in the range is covered by some glyph(s).  If not, change
1474          <pos> and <to> of glyphs to cover uncovered characters.  */
1475       int len = to_pos - from_pos;
1476       int pos;
1477       MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1478       MGlyph *g, *gend = MGLYPH (to);
1479       MGlyph *latest = gend;
1480
1481       for (g = MGLYPH (from); g != gend; g++)
1482         if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1483           g->code = ((rface->rfont->driver->encode_char)
1484                      (NULL, (MFont *) rface->rfont, NULL, g->code));
1485       for (i = 0; i < len; i++)
1486         glyphs[i] = NULL;
1487       for (g = MGLYPH (from); g != gend; g++)
1488         {
1489           if (g->pos < latest->pos)
1490             latest = g;
1491           if (! glyphs[g->pos - from_pos])
1492             {
1493               for (i = g->pos; i < g->to; i++)
1494                 glyphs[i - from_pos] = g;
1495             }
1496         }
1497       i = 0;
1498       if (! glyphs[0])
1499         {
1500           pos = latest->pos;
1501           for (g = latest; g->pos == pos; g++)
1502             g->pos = from_pos;
1503           i++;
1504         }
1505       for (; i < len; i++)
1506         {
1507           if (! glyphs[i])
1508             {
1509               for (g = latest; g->pos == latest->pos; g++)
1510                 g->to = from_pos + i + 1;
1511             }
1512           else
1513             latest = glyphs[i];
1514         }
1515     }
1516   MDEBUG_PRINT ("\n [FLT]   (RESULT (");
1517   if (mdebug__flag & mdebug_mask
1518       && ctx.encoded_offset < to)
1519     {
1520       if (gstring->glyphs[from].type == GLYPH_PAD)
1521         fprintf (stderr, "|");
1522       else
1523         fprintf (stderr, "%X", gstring->glyphs[from].code);
1524       for (from++; from < to; from++)
1525         {
1526           if (gstring->glyphs[from].type == GLYPH_PAD)
1527             fprintf (stderr, " |");
1528           else
1529             fprintf (stderr, " %X", gstring->glyphs[from].code);
1530         }
1531       fprintf (stderr, "))");
1532     }
1533   MDEBUG_PRINT (")\n");
1534
1535   return to;
1536 }
1537
1538 \f
1539 /* for debugging... */
1540
1541 static void
1542 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1543 {
1544   char *prefix = (char *) alloca (indent + 1);
1545
1546   memset (prefix, 32, indent);
1547   prefix[indent] = 0;
1548
1549   if (id >= 0)
1550     fprintf (stderr, "0x%02X", id);
1551   else if (id <= CMD_ID_OFFSET_INDEX)
1552     {
1553       int idx = CMD_ID_TO_INDEX (id);
1554       FontLayoutCmd *cmd = stage->cmds + idx;
1555
1556       if (cmd->type == FontLayoutCmdTypeRule)
1557         {
1558           FontLayoutCmdRule *rule = &cmd->body.rule;
1559           int i;
1560
1561           fprintf (stderr, "(rule ");
1562           if (rule->src_type == SRC_REGEX)
1563             fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1564           else if (rule->src_type == SRC_INDEX)
1565             fprintf (stderr, "%d", rule->src.match_idx);
1566           else if (rule->src_type == SRC_SEQ)
1567             fprintf (stderr, "(seq)");
1568           else if (rule->src_type == SRC_RANGE)
1569             fprintf (stderr, "(range)");
1570           else
1571             fprintf (stderr, "(invalid src)");
1572
1573           for (i = 0; i < rule->n_cmds; i++)
1574             {
1575               fprintf (stderr, "\n%s  ", prefix);
1576               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1577             }
1578           fprintf (stderr, ")");
1579         }
1580       else if (cmd->type == FontLayoutCmdTypeCond)
1581         {
1582           FontLayoutCmdCond *cond = &cmd->body.cond;
1583           int i;
1584
1585           fprintf (stderr, "(cond");
1586           for (i = 0; i < cond->n_cmds; i++)
1587             {
1588               fprintf (stderr, "\n%s  ", prefix);
1589               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1590             }
1591           fprintf (stderr, ")");
1592         }
1593       else if (cmd->type == FontLayoutCmdTypeOTF)
1594         {
1595           fprintf (stderr, "(otf)");
1596         }
1597       else
1598         fprintf (stderr, "(error-command)");
1599     }
1600   else if (id <= CMD_ID_OFFSET_COMBINING)
1601     fprintf (stderr, "cominging-code");
1602   else
1603     fprintf (stderr, "(predefiend %d)", id);
1604 }
1605
1606 void
1607 dump_flt (MFontLayoutTable *flt, int indent)
1608 {
1609   char *prefix = (char *) alloca (indent + 1);
1610   MPlist *plist;
1611   int stage_idx = 0;
1612
1613   memset (prefix, 32, indent);
1614   prefix[indent] = 0;
1615   fprintf (stderr, "(flt");
1616   MPLIST_DO (plist, flt)
1617     {
1618       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1619       int i;
1620
1621       fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
1622       for (i = 0; i < stage->used; i++)
1623         {
1624           fprintf (stderr, "\n%s    ", prefix);
1625           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1626         }
1627       fprintf (stderr, ")");
1628       stage_idx++;
1629     }
1630   fprintf (stderr, ")");
1631 }