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