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