(mplist_deserialize): Extern it.
[m17n/m17n-lib.git] / src / font-flt.c
1 /* font-flt.c -- Font Layout Table sub-module.
2    Copyright (C) 2003, 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 #include "config.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <regex.h>
31
32 #include "m17n-gui.h"
33 #include "m17n-misc.h"
34 #include "internal.h"
35 #include "mtext.h"
36 #include "symbol.h"
37 #include "plist.h"
38 #include "internal-gui.h"
39 #include "font.h"
40 #include "face.h"
41
42 /* Font Layouter */
43
44 /* Font Layout Table (FLT)
45
46 Predefined terms: SYMBOL, INTEGER, STRING
47
48 FLT ::= '(' STAGE + ')'
49
50 STAGE ::= CATEGORY-TABLE ? FONT-LAYOUT-RULE
51
52 ;; Each STAGE consumes a source (code sequence) and produces another
53 ;; code sequence that is given to the next STAGE as a source.  The
54 ;; source given to the first stage is a sequence of character codes
55 ;; that are assigned category codes by CATEGORY-TABLE.  The output of
56 ;; the last stage is a glyph code sequence given to the renderer.
57
58 CATEGORY-TABLE ::=
59         '(' 'category' CATEGORY-SPEC + ')'
60 CATEGORY-SPEC ::=
61         '(' CODE [ CODE ] CATEGORY ')'
62 CODE ::= INTEGER
63 CATEGORY ::= INTEGER
64 ;; ASCII character codes of alphabet ('A' .. 'Z' 'a' .. 'z').
65 ;; Ex: CATEGORY-TABLE
66 ;; (category
67 ;;   (0x0900 0x097F     ?E)     ; All Devanagari characters
68 ;;   (0x093C            ?N))    ; DEVANAGARI-LETTER NUKTA
69 ;;      Assign the category 'E' to all Devanagari characters but 0x093C,
70 ;;      assign the category 'N' to 0x093C.
71
72 FONT-LAYOUT-RULE ::=
73         '(' 'generator' RULE MACRO-DEF * ')'
74
75 RULE ::= COMMAND | REGEXP-RULE | MATCH-RULE | MAP-RULE
76          | COND-STRUCT | MACRO-NAME
77
78 COMMAND ::=
79         DIRECT-CODE | COMBINING | PREDEFIND-COMMAND | OTF-COMMAND
80
81 DIRECT-CODE ::= INTEGER
82 ;; Always succeed.  Produce the code.  Consume no source.
83
84 PREDEFIND-COMMAND ::=
85         '=' | '*' | '<' | '>' | '|'
86
87 ;; '=': Succeed when the current run contains at least one code.
88 ;; Consume the first code in the current run, and produce it as is.
89
90 ;; '*': If the the previous command succeeded, repeat it until it
91 ;; fails.  
92
93 ;; '<': Produce a special code that indicates the start of grapheme
94 ;; cluster.  Succeed always, consume nothing.
95
96 ;; '>': Produce a special code that indicates the end of grapheme
97 ;; cluster.  Succeed always, consume nothing.
98
99 ;; '|': Produce a special code whose category is ' '.  Succeed always,
100 ;; consume nothing.
101
102 OTF-COMMAND ::=
103         'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]]
104 ;; Run the Open Type Layout Table on the current run.  Succeed always,
105 ;; consume nothing.
106
107 SCRIPT ::= OTF-TAG
108 ;;      OTF's ScriptTag name (four letters) listed at:
109 ;;      <http://www.microsoft.om/typograph/otspec/scripttags.htm>
110 LANGSYS ::= OTF-TAG
111 ;;      OTF's Language System name (four letters) listed at:
112 ;;      <http://www.microsoft.om/typograph/otspec/languagetags.htm>
113
114 GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
115 GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
116 FEATURE ::= OTF-TAG
117 ;;      OTF's Feature name (four letters) listed at:
118 ;;      <http://www.microsoft.om/typograph/otspec/???.htm>
119
120 OTF-TAG ::= PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR
121
122 ;; Ex. OTF-COMMAND
123 ;; 'otf:deva'
124 ;;      Run all features in the default langsys of 'deva' script.
125 ;; 'otf:deva::nukt:haln'
126 ;;      Run all GSUB features, run 'nukt' and 'haln' GPOS features.
127 ;; 'otf:deva:: :'
128 ;;      Run all GSUB features, run no GPOS features.
129
130 REGEXP-RULE ::=
131         '(' REGEXP RULE * ')'
132
133 ;; Succeed if REGXP matches the head of source.  Run RULEs while
134 ;; limiting the source to the matching part.  Consume that part.
135
136 REGEXP ::= STRING
137 ;; Must be composed only from ASCII characters.  'A' - 'Z', 'a' - 'z'
138 ;; correspond to CATEGORY.
139
140 ;; Ex: REGEXP-RULE
141 ;; ("VA?"
142 ;;   < | vowel * | >)
143
144 MATCH-RULE ::=
145         '(' MATCH-IDX RULE * ')'
146
147 ;; Succeed if the previous REGEXP-RULE found a matching part for
148 ;; MATCH-IDX.  Run RULEs while limiting the source to the matching
149 ;; part.  If MATCH-IDX is zero, consume the whole part, else consume
150 ;; nothing.
151
152 MATCH-IDX ::= INTEGER
153 ;; Must be 0..20.
154
155 ;; Ex. MATCH-RULE
156 ;; (2 consonant *)
157
158 MAP-RULE ::=
159         '(' ( SOURCE-SEQ | SOURCE-RANGE ) RULE * ')'
160
161 ;; Succeed if the source matches SOURCE-SEQ or SOURCE-RANGE.  Run
162 ;; RULEs while limiting the source to the matching part.  Consume that
163 ;; part.
164
165 SOURCE-SEQ ::=
166         '(' CODE + ')'
167 SOURCE-RANGE ::=
168         '(' 'range' CODE CODE ')'
169 ;; Ex. MAP-RULE
170 ;; ((0x0915 0x094D)             0x43)
171 ;;      If the source code sequence is 0x0915 0x094D, produce 0x43.
172 ;; ((range 0x0F40 0x0F6A)       0x2221)
173 ;;      If the first source code CODE is in the range 0x0F40..0x0F6A, 
174 ;;      produce (0x2221 + (CODE - 0x0F40)).
175
176 COND-STRUCT ::=
177         '(' 'cond' RULE + ')'
178
179 ;; Try each rule in sequence until one succeeds.  Succeed if one
180 ;; succeeds.  Consume nothing.
181
182 ;; Ex. COND-STRUCT
183 ;; (cond
184 ;;  ((0x0915 0x094D)            0x43)
185 ;;  ((range 0x0F40 0x0F6A)      0x2221)
186 ;;  = )
187
188 COMBINING ::= 'V''H''O''V''H'
189 V ::= ( 't' | 'c' | 'b' | 'B' )
190 H ::= ( 'l' | 'c' | 'r' )
191 O ::= ( '.' | XOFF | YOFF | XOFF YOFF )
192 XOFF ::= '<'INTEGER | '>'INTEGER 
193 YOFF ::= '+'INTEGER | '-'INTEGER
194 ;; INTEGER must be integer 0..127
195
196 ;; VH pair indicates 12 reference points of a glyph as below:
197 ;;
198 ;;   0----1----2 <---- ascent    0:tl (top-left)
199 ;;   |         |                 1:tc (top-center)
200 ;;   |         |                 2:tr (top-right)
201 ;;   |         |                 3:Bl (base-left)
202 ;;   9   10   11 <---- center    4:Bc (base-center)
203 ;;   |         |                 5:Br (base-right)
204 ;; --3----4----5-- <-- baseline  6:bl (bottom-left)
205 ;;   |         |                 7:bc (bottom-center)
206 ;;   6----7----8 <---- descent   8:br (bottom-right)
207 ;;                               9:cl (center-left)
208 ;;   |    |    |                10:cc (center-center)
209 ;; left center right            11:cr (center-right)
210 ;;
211 ;; Ex. COMBINING
212 ;; 'tc.bc':
213 ;;      Align top-left point of the previous glyph and bottom-center
214 ;;      point of the current glyph.
215 ;; 'Bl<20-10Br'
216 ;;      Align 20% left and 10% below of base-left point of the previous
217 ;;      glyph and base-right point of the current glyph.
218
219 MACRO-DEF ::=
220         '(' MACRO-NAME RULE + ')'
221 MACRO-NAME ::= SYMBOL
222
223 */
224
225 static int mdebug_mask = MDEBUG_FONT_FLT;
226
227 MSymbol Mlayouter;
228
229 static MPlist *flt_list;
230
231 /* Command ID:
232                  0 ...          : direct code
233                    -1           : invalid
234              -0x0F .. -2        : builtin commands
235         -0x100000F .. -0x10     : combining code
236                   ... -0x1000010: index to FontLayoutStage->cmds
237  */
238
239 #define INVALID_CMD_ID -1
240 #define CMD_ID_OFFSET_BUILTIN   -2
241 #define CMD_ID_OFFSET_COMBINING -0x10
242 #define CMD_ID_OFFSET_INDEX     -0x1000010
243
244 /* Builtin commands. */
245 #define CMD_ID_COPY             -2 /* '=' */
246 #define CMD_ID_REPEAT           -3 /* '*' */
247 #define CMD_ID_CLUSTER_BEGIN    -4 /* '<' */
248 #define CMD_ID_CLUSTER_END      -5 /* '>' */
249 #define CMD_ID_SEPARATOR        -6 /* '|' */
250 #define CMD_ID_LEFT_PADDING     -7 /* '[' */
251 #define CMD_ID_RIGHT_PADDING    -8 /* ']' */
252
253 #define CMD_ID_TO_COMBINING_CODE(id) (CMD_ID_OFFSET_COMBINING - (id))
254 #define COMBINING_CODE_TO_CMD_ID(code) (CMD_ID_OFFSET_COMBINING - (code))
255
256 #define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id))
257 #define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx))
258
259 static MSymbol Mcond, Mrange;
260
261 #define GLYPH_CODE_P(code)      \
262   ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX)
263
264 #define GLYPH_CODE_INDEX(code) ((code) - GLYPH_CODE_MIN)
265
266 enum FontLayoutCmdRuleSrcType
267   {
268     SRC_REGEX,
269     SRC_INDEX,
270     SRC_SEQ,
271     SRC_RANGE
272   };
273
274 typedef struct
275 {
276   enum FontLayoutCmdRuleSrcType src_type;
277   union {
278     struct {
279       char *pattern;
280       regex_t preg;
281     } re;
282     int match_idx;
283     struct {
284       int n_codes;
285       int *codes;
286     } seq;
287     struct {
288       int from, to;
289     } range;
290   } src;
291
292   int n_cmds;
293   int *cmd_ids;
294 } FontLayoutCmdRule;
295
296 typedef struct
297 {
298   int n_cmds;
299   int *cmd_ids;
300 } FontLayoutCmdCond;
301
302 typedef struct
303 {
304   MSymbol script;
305   MSymbol langsys;
306   MSymbol gsub_features;
307   MSymbol gpos_features;
308 } FontLayoutCmdOTF;
309
310 enum FontLayoutCmdType
311   {
312     FontLayoutCmdTypeRule,
313     FontLayoutCmdTypeCond,
314     FontLayoutCmdTypeOTF,
315     FontLayoutCmdTypeMAX
316   };
317
318 typedef struct
319 {
320   enum FontLayoutCmdType type;
321   union {
322     FontLayoutCmdRule rule;
323     FontLayoutCmdCond cond;
324     FontLayoutCmdOTF otf;
325   } body;
326 } FontLayoutCmd;
327
328 typedef struct 
329 {
330   MCharTable *category;
331   int size, inc, used;
332   FontLayoutCmd *cmds;
333 } FontLayoutStage;
334
335 typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */
336
337 /* Font layout table loader */
338
339 /* Load a category table from PLIST.  PLIST has this form:
340       PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
341 */
342
343 static MCharTable *
344 load_category_table (MPlist *plist)
345 {
346   MCharTable *table;
347
348   table = mchartable (Minteger, (void *) 0);
349
350   MPLIST_DO (plist, plist)
351     {
352       MPlist *elt;
353       int from, to, category_code;
354
355       if (! MPLIST_PLIST (plist))
356         MERROR (MERROR_FONT, NULL);
357       elt = MPLIST_PLIST (plist);
358       if (! MPLIST_INTEGER_P (elt))
359         MERROR (MERROR_FONT, NULL);
360       from = MPLIST_INTEGER (elt);
361       elt = MPLIST_NEXT (elt);
362       if (! MPLIST_INTEGER_P (elt))
363         MERROR (MERROR_FONT, NULL);
364       to = MPLIST_INTEGER (elt);
365       elt = MPLIST_NEXT (elt);
366       if (MPLIST_TAIL_P (elt))
367         {
368           category_code = to;
369           to = from;
370         }
371       else
372         {
373           if (! MPLIST_INTEGER_P (elt))
374             MERROR (MERROR_FONT, NULL);
375           category_code = MPLIST_INTEGER (elt);
376         }
377       if (! isalpha (category_code))
378         MERROR (MERROR_FONT, NULL);
379
380       if (from == to)
381         mchartable_set (table, from, (void *) category_code);
382       else
383         mchartable_set_range (table, from, to, (void *) category_code);
384     }
385
386   return table;
387 }
388
389
390 /* Parse OTF command name NAME and store the result in CMD.
391    NAME has this form:
392         :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
393    where GSUB-FEATURES and GPOS-FEATURES have this form:
394         [FEATURE[,FEATURE]*] | ' '  */
395
396 static int
397 load_otf_command (FontLayoutCmd *cmd, char *name)
398 {
399   char *p = name, *beg;
400
401   cmd->type = FontLayoutCmdTypeOTF;
402   cmd->body.otf.script = cmd->body.otf.langsys = Mnil;
403   cmd->body.otf.gsub_features = cmd->body.otf.gpos_features = Mt;
404
405   while (*p)
406     {
407       if (*p == ':')
408         {
409           for (beg = ++p; *p && *p != '/' && *p != '=' && *p != '+'; p++);
410           if (beg < p)
411             cmd->body.otf.script = msymbol__with_len (beg, p - beg);
412         }
413       else if (*p == '/')
414         {
415           for (beg = ++p; *p && *p != '=' && *p != '+'; p++);
416           if (beg < p)
417             cmd->body.otf.langsys = msymbol__with_len (beg, p - beg);
418         }
419       else if (*p == '=')
420         {
421           for (beg = ++p; *p && *p != '+'; p++);
422           if (beg < p)
423             cmd->body.otf.gsub_features = msymbol__with_len (beg, p - beg);
424           else
425             cmd->body.otf.gsub_features = Mnil;
426         }
427       else if (*p == '+')
428         {
429           for (beg = ++p; *p && *p != '+'; p++);
430           if (beg < p)
431             cmd->body.otf.gpos_features = msymbol__with_len (beg, p - beg);
432           else
433             cmd->body.otf.gpos_features = Mnil;
434         }
435       else
436         p++;
437     }
438
439   return (cmd->body.otf.script == Mnil ? -1 : 0);
440 }
441
442
443 /* Read a decimal number from STR preceded by one of "+-><".  '+' and
444    '>' means a plus sign, '-' and '<' means a minus sign.  If the
445    number is greater than 127, limit it to 127.  */
446
447 static int
448 read_decimal_number (char **str)
449 {
450   char *p = *str;
451   int sign = (*p == '-' || *p == '<') ? -1 : 1;
452   int n = 0;
453
454   p++;
455   while (*p >= '0' && *p <= '9')
456     n = n * 10 + *p++ - '0';
457   *str = p;
458   if (n == 0)
459     n = 5;
460   return (n < 127 ? n * sign : 127 * sign);
461 }
462
463
464 /* Read a horizontal and vertical combining positions from STR, and
465    store them in the place pointed by X and Y.  The horizontal
466    position left, center, and right are represented by 0, 1, and 2
467    respectively.  The vertical position top, center, bottom, and base
468    are represented by 0, 1, 2, and 3 respectively.  If successfully
469    read, return 0, else return -1.  */
470
471 static int
472 read_combining_position (char *str, int *x, int *y)
473 {
474   int c = *str++;
475   int i;
476
477   /* Vertical position comes first.  */
478   for (i = 0; i < 4; i++)
479     if (c == "tcbB"[i])
480       {
481         *y = i;
482         break;
483       }
484   if (i == 4)
485     return -1;
486   c = *str;
487   /* Then comse horizontal position.  */
488   for (i = 0; i < 3; i++)
489     if (c == "lcr"[i])
490       {
491         *x = i;
492         return 0;
493       }
494   return -1;
495 }
496
497
498 /* Return a combining code corresponding to SYM.  */
499
500 static int
501 get_combining_command (MSymbol sym)
502 {
503   char *str = msymbol_name (sym);
504   int base_x, base_y, add_x, add_y, off_x, off_y;
505   int c;
506
507   if (read_combining_position (str, &base_x, &base_y) < 0)
508     return 0;
509   str += 2;
510   c = *str;
511   if (c == '.')
512     {
513       off_x = off_y = 128;
514       str++;
515     }
516   else
517     {
518       if (c == '+' || c == '-')
519         {
520           off_y = read_decimal_number (&str) + 128;
521           c = *str;
522         }
523       else
524         off_y = 128;
525       if (c == '<' || c == '>')
526         off_x = read_decimal_number (&str) + 128;
527       else
528         off_x = 128;
529     }
530   if (read_combining_position (str, &add_x, &add_y) < 0)
531     return 0;
532
533   c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
534   return (COMBINING_CODE_TO_CMD_ID (c));
535 }
536
537
538 /* Load a command from PLIST into STAGE, and return that
539    identification number.  If ID is not INVALID_CMD_ID, that means we
540    are loading a top level command or a macro.  In that case, use ID
541    as the identification number of the command.  Otherwise, generate a
542    new id number for the command.  MACROS is a list of raw macros.  */
543
544 static int
545 load_command (FontLayoutStage *stage, MPlist *plist,
546               MPlist *macros, int id)
547 {
548   int i;
549
550   if (MPLIST_INTEGER_P (plist))
551     {
552       int code = MPLIST_INTEGER (plist);
553
554       if (code < 0)
555         MERROR (MERROR_DRAW, INVALID_CMD_ID);
556       return code;
557     }
558   else if (MPLIST_PLIST_P (plist))
559     {
560       /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
561                    | ( ( INTEGER INTEGER ) ... )
562                    | ( ( range INTEGER INTEGER ) ... )  */
563       MPlist *elt = MPLIST_PLIST (plist);
564       int len = MPLIST_LENGTH (elt) - 1;
565       FontLayoutCmd *cmd;
566
567       if (id == INVALID_CMD_ID)
568         {
569           FontLayoutCmd dummy;
570           id = INDEX_TO_CMD_ID (stage->used);
571           MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
572         }
573       cmd = stage->cmds + CMD_ID_TO_INDEX (id);
574
575       if (MPLIST_SYMBOL_P (elt))
576         {
577           if (MPLIST_SYMBOL (elt) != Mcond)
578             MERROR (MERROR_DRAW, INVALID_CMD_ID);
579           elt = MPLIST_NEXT (elt);
580           cmd->type = FontLayoutCmdTypeCond;
581           cmd->body.cond.n_cmds = len;
582           MTABLE_CALLOC (cmd->body.cond.cmd_ids, len, MERROR_DRAW);
583           for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
584             {
585               int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
586
587               if (this_id == INVALID_CMD_ID)
588                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
589               /* The above load_command may relocate stage->cmds.  */
590               cmd = stage->cmds + CMD_ID_TO_INDEX (id);
591               cmd->body.cond.cmd_ids[i] = this_id;
592             }
593         }
594       else
595         {
596           cmd->type = FontLayoutCmdTypeRule;
597           if (MPLIST_MTEXT_P (elt))
598             {
599               char *str = (char *) MTEXT_DATA (MPLIST_MTEXT (elt));
600
601               if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
602                 MERROR (MERROR_FONT, INVALID_CMD_ID);
603               cmd->body.rule.src_type = SRC_REGEX;
604               cmd->body.rule.src.re.pattern = strdup (str);
605             }
606           else if (MPLIST_INTEGER_P (elt))
607             {
608               cmd->body.rule.src_type = SRC_INDEX;
609               cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
610             }
611           else if (MPLIST_PLIST_P (elt))
612             {
613               MPlist *pl = MPLIST_PLIST (elt);
614               int size = MPLIST_LENGTH (pl);
615
616               if (MPLIST_INTEGER_P (pl))
617                 {
618                   int i;
619
620                   cmd->body.rule.src_type = SRC_SEQ;
621                   cmd->body.rule.src.seq.n_codes = size;
622                   MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
623                                  MERROR_FONT);
624                   for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
625                     {
626                       if (! MPLIST_INTEGER_P (pl))
627                         MERROR (MERROR_DRAW, INVALID_CMD_ID);
628                       cmd->body.rule.src.seq.codes[i]
629                         = (unsigned) MPLIST_INTEGER (pl);
630                     }
631                 }
632               else if (MPLIST_SYMBOL_P (pl) && size == 3)
633                 {
634                   cmd->body.rule.src_type = SRC_RANGE;
635                   pl = MPLIST_NEXT (pl);
636                   if (! MPLIST_INTEGER_P (pl))
637                     MERROR (MERROR_DRAW, INVALID_CMD_ID);
638                   cmd->body.rule.src.range.from
639                     = (unsigned) MPLIST_INTEGER (pl);
640                   pl = MPLIST_NEXT (pl);
641                   if (! MPLIST_INTEGER_P (pl))
642                     MERROR (MERROR_DRAW, INVALID_CMD_ID);
643                   cmd->body.rule.src.range.to
644                     = (unsigned) MPLIST_INTEGER (pl);
645                 }
646               else
647                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
648             }
649           else
650             MERROR (MERROR_DRAW, INVALID_CMD_ID);
651
652           elt = MPLIST_NEXT (elt);
653           cmd->body.rule.n_cmds = len;
654           MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
655           for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
656             {
657               int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
658
659               if (this_id == INVALID_CMD_ID)
660                 MERROR (MERROR_DRAW, INVALID_CMD_ID);
661               /* The above load_command may relocate stage->cmds.  */
662               cmd = stage->cmds + CMD_ID_TO_INDEX (id);
663               cmd->body.rule.cmd_ids[i] = this_id;
664             }
665         }
666     }
667   else if (MPLIST_SYMBOL_P (plist))
668     {
669       MPlist *elt;
670       MSymbol sym = MPLIST_SYMBOL (plist);
671       char *name = msymbol_name (sym);
672       int len = strlen (name);
673       FontLayoutCmd cmd;
674
675       if (len > 4
676           && ! strncmp (name, "otf:", 4)
677           && load_otf_command (&cmd, name + 3) >= 0)
678         {
679           if (id == INVALID_CMD_ID)
680             {
681               id = INDEX_TO_CMD_ID (stage->used);
682               MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
683             }
684           else
685             stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
686           return id;
687         }
688
689       if (len == 1)
690         {
691           if (*name == '=')
692             return CMD_ID_COPY;
693           else if (*name == '*')
694             return CMD_ID_REPEAT;
695           else if (*name == '<')
696             return CMD_ID_CLUSTER_BEGIN;
697           else if (*name == '>')
698             return CMD_ID_CLUSTER_END;
699           else if (*name == '|')
700             return CMD_ID_SEPARATOR;
701           else if (*name == '[')
702             return CMD_ID_LEFT_PADDING;
703           else if (*name == ']')
704             return CMD_ID_RIGHT_PADDING;
705           else
706             id = 0;
707         }
708       else
709         {
710           id = get_combining_command (sym);
711           if (id)
712             return id;
713         }
714
715       i = 1;
716       MPLIST_DO (elt, macros)
717         {
718           if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
719             {
720               id = INDEX_TO_CMD_ID (i);
721               if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
722                 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
723                                    macros, id);
724               return id;
725             }
726           i++;
727         }
728       MERROR (MERROR_DRAW, INVALID_CMD_ID);
729     }
730   else
731     MERROR (MERROR_DRAW, INVALID_CMD_ID);
732
733   return id;
734 }
735
736 static void
737 free_flt_command (FontLayoutCmd *cmd)
738 {
739   if (cmd->type == FontLayoutCmdTypeRule)
740     {
741       FontLayoutCmdRule *rule = &cmd->body.rule;
742
743       if (rule->src_type == SRC_REGEX)
744         {
745           free (rule->src.re.pattern);
746           regfree (&rule->src.re.preg);
747         }
748       else if (rule->src_type == SRC_SEQ)
749         free (rule->src.seq.codes);
750       free (rule->cmd_ids);
751     }
752   else if (cmd->type == FontLayoutCmdTypeCond)
753     free (cmd->body.cond.cmd_ids);
754 }
755
756 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
757    and return it.  PLIST has this form:
758       PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
759 */
760
761 static FontLayoutStage *
762 load_generator (MPlist *plist)
763 {
764   FontLayoutStage *stage;
765   MPlist *elt, *pl;
766   FontLayoutCmd dummy;
767
768   MSTRUCT_CALLOC (stage, MERROR_DRAW);
769   MLIST_INIT1 (stage, cmds, 32);
770   dummy.type = FontLayoutCmdTypeMAX;
771   MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
772   MPLIST_DO (elt, MPLIST_NEXT (plist))
773     {
774       if (! MPLIST_PLIST_P (elt))
775         MERROR (MERROR_FONT, NULL);
776       pl = MPLIST_PLIST (elt);
777       if (! MPLIST_SYMBOL_P (pl))
778         MERROR (MERROR_FONT, NULL);
779       MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
780     }
781
782   /* Load the first command from PLIST into STAGE->cmds[0].  Macros
783      called in the first command are also loaded from MPLIST_NEXT
784      (PLIST) into STAGE->cmds[n].  */
785   if (load_command (stage, plist, MPLIST_NEXT (plist), INDEX_TO_CMD_ID (0))
786       == INVALID_CMD_ID)
787     {
788       MLIST_FREE1 (stage, cmds);
789       free (stage);
790       MERROR (MERROR_DRAW, NULL);
791     }
792
793   return stage;
794 }
795
796
797 /* Load FLT of name LAYOUTER_NAME from the m17n database into a newly
798    allocated memory, and return it.  */
799
800 static MFontLayoutTable *
801 load_flt (MSymbol layouter_name)
802 {
803   MDatabase *mdb;
804   MPlist *top = NULL, *plist;
805   MSymbol Mcategory = msymbol ("category");
806   MSymbol Mgenerator = msymbol ("generator");
807   MFontLayoutTable *layouter = NULL;
808   MCharTable *category = NULL;
809
810   if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
811     MERROR_GOTO (MERROR_FONT, finish);
812   if (! (top = (MPlist *) mdatabase_load (mdb)))
813     MERROR_GOTO (0, finish);
814   if (! MPLIST_PLIST_P (top))
815     MERROR_GOTO (MERROR_FONT, finish);
816
817   MPLIST_DO (plist, top)
818     {
819       MSymbol sym;
820       MPlist *elt;
821
822       if (! MPLIST_PLIST (plist))
823         MERROR_GOTO (MERROR_FONT, finish);
824       elt = MPLIST_PLIST (plist);
825       if (! MPLIST_SYMBOL_P (elt))
826         MERROR_GOTO (MERROR_FONT, finish);
827       sym = MPLIST_SYMBOL (elt);
828       elt = MPLIST_NEXT (elt);
829       if (! elt)
830         MERROR_GOTO (MERROR_FONT, finish);
831       if (sym == Mcategory)
832         {
833           if (category)
834             M17N_OBJECT_UNREF (category);
835           category = load_category_table (elt);
836         }
837       else if (sym == Mgenerator)
838         {
839           FontLayoutStage *stage;
840
841           if (! category)
842             MERROR_GOTO (MERROR_FONT, finish);
843           stage = load_generator (elt);
844           if (! stage)
845             MERROR_GOTO (MERROR_FONT, finish);
846           stage->category = category;
847           M17N_OBJECT_REF (category);
848           if (! layouter)
849             {
850               layouter = mplist ();
851               /* Here don't do M17N_OBJECT_REF (category) because we
852                  don't unref the value of the element added below.  */
853               mplist_add (layouter, Mcategory, category);
854             }
855           mplist_add (layouter, Mt, stage);
856         }
857       else
858         MERROR_GOTO (MERROR_FONT, finish);
859     }
860
861   if (category)
862     M17N_OBJECT_UNREF (category);
863
864  finish:
865   M17N_OBJECT_UNREF (top);
866   mplist_add (flt_list, layouter_name, layouter);
867   return layouter;
868 }
869
870
871 static void
872 free_flt_stage (FontLayoutStage *stage)
873 {
874   int i;
875
876   M17N_OBJECT_UNREF (stage->category);
877   for (i = 0; i < stage->used; i++)
878     free_flt_command (stage->cmds + i);
879   MLIST_FREE1 (stage, cmds);
880   free (stage);
881 }
882
883
884 static MFontLayoutTable *
885 get_font_layout_table (MSymbol layouter_name)
886 {
887   MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
888
889   return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
890 }
891
892
893 /* FLS (Font Layout Service) */
894
895 /* Structure to hold information about a context of FLS.  */
896
897 typedef struct
898 {
899   /* Pointer to the current stage.  */
900   FontLayoutStage *stage;
901
902   /* Encode each MGlyph->code by the current category table into this
903      array.  An element is a category.  */
904   char *encoded;
905   /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
906      index GIDX.  */
907   int encoded_offset;
908   int *match_indices;
909   int code_offset;
910   int cluster_begin_idx;
911   int cluster_begin_pos;
912   int cluster_end_pos;
913   int combining_code;
914   int left_padding;
915   MRealizedFont *rfont;
916 } FontLayoutContext;
917
918 static int run_command (int depth,
919                         int, MGlyphString *, int, int, FontLayoutContext *);
920
921 #define NMATCH 20
922
923 static int
924 run_rule (int depth,
925           FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
926           FontLayoutContext *ctx)
927 {
928   int *saved_match_indices = ctx->match_indices;
929   int match_indices[NMATCH * 2];
930   int consumed;
931   int i;
932   int orig_from = from;
933
934   if (ctx->cluster_begin_idx)
935     {
936       if (ctx->cluster_begin_pos > MGLYPH (from)->pos)
937         ctx->cluster_begin_pos = MGLYPH (from)->pos;
938       if (ctx->cluster_end_pos < MGLYPH (to)->pos)
939         ctx->cluster_end_pos = MGLYPH (to)->pos;
940     }
941
942   if (rule->src_type == SRC_SEQ)
943     {
944       int len;
945
946       len = rule->src.seq.n_codes;
947       if (len > (to - from))
948         return 0;
949       for (i = 0; i < len; i++)
950         if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
951           break;
952       if (i < len)
953         return 0;
954       to = from + len;
955       MDEBUG_PRINT1 (" (SEQ 0x%X", rule->src.seq.codes[0]);
956     }
957   else if (rule->src_type == SRC_RANGE)
958     {
959       int head;
960
961       if (from >= to)
962         return 0;
963       head = gstring->glyphs[from].code;
964       if (head < rule->src.range.from || head > rule->src.range.to)
965         return 0;
966       ctx->code_offset = head - rule->src.range.from;
967       to = from + 1;
968       MDEBUG_PRINT2 (" (RANGE 0x%X-0x%X",
969                      rule->src.range.from, rule->src.range.to);
970     }
971   else if (rule->src_type == SRC_REGEX)
972     {
973       regmatch_t pmatch[NMATCH];
974       char saved_code;
975       int result;
976
977       if (from > to)
978         return 0;
979       saved_code = ctx->encoded[to - ctx->encoded_offset];
980       ctx->encoded[to - ctx->encoded_offset] = '\0';
981       result = regexec (&(rule->src.re.preg),
982                         ctx->encoded + from - ctx->encoded_offset,
983                         NMATCH, pmatch, 0);
984       if (result == 0 && pmatch[0].rm_so == 0)
985         {
986           MDEBUG_PRINT3 (" (REGEX \"%s\" \"%s\" %d",
987                          rule->src.re.pattern,
988                          ctx->encoded + from - ctx->encoded_offset,
989                          pmatch[0].rm_eo);
990           ctx->encoded[to - ctx->encoded_offset] = saved_code;
991           for (i = 0; i < NMATCH; i++)
992             {
993               if (pmatch[i].rm_so < 0)
994                 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
995               else
996                 {
997                   match_indices[i * 2] = from + pmatch[i].rm_so;
998                   match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
999                 }
1000             }
1001           ctx->match_indices = match_indices;
1002           to = match_indices[1];
1003         }
1004       else
1005         {
1006           ctx->encoded[to - ctx->encoded_offset] = saved_code;
1007           return 0;
1008         }
1009     }
1010   else if (rule->src_type == SRC_INDEX)
1011     {
1012       if (rule->src.match_idx >= NMATCH)
1013         return 0;
1014       from = ctx->match_indices[rule->src.match_idx * 2];
1015       if (from < 0)
1016         return 0;
1017       to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1018       MDEBUG_PRINT1 (" (INDEX %d", rule->src.match_idx);
1019     }
1020
1021   consumed = 0;
1022   depth++;
1023   for (i = 0; i < rule->n_cmds; i++)
1024     {
1025       int pos;
1026
1027       if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1028         {
1029           if (! consumed)
1030             continue;
1031           i--;
1032         }
1033       pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1034       if (pos < 0)
1035         MERROR (MERROR_DRAW, -1);
1036       consumed = pos > from;
1037       if (consumed)
1038         from = pos;
1039     }
1040
1041   ctx->match_indices = saved_match_indices;
1042   MDEBUG_PRINT (")");
1043   return (rule->src_type == SRC_INDEX ? orig_from : to);
1044 }
1045
1046 static int
1047 run_cond (int depth,
1048           FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1049           FontLayoutContext *ctx)
1050 {
1051   int i, pos = 0;
1052
1053   MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1054   depth++;
1055   for (i = 0; i < cond->n_cmds; i++)
1056     if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1057         != 0)
1058       break;
1059   if (pos < 0)
1060     MERROR (MERROR_DRAW, -1);
1061   MDEBUG_PRINT (")");
1062   return (pos);
1063 }
1064
1065 static int
1066 run_otf (int depth,
1067          FontLayoutCmdOTF *otf_cmd, MGlyphString *gstring, int from, int to,
1068          FontLayoutContext *ctx)
1069 {
1070 #ifdef HAVE_OTF
1071   int gidx = gstring->used;
1072
1073   to = mfont__ft_drive_otf (gstring, from, to, ctx->rfont,
1074                             otf_cmd->script, otf_cmd->langsys,
1075                             otf_cmd->gsub_features, otf_cmd->gpos_features);
1076   if (gidx < gstring->used)
1077     MGLYPH (gidx)->left_padding = ctx->left_padding;
1078 #endif
1079   return to;
1080 }
1081
1082 static int
1083 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1084              FontLayoutContext *ctx)
1085 {
1086   MGlyph g;
1087
1088   if (id >= 0)
1089     {
1090       int i;
1091
1092       /* Direct code (== id + ctx->code_offset) output.
1093          The source is not consumed.  */
1094       if (from < to)
1095         g = *(MGLYPH (from));
1096       else
1097         g = *(MGLYPH (from - 1));
1098       g.type = GLYPH_CHAR;
1099       g.code = ctx->code_offset + id;
1100       MDEBUG_PRINT1 (" (DIRECT 0x%X", g.code);
1101       if (ctx->combining_code)
1102         g.combining_code = ctx->combining_code;
1103       if (ctx->left_padding)
1104         g.left_padding = ctx->left_padding;
1105       for (i = from; i < to; i++)
1106         {
1107           MGlyph *tmp = MGLYPH (i);
1108
1109           if (g.pos > tmp->pos)
1110             g.pos = tmp->pos;
1111           else if (g.to < tmp->to)
1112             g.to = tmp->to;
1113         }
1114       APPEND_GLYPH (gstring, g);
1115       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1116       MDEBUG_PRINT (")");
1117       return (from);
1118     }
1119
1120   if (id <= CMD_ID_OFFSET_INDEX)
1121     {
1122       int idx = CMD_ID_TO_INDEX (id);
1123       FontLayoutCmd *cmd;
1124
1125       if (idx >= ctx->stage->used)
1126         MERROR (MERROR_DRAW, -1);
1127       cmd = ctx->stage->cmds + idx;
1128       if (cmd->type == FontLayoutCmdTypeRule)
1129         to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1130       else if (cmd->type == FontLayoutCmdTypeCond)
1131         to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1132       else if (cmd->type == FontLayoutCmdTypeOTF)
1133         to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1134
1135       if (to < 0)
1136         return -1;
1137       return to;
1138     }
1139
1140   if (id <= CMD_ID_OFFSET_COMBINING)
1141     {
1142       ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1143       return from;
1144     }
1145
1146   switch (id)
1147     {
1148     case CMD_ID_COPY:
1149       {
1150         if (from >= to)
1151           return from;
1152         g = *(MGLYPH (from));
1153         if (ctx->combining_code)
1154           g.combining_code = ctx->combining_code;
1155         if (ctx->left_padding)
1156           g.left_padding = ctx->left_padding;
1157         APPEND_GLYPH (gstring, g);
1158         ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1159         return (from + 1);
1160       }
1161
1162     case CMD_ID_CLUSTER_BEGIN:
1163       if (! ctx->cluster_begin_idx)
1164         {
1165           MDEBUG_PRINT1 (" <%d", MGLYPH (from)->pos);
1166           ctx->cluster_begin_idx = gstring->used;
1167           ctx->cluster_begin_pos = MGLYPH (from)->pos;
1168           ctx->cluster_end_pos = MGLYPH (from)->to;
1169         }
1170       return from;
1171
1172     case CMD_ID_CLUSTER_END:
1173       if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1174         {
1175           int i;
1176
1177           MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1178           for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1179             {
1180               MGLYPH (i)->pos = ctx->cluster_begin_pos;
1181               MGLYPH (i)->to = ctx->cluster_end_pos;
1182             }
1183           ctx->cluster_begin_idx = 0;
1184         }
1185       return from;
1186
1187     case CMD_ID_SEPARATOR:
1188       {
1189         if (from < to)
1190           g = *(MGLYPH (from));
1191         else
1192           g = *(MGLYPH (from - 1));
1193         g.type = GLYPH_PAD;
1194         /* g.c = g.code = 0; */
1195         g.width = 0;
1196         APPEND_GLYPH (gstring, g);
1197         return from;
1198       }
1199
1200     case CMD_ID_LEFT_PADDING:
1201       ctx->left_padding = 1;
1202       return from;
1203
1204     case CMD_ID_RIGHT_PADDING:
1205       if (gstring->used > 0)
1206         gstring->glyphs[gstring->used - 1].right_padding = 1;
1207       return from;
1208     }
1209
1210   MERROR (MERROR_DRAW, -1);
1211 }
1212
1213 \f
1214 /* Internal API */
1215
1216 int
1217 mfont__flt_init (void)
1218 {
1219   Mcond = msymbol ("cond");
1220   Mrange = msymbol ("range");
1221   Mlayouter = msymbol ("layouter");
1222   flt_list = mplist ();
1223   return 0;
1224 }
1225
1226 void
1227 mfont__flt_fini (void)
1228 {
1229   MPlist *plist, *pl;
1230
1231   MPLIST_DO (plist, flt_list)
1232     {
1233       pl = MPLIST_PLIST (plist);
1234       if (pl)
1235         {
1236           MPLIST_DO (pl, MPLIST_NEXT (pl))
1237             free_flt_stage (MPLIST_VAL (pl));
1238           pl = MPLIST_PLIST (plist);
1239           M17N_OBJECT_UNREF (pl);
1240         }
1241     }
1242   M17N_OBJECT_UNREF (flt_list);
1243 }
1244
1245 unsigned
1246 mfont__flt_encode_char (MSymbol layouter_name, int c)
1247 {
1248   MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1249   MCharTable *table;
1250   unsigned code;
1251
1252   if (! layouter)
1253     return MCHAR_INVALID_CODE;
1254   table = MPLIST_VAL (layouter);
1255   code = (unsigned) mchartable_lookup (table, c);
1256   return (code ? code : MCHAR_INVALID_CODE);
1257 }
1258
1259 int
1260 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1261 {
1262   int stage_idx = 0;
1263   int gidx;
1264   int i;
1265   FontLayoutContext ctx;
1266   MCharTable *table;
1267   int encoded_len;
1268   int match_indices[NMATCH];
1269   MSymbol layouter_name = rface->rfont->layouter;
1270   MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1271   MRealizedFace *ascii_rface = rface->ascii_rface;
1272   FontLayoutStage *stage;
1273   int from_pos, to_pos;
1274   MGlyph dummy;
1275
1276   if (! layouter)
1277     {
1278       /* FLT not found.  Make all glyphs invisible.  */
1279       while (from < to)
1280         gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1281       return to;
1282     }
1283
1284   dummy = gstring->glyphs[from];
1285   MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1286
1287   /* Setup CTX.  */
1288   memset (&ctx, 0, sizeof ctx);
1289   ctx.rfont = rface->rfont;
1290   table = MPLIST_VAL (layouter);
1291   layouter = MPLIST_NEXT (layouter);
1292   stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1293   gidx = from;
1294   /* Find previous glyphs that are also supported by the layouter.  */
1295   while (gidx > 1
1296          && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1297     gidx--;
1298   /* + 2 is for a separator ' ' and a terminator '\0'.  */
1299   encoded_len = gstring->used - gidx + 2;
1300   ctx.encoded = (char *) alloca (encoded_len);
1301
1302   for (i = 0; gidx < from; i++, gidx++)
1303     ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1304
1305   ctx.encoded[i++] = ' ';
1306   ctx.encoded_offset = from - i;
1307
1308   /* Now each MGlyph->code contains encoded char.  Set it in
1309      ctx.encoded[], and set MGlyph->c to MGlyph->code.  */
1310   for (gidx = from; gidx < to ; i++, gidx++)
1311     {
1312       ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1313       MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1314     }
1315   ctx.encoded[i++] = '\0';
1316
1317   match_indices[0] = from;
1318   match_indices[1] = to;
1319   for (i = 2; i < NMATCH; i++)
1320     match_indices[i] = -1;
1321   ctx.match_indices = match_indices;
1322
1323   from_pos = MGLYPH (from)->pos;
1324   to_pos = MGLYPH (to)->pos;
1325
1326   for (stage_idx = 0; 1; stage_idx++)
1327     {
1328       int len = to - from;
1329       int result;
1330
1331       MDEBUG_PRINT1 ("\n [FLT]   (STAGE %d", stage_idx);
1332       gidx = gstring->used;
1333       ctx.stage = stage;
1334
1335       result = run_command (2, INDEX_TO_CMD_ID (0), gstring,
1336                             ctx.encoded_offset, to, &ctx);
1337       MDEBUG_PRINT (")");
1338       if (result < 0)
1339         return -1;
1340       to = from + (gstring->used - gidx);
1341       REPLACE_GLYPHS (gstring, gidx, from, len);
1342
1343       layouter = MPLIST_NEXT (layouter);
1344       /* If this is the last stage, break the loop. */
1345       if (MPLIST_TAIL_P (layouter))
1346         break;
1347
1348       /* Otherwise, prepare for the next iteration.   */
1349       stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1350       table = stage->category;
1351       if (to - from >= encoded_len)
1352         {
1353           encoded_len = to + 1;
1354           ctx.encoded = (char *) alloca (encoded_len);
1355         }
1356
1357       for (i = from; i < to; i++)
1358         {
1359           MGlyph *g = MGLYPH (i);
1360
1361           if (g->type == GLYPH_PAD)
1362             ctx.encoded[i - from] = ' ';
1363           else if (! g->otf_encoded)
1364             ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1365 #ifdef HAVE_FREETYPE
1366           else
1367             {
1368               int c = mfont__ft_decode_otf (g);
1369
1370               if (c >= 0)
1371                 {
1372                   c = (int) mchartable_lookup (table, c);
1373                   if (! c)
1374                     c = -1;
1375                 }
1376               ctx.encoded[i - from] = (c >= 0 ? c : 1);
1377             }
1378 #endif  /* HAVE_FREETYPE */
1379         }
1380       ctx.encoded[i - from] = '\0';
1381       ctx.encoded_offset = from;
1382       ctx.match_indices[0] = from;
1383       ctx.match_indices[1] = to;
1384     }
1385
1386   MDEBUG_PRINT (")\n");
1387
1388   if (from == to)
1389     {
1390       /* Somehow there's no glyph contributing to characters between
1391          FROM_POS and TO_POS.  We must add one dummy space glyph for
1392          those characters.  */
1393       MGlyph g;
1394
1395       g.type = GLYPH_SPACE;
1396       g.c = ' ', g.code = ' ';
1397       g.pos = from_pos, g.to = to_pos;
1398       g.rface = ascii_rface;
1399       INSERT_GLYPH (gstring, from, g);
1400       to = from + 1;
1401     }
1402   else
1403     {
1404       /* Here we must check if all characters in the range is covered
1405          by some glyph(s).  If not, change <pos> and <to> of glyphs to
1406          cover uncovered characters.  */
1407       int len = to_pos - from_pos;
1408       int pos;
1409       MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1410       MGlyph *g, *gend = MGLYPH (to);
1411       MGlyph *latest = gend;
1412
1413       for (i = 0; i < len; i++)
1414         glyphs[i] = NULL;
1415       for (g = MGLYPH (from); g != gend; g++)
1416         {
1417           if (g->pos < latest->pos)
1418             latest = g;
1419           if (! glyphs[g->pos - from_pos])
1420             {
1421               for (i = g->pos; i < g->to; i++)
1422                 glyphs[i - from_pos] = g;
1423             }
1424         }
1425       i = 0;
1426       if (! glyphs[0])
1427         {
1428           pos = latest->pos;
1429           for (g = latest; g->pos == pos; g++)
1430             g->pos = from_pos;
1431           i++;
1432         }
1433       for (; i < len; i++)
1434         {
1435           if (! glyphs[i])
1436             {
1437               for (g = latest; g->pos == latest->pos; g++)
1438                 g->to = from_pos + i + 1;
1439             }
1440           else
1441             latest = glyphs[i];
1442         }
1443     }
1444   return to;
1445 }
1446
1447 \f
1448 /* for debugging... */
1449
1450 static void
1451 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1452 {
1453   char *prefix = (char *) alloca (indent + 1);
1454
1455   memset (prefix, 32, indent);
1456   prefix[indent] = 0;
1457
1458   if (id >= 0)
1459     fprintf (stderr, "0x%02X", id);
1460   else if (id <= CMD_ID_OFFSET_INDEX)
1461     {
1462       int idx = CMD_ID_TO_INDEX (id);
1463       FontLayoutCmd *cmd = stage->cmds + idx;
1464
1465       if (cmd->type == FontLayoutCmdTypeRule)
1466         {
1467           FontLayoutCmdRule *rule = &cmd->body.rule;
1468           int i;
1469
1470           fprintf (stderr, "(rule ");
1471           if (rule->src_type == SRC_REGEX)
1472             fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1473           else if (rule->src_type == SRC_INDEX)
1474             fprintf (stderr, "%d", rule->src.match_idx);
1475           else if (rule->src_type == SRC_SEQ)
1476             fprintf (stderr, "(seq)");
1477           else if (rule->src_type == SRC_RANGE)
1478             fprintf (stderr, "(range)");
1479           else
1480             fprintf (stderr, "(invalid src)");
1481
1482           for (i = 0; i < rule->n_cmds; i++)
1483             {
1484               fprintf (stderr, "\n%s  ", prefix);
1485               dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1486             }
1487           fprintf (stderr, ")");
1488         }
1489       else if (cmd->type == FontLayoutCmdTypeCond)
1490         {
1491           FontLayoutCmdCond *cond = &cmd->body.cond;
1492           int i;
1493
1494           fprintf (stderr, "(cond");
1495           for (i = 0; i < cond->n_cmds; i++)
1496             {
1497               fprintf (stderr, "\n%s  ", prefix);
1498               dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1499             }
1500           fprintf (stderr, ")");
1501         }
1502       else if (cmd->type == FontLayoutCmdTypeOTF)
1503         {
1504           fprintf (stderr, "(otf)");
1505         }
1506       else
1507         fprintf (stderr, "(error-command)");
1508     }
1509   else if (id <= CMD_ID_OFFSET_COMBINING)
1510     fprintf (stderr, "cominging-code");
1511   else
1512     fprintf (stderr, "(predefiend %d)", id);
1513 }
1514
1515 void
1516 dump_flt (MFontLayoutTable *flt, int indent)
1517 {
1518   char *prefix = (char *) alloca (indent + 1);
1519   MPlist *plist;
1520   int stage_idx = 0;
1521
1522   memset (prefix, 32, indent);
1523   prefix[indent] = 0;
1524   fprintf (stderr, "(flt");
1525   MPLIST_DO (plist, flt)
1526     {
1527       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1528       int i;
1529
1530       fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
1531       for (i = 0; i < stage->used; i++)
1532         {
1533           fprintf (stderr, "\n%s    ", prefix);
1534           dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1535         }
1536       fprintf (stderr, ")");
1537       stage_idx++;
1538     }
1539   fprintf (stderr, ")");
1540 }