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
6 This file is part of the m17n library.
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.
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.
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., 51 Franklin Street, Fifth Floor,
29 #include <sys/types.h>
33 #include "m17n-misc.h"
38 #include "internal-flt.h"
39 #include "internal-gui.h"
45 /* Font Layout Table (FLT)
47 Predefined terms: SYMBOL, INTEGER, STRING
49 FLT ::= '(' STAGE + ')'
51 STAGE ::= CATEGORY-TABLE ? FONT-LAYOUT-RULE
53 ;; Each STAGE consumes a source (code sequence) and produces another
54 ;; code sequence that is given to the next STAGE as a source. The
55 ;; source given to the first stage is a sequence of character codes
56 ;; that are assigned category codes by CATEGORY-TABLE. The output of
57 ;; the last stage is a glyph code sequence given to the renderer.
60 '(' 'category' CATEGORY-SPEC + ')'
62 '(' CODE [ CODE ] CATEGORY ')'
65 ;; ASCII character codes of alphabet ('A' .. 'Z' 'a' .. 'z').
68 ;; (0x0900 0x097F ?E) ; All Devanagari characters
69 ;; (0x093C ?N)) ; DEVANAGARI-LETTER NUKTA
70 ;; Assign the category 'E' to all Devanagari characters but 0x093C,
71 ;; assign the category 'N' to 0x093C.
74 '(' 'generator' RULE MACRO-DEF * ')'
76 RULE ::= COMMAND | REGEXP-RULE | MATCH-RULE | MAP-RULE
77 | COND-STRUCT | MACRO-NAME
80 DIRECT-CODE | COMBINING | PREDEFIND-COMMAND | OTF-COMMAND
82 DIRECT-CODE ::= INTEGER
83 ;; Always succeed. Produce the code. Consume no source.
86 '=' | '*' | '<' | '>' | '|'
88 ;; '=': Succeed when the current run contains at least one code.
89 ;; Consume the first code in the current run, and produce it as is.
91 ;; '*': If the the previous command succeeded, repeat it until it
94 ;; '<': Produce a special code that indicates the start of grapheme
95 ;; cluster. Succeed always, consume nothing.
97 ;; '>': Produce a special code that indicates the end of grapheme
98 ;; cluster. Succeed always, consume nothing.
100 ;; '|': Produce a special code whose category is ' '. Succeed always,
104 'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]]
105 ;; Run the Open Type Layout Table on the current run. Succeed always,
109 ;; OTF's ScriptTag name (four letters) listed at:
110 ;; <http://www.microsoft.om/typograph/otspec/scripttags.htm>
112 ;; OTF's Language System name (four letters) listed at:
113 ;; <http://www.microsoft.om/typograph/otspec/languagetags.htm>
115 GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
116 GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
118 ;; OTF's Feature name (four letters) listed at:
119 ;; <http://www.microsoft.om/typograph/otspec/???.htm>
121 OTF-TAG ::= PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR
125 ;; Run all features in the default langsys of 'deva' script.
126 ;; 'otf:deva::nukt:haln'
127 ;; Run all GSUB features, run 'nukt' and 'haln' GPOS features.
129 ;; Run all GSUB features, run no GPOS features.
132 '(' REGEXP RULE * ')'
134 ;; Succeed if REGXP matches the head of source. Run RULEs while
135 ;; limiting the source to the matching part. Consume that part.
138 ;; Must be composed only from ASCII characters. 'A' - 'Z', 'a' - 'z'
139 ;; correspond to CATEGORY.
146 '(' MATCH-IDX RULE * ')'
148 ;; Succeed if the previous REGEXP-RULE found a matching part for
149 ;; MATCH-IDX. Run RULEs while limiting the source to the matching
150 ;; part. If MATCH-IDX is zero, consume the whole part, else consume
153 MATCH-IDX ::= INTEGER
160 '(' ( SOURCE-SEQ | SOURCE-RANGE ) RULE * ')'
162 ;; Succeed if the source matches SOURCE-SEQ or SOURCE-RANGE. Run
163 ;; RULEs while limiting the source to the matching part. Consume that
169 '(' 'range' CODE CODE ')'
171 ;; ((0x0915 0x094D) 0x43)
172 ;; If the source code sequence is 0x0915 0x094D, produce 0x43.
173 ;; ((range 0x0F40 0x0F6A) 0x2221)
174 ;; If the first source code CODE is in the range 0x0F40..0x0F6A,
175 ;; produce (0x2221 + (CODE - 0x0F40)).
178 '(' 'cond' RULE + ')'
180 ;; Try each rule in sequence until one succeeds. Succeed if one
181 ;; succeeds. Consume nothing.
185 ;; ((0x0915 0x094D) 0x43)
186 ;; ((range 0x0F40 0x0F6A) 0x2221)
189 COMBINING ::= 'V''H''O''V''H'
190 V ::= ( 't' | 'c' | 'b' | 'B' )
191 H ::= ( 'l' | 'c' | 'r' )
192 O ::= ( '.' | XOFF | YOFF | XOFF YOFF )
193 XOFF ::= '<'INTEGER | '>'INTEGER
194 YOFF ::= '+'INTEGER | '-'INTEGER
195 ;; INTEGER must be integer 0..127
197 ;; VH pair indicates 12 reference points of a glyph as below:
199 ;; 0----1----2 <---- ascent 0:tl (top-left)
200 ;; | | 1:tc (top-center)
201 ;; | | 2:tr (top-right)
202 ;; | | 3:Bl (base-left)
203 ;; 9 10 11 <---- center 4:Bc (base-center)
204 ;; | | 5:Br (base-right)
205 ;; --3----4----5-- <-- baseline 6:bl (bottom-left)
206 ;; | | 7:bc (bottom-center)
207 ;; 6----7----8 <---- descent 8:br (bottom-right)
208 ;; 9:cl (center-left)
209 ;; | | | 10:cc (center-center)
210 ;; left center right 11:cr (center-right)
214 ;; Align top-left point of the previous glyph and bottom-center
215 ;; point of the current glyph.
217 ;; Align 20% left and 10% below of base-left point of the previous
218 ;; glyph and base-right point of the current glyph.
221 '(' MACRO-NAME RULE + ')'
222 MACRO-NAME ::= SYMBOL
226 static int mdebug_flag = MDEBUG_FONT_FLT;
230 static MPlist *flt_list;
235 -0x0F .. -2 : builtin commands
236 -0x100000F .. -0x10 : combining code
237 ... -0x1000010: index to FontLayoutStage->cmds
240 #define INVALID_CMD_ID -1
241 #define CMD_ID_OFFSET_BUILTIN -2
242 #define CMD_ID_OFFSET_COMBINING -0x10
243 #define CMD_ID_OFFSET_INDEX -0x1000010
245 /* Builtin commands. */
246 #define CMD_ID_COPY -2 /* '=' */
247 #define CMD_ID_REPEAT -3 /* '*' */
248 #define CMD_ID_CLUSTER_BEGIN -4 /* '<' */
249 #define CMD_ID_CLUSTER_END -5 /* '>' */
250 #define CMD_ID_SEPARATOR -6 /* '|' */
251 #define CMD_ID_LEFT_PADDING -7 /* '[' */
252 #define CMD_ID_RIGHT_PADDING -8 /* ']' */
254 #define CMD_ID_TO_COMBINING_CODE(id) (CMD_ID_OFFSET_COMBINING - (id))
255 #define COMBINING_CODE_TO_CMD_ID(code) (CMD_ID_OFFSET_COMBINING - (code))
257 #define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id))
258 #define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx))
260 static MSymbol Mcond, Mrange, Mexist;
262 #define GLYPH_CODE_P(code) \
263 ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX)
265 #define GLYPH_CODE_INDEX(code) ((code) - GLYPH_CODE_MIN)
267 #define UPDATE_CLUSTER_RANGE(ctx, g) \
269 if ((ctx)->cluster_begin_idx) \
271 if (ctx->cluster_begin_pos > (g).pos) \
272 ctx->cluster_begin_pos = (g).pos; \
273 if (ctx->cluster_end_pos < (g).to) \
274 ctx->cluster_end_pos = (g).to; \
278 enum FontLayoutCmdRuleSrcType
289 enum FontLayoutCmdRuleSrcType src_type;
314 /* Beginning and end indices of series of SEQ commands. */
315 int seq_beg, seq_end;
316 /* Range of the first character appears in the above series. */
317 int seq_from, seq_to;
323 enum FontLayoutCmdType
325 FontLayoutCmdTypeRule,
326 FontLayoutCmdTypeCond,
327 FontLayoutCmdTypeOTF,
333 enum FontLayoutCmdType type;
335 FontLayoutCmdRule rule;
336 FontLayoutCmdCond cond;
337 MFontCapability *otf;
343 MCharTable *category;
348 typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */
350 /* Font layout table loader */
352 /* Load a category table from PLIST. PLIST has this form:
353 PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
357 load_category_table (MPlist *plist)
361 table = mchartable (Minteger, (void *) 0);
363 MPLIST_DO (plist, plist)
366 int from, to, category_code;
368 if (! MPLIST_PLIST (plist))
369 MERROR (MERROR_FONT, NULL);
370 elt = MPLIST_PLIST (plist);
371 if (! MPLIST_INTEGER_P (elt))
372 MERROR (MERROR_FONT, NULL);
373 from = MPLIST_INTEGER (elt);
374 elt = MPLIST_NEXT (elt);
375 if (! MPLIST_INTEGER_P (elt))
376 MERROR (MERROR_FONT, NULL);
377 to = MPLIST_INTEGER (elt);
378 elt = MPLIST_NEXT (elt);
379 if (MPLIST_TAIL_P (elt))
386 if (! MPLIST_INTEGER_P (elt))
387 MERROR (MERROR_FONT, NULL);
388 category_code = MPLIST_INTEGER (elt);
390 if (! isalnum (category_code))
391 MERROR (MERROR_FONT, NULL);
394 mchartable_set (table, from, (void *) category_code);
396 mchartable_set_range (table, from, to, (void *) category_code);
403 /* Parse OTF command name NAME and store the result in CMD.
405 :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
406 where GSUB-FEATURES and GPOS-FEATURES have this form:
407 [FEATURE[,FEATURE]*] | ' ' */
410 load_otf_command (FontLayoutCmd *cmd, MSymbol sym)
412 char *name = MSYMBOL_NAME (sym);
416 /* This is old format of "otf:...". Change it to ":otf=...". */
417 char *str = alloca (MSYMBOL_NAMELEN (sym) + 2);
419 sprintf (str, ":otf=");
420 strcat (str, name + 4);
424 cmd->body.otf = mfont__get_capability (sym);
427 if (cmd->body.otf->script == Mnil)
429 cmd->body.otf = NULL;
432 M17N_OBJECT_REF (cmd->body.otf);
433 cmd->type = FontLayoutCmdTypeOTF;
438 /* Read a decimal number from STR preceded by one of "+-><". '+' and
439 '>' means a plus sign, '-' and '<' means a minus sign. If the
440 number is greater than 127, limit it to 127. */
443 read_decimal_number (char **str)
446 int sign = (*p == '-' || *p == '<') ? -1 : 1;
450 while (*p >= '0' && *p <= '9')
451 n = n * 10 + *p++ - '0';
455 return (n < 127 ? n * sign : 127 * sign);
459 /* Read a horizontal and vertical combining positions from STR, and
460 store them in the place pointed by X and Y. The horizontal
461 position left, center, and right are represented by 0, 1, and 2
462 respectively. The vertical position top, center, bottom, and base
463 are represented by 0, 1, 2, and 3 respectively. If successfully
464 read, return 0, else return -1. */
467 read_combining_position (char *str, int *x, int *y)
472 /* Vertical position comes first. */
473 for (i = 0; i < 4; i++)
482 /* Then comse horizontal position. */
483 for (i = 0; i < 3; i++)
493 /* Return a combining code corresponding to SYM. */
496 get_combining_command (MSymbol sym)
498 char *str = msymbol_name (sym);
499 int base_x, base_y, add_x, add_y, off_x, off_y;
502 if (read_combining_position (str, &base_x, &base_y) < 0)
513 if (c == '+' || c == '-')
515 off_y = read_decimal_number (&str) + 128;
520 if (c == '<' || c == '>')
521 off_x = read_decimal_number (&str) + 128;
525 if (read_combining_position (str, &add_x, &add_y) < 0)
528 c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
529 return (COMBINING_CODE_TO_CMD_ID (c));
533 /* Load a command from PLIST into STAGE, and return that
534 identification number. If ID is not INVALID_CMD_ID, that means we
535 are loading a top level command or a macro. In that case, use ID
536 as the identification number of the command. Otherwise, generate a
537 new id number for the command. MACROS is a list of raw macros. */
540 load_command (FontLayoutStage *stage, MPlist *plist,
541 MPlist *macros, int id)
545 if (MPLIST_INTEGER_P (plist))
547 int code = MPLIST_INTEGER (plist);
550 MERROR (MERROR_DRAW, INVALID_CMD_ID);
553 else if (MPLIST_PLIST_P (plist))
555 /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
556 | ( ( INTEGER INTEGER ) ... )
557 | ( ( range INTEGER INTEGER ) ... ) */
558 MPlist *elt = MPLIST_PLIST (plist);
559 int len = MPLIST_LENGTH (elt) - 1;
562 if (id == INVALID_CMD_ID)
565 id = INDEX_TO_CMD_ID (stage->used);
566 MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
568 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
570 if (MPLIST_SYMBOL_P (elt))
572 FontLayoutCmdCond *cond;
574 if (MPLIST_SYMBOL (elt) != Mcond)
575 MERROR (MERROR_DRAW, INVALID_CMD_ID);
576 elt = MPLIST_NEXT (elt);
577 cmd->type = FontLayoutCmdTypeCond;
578 cond = &cmd->body.cond;
579 cond->seq_beg = cond->seq_end = -1;
580 cond->seq_from = cond->seq_to = 0;
582 MTABLE_CALLOC (cond->cmd_ids, len, MERROR_DRAW);
583 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
585 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
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 cond = &cmd->body.cond;
592 cond->cmd_ids[i] = this_id;
593 if (this_id <= CMD_ID_OFFSET_INDEX)
595 FontLayoutCmd *this_cmd
596 = stage->cmds + CMD_ID_TO_INDEX (this_id);
598 if (this_cmd->type == FontLayoutCmdTypeRule
599 && this_cmd->body.rule.src_type == SRC_SEQ)
601 int first_char = this_cmd->body.rule.src.seq.codes[0];
603 if (cond->seq_beg < 0)
605 /* The first SEQ command. */
607 cond->seq_from = cond->seq_to = first_char;
609 else if (cond->seq_end < 0)
611 /* The following SEQ command. */
612 if (cond->seq_from > first_char)
613 cond->seq_from = first_char;
614 else if (cond->seq_to < first_char)
615 cond->seq_to = first_char;
620 if (cond->seq_beg >= 0 && cond->seq_end < 0)
621 /* The previous one is the last SEQ command. */
627 if (cond->seq_beg >= 0 && cond->seq_end < 0)
628 /* The previous one is the last SEQ command. */
632 if (cond->seq_beg >= 0 && cond->seq_end < 0)
633 /* The previous one is the last SEQ command. */
638 cmd->type = FontLayoutCmdTypeRule;
639 if (MPLIST_MTEXT_P (elt))
641 MText *mt = MPLIST_MTEXT (elt);
642 char *str = (char *) MTEXT_DATA (mt);
646 mtext_ins_char (mt, 0, '^', 1);
647 str = (char *) MTEXT_DATA (mt);
649 if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
650 MERROR (MERROR_FONT, INVALID_CMD_ID);
651 cmd->body.rule.src_type = SRC_REGEX;
652 cmd->body.rule.src.re.pattern = strdup (str);
654 else if (MPLIST_INTEGER_P (elt))
656 cmd->body.rule.src_type = SRC_INDEX;
657 cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
659 else if (MPLIST_PLIST_P (elt))
661 MPlist *pl = MPLIST_PLIST (elt);
662 int size = MPLIST_LENGTH (pl);
664 if (MPLIST_INTEGER_P (pl))
668 cmd->body.rule.src_type = SRC_SEQ;
669 cmd->body.rule.src.seq.n_codes = size;
670 MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
672 for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
674 if (! MPLIST_INTEGER_P (pl))
675 MERROR (MERROR_DRAW, INVALID_CMD_ID);
676 cmd->body.rule.src.seq.codes[i]
677 = (unsigned) MPLIST_INTEGER (pl);
680 else if (MPLIST_SYMBOL_P (pl) && size == 3)
682 cmd->body.rule.src_type = SRC_RANGE;
683 pl = MPLIST_NEXT (pl);
684 if (! MPLIST_INTEGER_P (pl))
685 MERROR (MERROR_DRAW, INVALID_CMD_ID);
686 cmd->body.rule.src.range.from
687 = (unsigned) MPLIST_INTEGER (pl);
688 pl = MPLIST_NEXT (pl);
689 if (! MPLIST_INTEGER_P (pl))
690 MERROR (MERROR_DRAW, INVALID_CMD_ID);
691 cmd->body.rule.src.range.to
692 = (unsigned) MPLIST_INTEGER (pl);
694 else if (MPLIST_SYMBOL_P (pl) && size <= 2)
696 if (MPLIST_SYMBOL (pl) != Mexist)
697 MERROR (MERROR_FLT, INVALID_CMD_ID);
698 cmd->body.rule.src_type = SRC_EXIST;
700 cmd->body.rule.src.exist.c = -1;
703 pl = MPLIST_NEXT (pl);
704 if (! MPLIST_INTEGER_P (pl))
705 MERROR (MERROR_DRAW, INVALID_CMD_ID);
706 cmd->body.rule.src.exist.c = MPLIST_INTEGER (pl);
710 MERROR (MERROR_DRAW, INVALID_CMD_ID);
713 MERROR (MERROR_DRAW, INVALID_CMD_ID);
715 elt = MPLIST_NEXT (elt);
716 cmd->body.rule.n_cmds = len;
717 MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
718 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
720 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
722 if (this_id == INVALID_CMD_ID)
723 MERROR (MERROR_DRAW, INVALID_CMD_ID);
724 /* The above load_command may relocate stage->cmds. */
725 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
726 cmd->body.rule.cmd_ids[i] = this_id;
730 else if (MPLIST_SYMBOL_P (plist))
733 MSymbol sym = MPLIST_SYMBOL (plist);
734 char *name = msymbol_name (sym);
735 int len = strlen (name);
739 && ((name[0] == 'o' && name[1] == 't'
740 && name[2] == 'f' && name[3] == ':')
741 || (name[0] == ':' && name[1] == 'o' && name[2] == 't'
742 && name[3] == 'f' && name[4] == '='))
743 && load_otf_command (&cmd, sym) >= 0)
745 if (id == INVALID_CMD_ID)
747 id = INDEX_TO_CMD_ID (stage->used);
748 MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
751 stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
759 else if (*name == '*')
760 return CMD_ID_REPEAT;
761 else if (*name == '<')
762 return CMD_ID_CLUSTER_BEGIN;
763 else if (*name == '>')
764 return CMD_ID_CLUSTER_END;
765 else if (*name == '|')
766 return CMD_ID_SEPARATOR;
767 else if (*name == '[')
768 return CMD_ID_LEFT_PADDING;
769 else if (*name == ']')
770 return CMD_ID_RIGHT_PADDING;
776 id = get_combining_command (sym);
782 MPLIST_DO (elt, macros)
784 if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
786 id = INDEX_TO_CMD_ID (i);
787 if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
788 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
794 MERROR (MERROR_DRAW, INVALID_CMD_ID);
797 MERROR (MERROR_DRAW, INVALID_CMD_ID);
803 free_flt_command (FontLayoutCmd *cmd)
805 if (cmd->type == FontLayoutCmdTypeRule)
807 FontLayoutCmdRule *rule = &cmd->body.rule;
809 if (rule->src_type == SRC_REGEX)
811 free (rule->src.re.pattern);
812 regfree (&rule->src.re.preg);
814 else if (rule->src_type == SRC_SEQ)
815 free (rule->src.seq.codes);
816 free (rule->cmd_ids);
818 else if (cmd->type == FontLayoutCmdTypeCond)
819 free (cmd->body.cond.cmd_ids);
820 else if (cmd->type == FontLayoutCmdTypeOTF)
821 M17N_OBJECT_UNREF (cmd->body.otf);
824 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
825 and return it. PLIST has this form:
826 PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
829 static FontLayoutStage *
830 load_generator (MPlist *plist)
832 FontLayoutStage *stage;
836 MSTRUCT_CALLOC (stage, MERROR_DRAW);
837 MLIST_INIT1 (stage, cmds, 32);
838 dummy.type = FontLayoutCmdTypeMAX;
839 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
840 MPLIST_DO (elt, MPLIST_NEXT (plist))
842 if (! MPLIST_PLIST_P (elt))
843 MERROR (MERROR_FONT, NULL);
844 pl = MPLIST_PLIST (elt);
845 if (! MPLIST_SYMBOL_P (pl))
846 MERROR (MERROR_FONT, NULL);
847 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
850 /* Load the first command from PLIST into STAGE->cmds[0]. Macros
851 called in the first command are also loaded from MPLIST_NEXT
852 (PLIST) into STAGE->cmds[n]. */
853 if (load_command (stage, plist, MPLIST_NEXT (plist), INDEX_TO_CMD_ID (0))
856 MLIST_FREE1 (stage, cmds);
858 MERROR (MERROR_DRAW, NULL);
865 /* Load FLT of name LAYOUTER_NAME from the m17n database into a newly
866 allocated memory, and return it. */
868 static MFontLayoutTable *
869 load_flt (MSymbol layouter_name)
872 MPlist *top = NULL, *plist;
873 MSymbol Mcategory = msymbol ("category");
874 MSymbol Mgenerator = msymbol ("generator");
875 MSymbol Mend = msymbol ("end");
876 MFontLayoutTable *layouter = NULL;
877 MCharTable *category = NULL;
879 if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
880 MERROR_GOTO (MERROR_FONT, finish);
881 if (! (top = (MPlist *) mdatabase_load (mdb)))
882 MERROR_GOTO (0, finish);
883 if (! MPLIST_PLIST_P (top))
884 MERROR_GOTO (MERROR_FONT, finish);
886 MPLIST_DO (plist, top)
891 if (MPLIST_SYMBOL_P (plist)
892 && MPLIST_SYMBOL (plist) == Mend)
894 if (! MPLIST_PLIST (plist))
895 MERROR_GOTO (MERROR_FONT, finish);
896 elt = MPLIST_PLIST (plist);
897 if (! MPLIST_SYMBOL_P (elt))
898 MERROR_GOTO (MERROR_FONT, finish);
899 sym = MPLIST_SYMBOL (elt);
900 elt = MPLIST_NEXT (elt);
902 MERROR_GOTO (MERROR_FONT, finish);
903 if (sym == Mcategory)
906 M17N_OBJECT_UNREF (category);
907 category = load_category_table (elt);
909 else if (sym == Mgenerator)
911 FontLayoutStage *stage;
914 MERROR_GOTO (MERROR_FONT, finish);
915 stage = load_generator (elt);
917 MERROR_GOTO (MERROR_FONT, finish);
918 stage->category = category;
919 M17N_OBJECT_REF (category);
922 layouter = mplist ();
923 /* Here don't do M17N_OBJECT_REF (category) because we
924 don't unref the value of the element added below. */
925 mplist_add (layouter, Mcategory, category);
927 mplist_add (layouter, Mt, stage);
932 M17N_OBJECT_UNREF (category);
935 M17N_OBJECT_UNREF (top);
936 mplist_add (flt_list, layouter_name, layouter);
942 free_flt_stage (FontLayoutStage *stage)
946 M17N_OBJECT_UNREF (stage->category);
947 for (i = 0; i < stage->used; i++)
948 free_flt_command (stage->cmds + i);
949 MLIST_FREE1 (stage, cmds);
954 static MFontLayoutTable *
955 get_font_layout_table (MSymbol layouter_name)
957 MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
959 return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
963 /* FLS (Font Layout Service) */
965 /* Structure to hold information about a context of FLS. */
969 /* Pointer to the current stage. */
970 FontLayoutStage *stage;
972 /* Encode each MGlyph->code by the current category table into this
973 array. An element is a category. */
975 /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
980 int cluster_begin_idx;
981 int cluster_begin_pos;
987 static int run_command (int depth,
988 int, MGlyphString *, int, int, FontLayoutContext *);
994 FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
995 FontLayoutContext *ctx)
997 int *saved_match_indices = ctx->match_indices;
998 int match_indices[NMATCH * 2];
1001 int orig_from = from;
1003 if (rule->src_type == SRC_SEQ)
1007 len = rule->src.seq.n_codes;
1008 if (len > (to - from))
1010 for (i = 0; i < len; i++)
1011 if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
1016 if (MDEBUG_FLAG () > 2)
1017 MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "",
1018 rule->src.seq.codes[0]);
1020 else if (rule->src_type == SRC_RANGE)
1026 head = gstring->glyphs[from].code;
1027 if (head < rule->src.range.from || head > rule->src.range.to)
1029 ctx->code_offset = head - rule->src.range.from;
1031 if (MDEBUG_FLAG () > 2)
1032 MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "",
1033 rule->src.range.from, rule->src.range.to);
1035 else if (rule->src_type == SRC_REGEX)
1037 regmatch_t pmatch[NMATCH];
1043 saved_code = ctx->encoded[to - ctx->encoded_offset];
1044 ctx->encoded[to - ctx->encoded_offset] = '\0';
1045 result = regexec (&(rule->src.re.preg),
1046 ctx->encoded + from - ctx->encoded_offset,
1048 if (result == 0 && pmatch[0].rm_so == 0)
1050 if (MDEBUG_FLAG () > 2)
1051 MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "",
1052 rule->src.re.pattern,
1053 ctx->encoded + from - ctx->encoded_offset,
1055 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1056 for (i = 0; i < NMATCH; i++)
1058 if (pmatch[i].rm_so < 0)
1059 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1062 match_indices[i * 2] = from + pmatch[i].rm_so;
1063 match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1066 ctx->match_indices = match_indices;
1067 to = match_indices[1];
1071 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1075 else if (rule->src_type == SRC_INDEX)
1077 if (rule->src.match_idx >= NMATCH)
1079 from = ctx->match_indices[rule->src.match_idx * 2];
1082 to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1083 if (MDEBUG_FLAG () > 2)
1084 MDEBUG_PRINT3 ("\n [FLT] %*s(INDEX %d", depth, "", rule->src.match_idx);
1086 else if (rule->src_type == SRC_EXIST)
1088 MGlyph *g = MGLYPH (from);
1089 int encoded = g->otf_encoded;
1092 if (rule->src.exist.c < 0)
1101 code = rule->src.exist.c;
1107 code = (g->rface->rfont->driver->encode_char
1108 (NULL, (MFont *) g->rface->rfont, NULL, code));
1109 if (code == MCHAR_INVALID_CODE)
1116 for (i = 0; i < rule->n_cmds; i++)
1120 if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1126 pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1128 MERROR (MERROR_DRAW, -1);
1129 consumed = pos > from;
1134 ctx->match_indices = saved_match_indices;
1135 if (MDEBUG_FLAG () > 2)
1137 return (rule->src_type == SRC_INDEX ? orig_from : to);
1141 run_cond (int depth,
1142 FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1143 FontLayoutContext *ctx)
1147 if (MDEBUG_FLAG () > 2)
1148 MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1150 for (i = 0; i < cond->n_cmds; i++)
1152 /* TODO: Write a code for optimization utilizaing the info
1154 if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1159 MERROR (MERROR_DRAW, -1);
1160 if (MDEBUG_FLAG () > 2)
1167 MFontCapability *otf_cmd, MGlyphString *gstring, int from, int to,
1168 FontLayoutContext *ctx)
1171 int from_idx = gstring->used;
1173 if (MDEBUG_FLAG () > 2)
1175 MDEBUG_PRINT4 ("\n [FLT] %*s(OTF %s,%s)", depth, "",
1176 (! otf_cmd->features[MFONT_OTT_GSUB].str ? ""
1177 : otf_cmd->features[MFONT_OTT_GSUB].str),
1178 (! otf_cmd->features[MFONT_OTT_GPOS].str ? ""
1179 : otf_cmd->features[MFONT_OTT_GPOS].str));
1181 MDEBUG_PRINT3 ("\n [FLT] %*s:otf=%s", depth, "",
1182 MSYMBOL_NAME (otf_cmd->otf));
1184 to = mfont__ft_drive_otf (gstring, from, to, otf_cmd);
1185 if (ctx->cluster_begin_idx)
1186 for (; from_idx < gstring->used; from_idx++)
1187 UPDATE_CLUSTER_RANGE (ctx, gstring->glyphs[from_idx]);
1192 extern char *dump_combining_code (int code);
1195 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1196 FontLayoutContext *ctx)
1204 /* Direct code (== id + ctx->code_offset) output.
1205 The source is not consumed. */
1206 if (from < to || from == 1)
1207 g = *(MGLYPH (from));
1209 g = *(MGLYPH (from - 1));
1210 g.type = GLYPH_CHAR;
1211 g.code = ctx->code_offset + id;
1212 if (MDEBUG_FLAG () > 2)
1213 MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "", g.code);
1214 if (ctx->combining_code)
1215 g.combining_code = ctx->combining_code;
1216 if (ctx->left_padding)
1217 g.left_padding = ctx->left_padding;
1218 for (i = from; i < to; i++)
1220 MGlyph *tmp = MGLYPH (i);
1222 if (g.pos > tmp->pos)
1224 else if (g.to < tmp->to)
1227 APPEND_GLYPH (gstring, g);
1228 UPDATE_CLUSTER_RANGE (ctx, g);
1229 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1230 if (MDEBUG_FLAG () > 2)
1235 if (id <= CMD_ID_OFFSET_INDEX)
1237 int idx = CMD_ID_TO_INDEX (id);
1240 if (idx >= ctx->stage->used)
1241 MERROR (MERROR_DRAW, -1);
1242 cmd = ctx->stage->cmds + idx;
1243 if (cmd->type == FontLayoutCmdTypeRule)
1244 to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1245 else if (cmd->type == FontLayoutCmdTypeCond)
1246 to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1247 else if (cmd->type == FontLayoutCmdTypeOTF)
1248 to = run_otf (depth, cmd->body.otf, gstring, from, to, ctx);
1255 if (id <= CMD_ID_OFFSET_COMBINING)
1257 ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1258 if (MDEBUG_FLAG () > 2)
1259 MDEBUG_PRINT3 ("\n [FLT] %*s(CMB %s)", depth, "",
1260 dump_combining_code (ctx->combining_code));
1270 g = *(MGLYPH (from));
1271 if (ctx->combining_code)
1272 g.combining_code = ctx->combining_code;
1273 if (ctx->left_padding)
1274 g.left_padding = ctx->left_padding;
1275 APPEND_GLYPH (gstring, g);
1276 UPDATE_CLUSTER_RANGE (ctx, g);
1277 if (MDEBUG_FLAG () > 2)
1279 if (g.type == GLYPH_PAD)
1280 MDEBUG_PRINT2 ("\n [FLT] %*s(COPY |)", depth, "");
1282 MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g.code);
1284 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1288 case CMD_ID_CLUSTER_BEGIN:
1289 if (! ctx->cluster_begin_idx)
1291 if (MDEBUG_FLAG () > 2)
1292 MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "", MGLYPH (from)->pos);
1293 ctx->cluster_begin_idx = gstring->used;
1294 ctx->cluster_begin_pos = MGLYPH (from)->pos;
1295 ctx->cluster_end_pos = MGLYPH (from)->to;
1299 case CMD_ID_CLUSTER_END:
1300 if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1304 if (MDEBUG_FLAG () > 2)
1305 MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1306 for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1308 MGLYPH (i)->pos = ctx->cluster_begin_pos;
1309 MGLYPH (i)->to = ctx->cluster_end_pos;
1311 ctx->cluster_begin_idx = 0;
1315 case CMD_ID_SEPARATOR:
1318 g = *(MGLYPH (from));
1320 g = *(MGLYPH (from - 1));
1322 /* g.c = g.code = 0; */
1324 APPEND_GLYPH (gstring, g);
1328 case CMD_ID_LEFT_PADDING:
1329 if (MDEBUG_FLAG () > 2)
1330 MDEBUG_PRINT2 ("\n [FLT] %*s[", depth, "");
1331 ctx->left_padding = 1;
1334 case CMD_ID_RIGHT_PADDING:
1335 if (gstring->used > 0)
1337 if (MDEBUG_FLAG () > 2)
1338 MDEBUG_PRINT2 ("\n [FLT] %*s]", depth, "");
1339 gstring->glyphs[gstring->used - 1].right_padding = 1;
1344 MERROR (MERROR_DRAW, -1);
1351 mfont__flt_init (void)
1353 Mcond = msymbol ("cond");
1354 Mrange = msymbol ("range");
1355 Mlayouter = msymbol ("layouter");
1356 Mexist = msymbol ("exist");
1357 flt_list = mplist ();
1362 mfont__flt_fini (void)
1366 MPLIST_DO (plist, flt_list)
1368 pl = MPLIST_PLIST (plist);
1371 MPLIST_DO (pl, MPLIST_NEXT (pl))
1372 free_flt_stage (MPLIST_VAL (pl));
1373 pl = MPLIST_PLIST (plist);
1374 M17N_OBJECT_UNREF (pl);
1377 M17N_OBJECT_UNREF (flt_list);
1381 mfont__flt_encode_char (MSymbol layouter_name, int c)
1383 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1388 return MCHAR_INVALID_CODE;
1389 table = MPLIST_VAL (layouter);
1390 code = (unsigned) mchartable_lookup (table, c);
1391 return (code ? code : MCHAR_INVALID_CODE);
1395 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1400 FontLayoutContext ctx;
1403 int match_indices[NMATCH];
1404 MSymbol layouter_name = rface->layouter;
1405 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1406 MRealizedFace *ascii_rface = rface->ascii_rface;
1407 FontLayoutStage *stage;
1408 int from_pos, to_pos;
1413 /* FLT not found. Make all glyphs invisible. */
1415 gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1419 dummy = gstring->glyphs[from];
1420 MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1423 memset (&ctx, 0, sizeof ctx);
1424 table = MPLIST_VAL (layouter);
1425 layouter = MPLIST_NEXT (layouter);
1426 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1428 /* Find previous glyphs that are also supported by the layouter. */
1430 && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1432 /* + 2 is for a separator ' ' and a terminator '\0'. */
1433 encoded_len = gstring->used - gidx + 2;
1434 ctx.encoded = (char *) alloca (encoded_len);
1436 for (i = 0; gidx < from; i++, gidx++)
1437 ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1439 ctx.encoded[i++] = ' ';
1440 ctx.encoded_offset = from - i;
1442 /* Now each MGlyph->code contains encoded char. Set it in
1443 ctx.encoded[], and set MGlyph->c to MGlyph->code. */
1444 for (gidx = from; gidx < to ; i++, gidx++)
1446 ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1447 MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1449 ctx.encoded[i++] = '\0';
1451 match_indices[0] = from;
1452 match_indices[1] = to;
1453 for (i = 2; i < NMATCH; i++)
1454 match_indices[i] = -1;
1455 ctx.match_indices = match_indices;
1457 from_pos = MGLYPH (from)->pos;
1458 to_pos = MGLYPH (to)->pos;
1462 MDEBUG_PRINT ("\n [FLT] (SOURCE");
1463 for (i = from; i < to; i++)
1465 if (i > from && (i - from) % 8 == 0)
1466 MDEBUG_PRINT ("\n [FLT] ");
1467 MDEBUG_PRINT1 (" %04X", MGLYPH (i)->c);
1472 for (stage_idx = 0; 1; stage_idx++)
1474 int len = to - from;
1477 ctx.code_offset = ctx.combining_code = ctx.left_padding = 0;
1478 if (MDEBUG_FLAG () > 2)
1480 MDEBUG_PRINT2 ("\n [FLT] (STAGE %d \"%s\"", stage_idx,
1481 ctx.encoded + from - ctx.encoded_offset);
1482 fprintf (stderr, " (");
1483 for (i = from; i < to ; i++)
1485 if (gstring->glyphs[i].type == GLYPH_PAD)
1486 fprintf (stderr, "%*s|", (i > from), "");
1488 fprintf (stderr, "%*s%04X", (i > from), "",
1489 gstring->glyphs[i].code);
1491 fprintf (stderr, ")");
1494 gidx = gstring->used;
1497 result = run_command (4, INDEX_TO_CMD_ID (0), gstring,
1498 ctx.encoded_offset, to, &ctx);
1499 if (MDEBUG_FLAG () > 2)
1503 to = from + (gstring->used - gidx);
1504 REPLACE_GLYPHS (gstring, gidx, from, len);
1506 layouter = MPLIST_NEXT (layouter);
1507 /* If this is the last stage, break the loop. */
1508 if (MPLIST_TAIL_P (layouter))
1511 /* Otherwise, prepare for the next iteration. */
1512 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1513 table = stage->category;
1514 if (to - from >= encoded_len)
1516 encoded_len = to + 1;
1517 ctx.encoded = (char *) alloca (encoded_len);
1520 for (i = from; i < to; i++)
1522 MGlyph *g = MGLYPH (i);
1524 if (g->type == GLYPH_PAD)
1525 ctx.encoded[i - from] = ' ';
1526 else if (! g->otf_encoded)
1527 ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1528 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1531 int c = mfont__ft_decode_otf (g);
1535 c = (int) mchartable_lookup (table, c);
1539 ctx.encoded[i - from] = (c >= 0 ? c : 1);
1541 #endif /* HAVE_FREETYPE && HAVE_OTF */
1543 ctx.encoded[i - from] = '\0';
1544 ctx.encoded_offset = from;
1545 ctx.match_indices[0] = from;
1546 ctx.match_indices[1] = to;
1551 /* Somehow there's no glyph contributing to characters between
1552 FROM_POS and TO_POS. We must add one dummy space glyph for
1553 those characters. */
1556 g.type = GLYPH_SPACE;
1557 g.c = ' ', g.code = ' ';
1558 g.pos = from_pos, g.to = to_pos;
1559 g.rface = ascii_rface;
1560 INSERT_GLYPH (gstring, from, g);
1565 /* Get actual glyph IDs of glyphs. Also check if all characters
1566 in the range is covered by some glyph(s). If not, change
1567 <pos> and <to> of glyphs to cover uncovered characters. */
1568 int len = to_pos - from_pos;
1570 MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1571 MGlyph *g, *gend = MGLYPH (to);
1572 MGlyph *latest = gend;
1574 for (g = MGLYPH (from); g != gend; g++)
1575 if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1576 g->code = ((rface->rfont->driver->encode_char)
1577 (NULL, (MFont *) rface->rfont, NULL, g->code));
1578 for (i = 0; i < len; i++)
1580 for (g = MGLYPH (from); g != gend; g++)
1582 if (g->pos < latest->pos)
1584 if (! glyphs[g->pos - from_pos])
1586 for (i = g->pos; i < g->to; i++)
1587 glyphs[i - from_pos] = g;
1594 for (g = latest; g->pos == pos; g++)
1598 for (; i < len; i++)
1602 for (g = latest; g->pos == latest->pos; g++)
1603 g->to = from_pos + i + 1;
1611 MDEBUG_PRINT ("\n [FLT] (RESULT");
1612 for (i = from; i < to;)
1614 if (MGLYPH (i)->otf_encoded)
1620 while (i < to && ! MGLYPH (i)->otf_encoded) i++;
1621 mfont__get_metric (gstring, j, i);
1624 if (MDEBUG_FLAG () > 1)
1625 for (i = 0; from < to; from++, i++)
1628 int width, xoff, yoff;
1630 if (i > 0 && i % 4 == 0)
1631 MDEBUG_PRINT ("\n [FLT] ");
1634 width = g->width, xoff = g->xoff, yoff = g->yoff;
1635 if (g->right_padding && g->rbearing > g->width)
1636 width = g->rbearing;
1637 if (g->left_padding && g->lbearing < 0)
1639 width += - g->lbearing;
1640 xoff += - g->lbearing;
1642 MDEBUG_PRINT4 (" (%04X %d %d %d)", g->code, width, xoff, yoff);
1645 for (; from < to; from++)
1647 MGLYPH (from)->otf_encoded = 1;
1648 MDEBUG_PRINT1 (" %04X", MGLYPH (from)->code);
1650 MDEBUG_PRINT ("))\n");
1657 /* for debugging... */
1660 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1662 char *prefix = (char *) alloca (indent + 1);
1664 memset (prefix, 32, indent);
1668 fprintf (stderr, "0x%02X", id);
1669 else if (id <= CMD_ID_OFFSET_INDEX)
1671 int idx = CMD_ID_TO_INDEX (id);
1672 FontLayoutCmd *cmd = stage->cmds + idx;
1674 if (cmd->type == FontLayoutCmdTypeRule)
1676 FontLayoutCmdRule *rule = &cmd->body.rule;
1679 fprintf (stderr, "(rule ");
1680 if (rule->src_type == SRC_REGEX)
1681 fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1682 else if (rule->src_type == SRC_INDEX)
1683 fprintf (stderr, "%d", rule->src.match_idx);
1684 else if (rule->src_type == SRC_SEQ)
1685 fprintf (stderr, "(seq)");
1686 else if (rule->src_type == SRC_RANGE)
1687 fprintf (stderr, "(range)");
1689 fprintf (stderr, "(invalid src)");
1691 for (i = 0; i < rule->n_cmds; i++)
1693 fprintf (stderr, "\n%s ", prefix);
1694 dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1696 fprintf (stderr, ")");
1698 else if (cmd->type == FontLayoutCmdTypeCond)
1700 FontLayoutCmdCond *cond = &cmd->body.cond;
1703 fprintf (stderr, "(cond");
1704 for (i = 0; i < cond->n_cmds; i++)
1706 fprintf (stderr, "\n%s ", prefix);
1707 dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1709 fprintf (stderr, ")");
1711 else if (cmd->type == FontLayoutCmdTypeOTF)
1713 fprintf (stderr, "(otf)");
1716 fprintf (stderr, "(error-command)");
1718 else if (id <= CMD_ID_OFFSET_COMBINING)
1719 fprintf (stderr, "cominging-code");
1721 fprintf (stderr, "(predefiend %d)", id);
1725 dump_flt (MFontLayoutTable *flt, int indent)
1727 char *prefix = (char *) alloca (indent + 1);
1731 memset (prefix, 32, indent);
1733 fprintf (stderr, "(flt");
1734 MPLIST_DO (plist, flt)
1736 FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1739 fprintf (stderr, "\n%s (stage %d", prefix, stage_idx);
1740 for (i = 0; i < stage->used; i++)
1742 fprintf (stderr, "\n%s ", prefix);
1743 dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1745 fprintf (stderr, ")");
1748 fprintf (stderr, ")");