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., 59 Temple Place, Suite 330, Boston, MA
29 #include <sys/types.h>
33 #include "m17n-misc.h"
38 #include "internal-gui.h"
44 /* Font Layout Table (FLT)
46 Predefined terms: SYMBOL, INTEGER, STRING
48 FLT ::= '(' STAGE + ')'
50 STAGE ::= CATEGORY-TABLE ? FONT-LAYOUT-RULE
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.
59 '(' 'category' CATEGORY-SPEC + ')'
61 '(' CODE [ CODE ] CATEGORY ')'
64 ;; ASCII character codes of alphabet ('A' .. 'Z' 'a' .. 'z').
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.
73 '(' 'generator' RULE MACRO-DEF * ')'
75 RULE ::= COMMAND | REGEXP-RULE | MATCH-RULE | MAP-RULE
76 | COND-STRUCT | MACRO-NAME
79 DIRECT-CODE | COMBINING | PREDEFIND-COMMAND | OTF-COMMAND
81 DIRECT-CODE ::= INTEGER
82 ;; Always succeed. Produce the code. Consume no source.
85 '=' | '*' | '<' | '>' | '|'
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.
90 ;; '*': If the the previous command succeeded, repeat it until it
93 ;; '<': Produce a special code that indicates the start of grapheme
94 ;; cluster. Succeed always, consume nothing.
96 ;; '>': Produce a special code that indicates the end of grapheme
97 ;; cluster. Succeed always, consume nothing.
99 ;; '|': Produce a special code whose category is ' '. Succeed always,
103 'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]]
104 ;; Run the Open Type Layout Table on the current run. Succeed always,
108 ;; OTF's ScriptTag name (four letters) listed at:
109 ;; <http://www.microsoft.om/typograph/otspec/scripttags.htm>
111 ;; OTF's Language System name (four letters) listed at:
112 ;; <http://www.microsoft.om/typograph/otspec/languagetags.htm>
114 GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
115 GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
117 ;; OTF's Feature name (four letters) listed at:
118 ;; <http://www.microsoft.om/typograph/otspec/???.htm>
120 OTF-TAG ::= PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR
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.
128 ;; Run all GSUB features, run no GPOS features.
131 '(' REGEXP RULE * ')'
133 ;; Succeed if REGXP matches the head of source. Run RULEs while
134 ;; limiting the source to the matching part. Consume that part.
137 ;; Must be composed only from ASCII characters. 'A' - 'Z', 'a' - 'z'
138 ;; correspond to CATEGORY.
145 '(' MATCH-IDX RULE * ')'
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
152 MATCH-IDX ::= INTEGER
159 '(' ( SOURCE-SEQ | SOURCE-RANGE ) RULE * ')'
161 ;; Succeed if the source matches SOURCE-SEQ or SOURCE-RANGE. Run
162 ;; RULEs while limiting the source to the matching part. Consume that
168 '(' 'range' CODE CODE ')'
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)).
177 '(' 'cond' RULE + ')'
179 ;; Try each rule in sequence until one succeeds. Succeed if one
180 ;; succeeds. Consume nothing.
184 ;; ((0x0915 0x094D) 0x43)
185 ;; ((range 0x0F40 0x0F6A) 0x2221)
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
196 ;; VH pair indicates 12 reference points of a glyph as below:
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)
213 ;; Align top-left point of the previous glyph and bottom-center
214 ;; point of the current glyph.
216 ;; Align 20% left and 10% below of base-left point of the previous
217 ;; glyph and base-right point of the current glyph.
220 '(' MACRO-NAME RULE + ')'
221 MACRO-NAME ::= SYMBOL
225 static int mdebug_mask = MDEBUG_FONT_FLT;
229 static MPlist *flt_list;
234 -0x0F .. -2 : builtin commands
235 -0x100000F .. -0x10 : combining code
236 ... -0x1000010: index to FontLayoutStage->cmds
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
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 /* ']' */
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))
256 #define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id))
257 #define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx))
259 static MSymbol Mcond, Mrange;
261 #define GLYPH_CODE_P(code) \
262 ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX)
264 #define GLYPH_CODE_INDEX(code) ((code) - GLYPH_CODE_MIN)
266 enum FontLayoutCmdRuleSrcType
276 enum FontLayoutCmdRuleSrcType src_type;
298 /* Beginning and end indices of series of SEQ commands. */
299 int seq_beg, seq_end;
300 /* Range of the first character appears in the above series. */
301 int seq_from, seq_to;
311 MSymbol gsub_features;
312 MSymbol gpos_features;
315 enum FontLayoutCmdType
317 FontLayoutCmdTypeRule,
318 FontLayoutCmdTypeCond,
319 FontLayoutCmdTypeOTF,
325 enum FontLayoutCmdType type;
327 FontLayoutCmdRule rule;
328 FontLayoutCmdCond cond;
329 FontLayoutCmdOTF otf;
335 MCharTable *category;
340 typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */
342 /* Font layout table loader */
344 /* Load a category table from PLIST. PLIST has this form:
345 PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
349 load_category_table (MPlist *plist)
353 table = mchartable (Minteger, (void *) 0);
355 MPLIST_DO (plist, plist)
358 int from, to, category_code;
360 if (! MPLIST_PLIST (plist))
361 MERROR (MERROR_FONT, NULL);
362 elt = MPLIST_PLIST (plist);
363 if (! MPLIST_INTEGER_P (elt))
364 MERROR (MERROR_FONT, NULL);
365 from = MPLIST_INTEGER (elt);
366 elt = MPLIST_NEXT (elt);
367 if (! MPLIST_INTEGER_P (elt))
368 MERROR (MERROR_FONT, NULL);
369 to = MPLIST_INTEGER (elt);
370 elt = MPLIST_NEXT (elt);
371 if (MPLIST_TAIL_P (elt))
378 if (! MPLIST_INTEGER_P (elt))
379 MERROR (MERROR_FONT, NULL);
380 category_code = MPLIST_INTEGER (elt);
382 if (! isalpha (category_code))
383 MERROR (MERROR_FONT, NULL);
386 mchartable_set (table, from, (void *) category_code);
388 mchartable_set_range (table, from, to, (void *) category_code);
395 /* Parse OTF command name NAME and store the result in CMD.
397 :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
398 where GSUB-FEATURES and GPOS-FEATURES have this form:
399 [FEATURE[,FEATURE]*] | ' ' */
402 load_otf_command (FontLayoutCmd *cmd, char *name)
404 char *p = name, *beg;
406 cmd->type = FontLayoutCmdTypeOTF;
407 cmd->body.otf.script = cmd->body.otf.langsys = Mnil;
408 cmd->body.otf.gsub_features = cmd->body.otf.gpos_features = Mt;
414 for (beg = ++p; *p && *p != '/' && *p != '=' && *p != '+'; p++);
416 cmd->body.otf.script = msymbol__with_len (beg, p - beg);
420 for (beg = ++p; *p && *p != '=' && *p != '+'; p++);
422 cmd->body.otf.langsys = msymbol__with_len (beg, p - beg);
426 for (beg = ++p; *p && *p != '+'; p++);
428 cmd->body.otf.gsub_features = msymbol__with_len (beg, p - beg);
430 cmd->body.otf.gsub_features = Mnil;
434 for (beg = ++p; *p && *p != '+'; p++);
436 cmd->body.otf.gpos_features = msymbol__with_len (beg, p - beg);
438 cmd->body.otf.gpos_features = Mnil;
444 return (cmd->body.otf.script == Mnil ? -1 : 0);
448 /* Read a decimal number from STR preceded by one of "+-><". '+' and
449 '>' means a plus sign, '-' and '<' means a minus sign. If the
450 number is greater than 127, limit it to 127. */
453 read_decimal_number (char **str)
456 int sign = (*p == '-' || *p == '<') ? -1 : 1;
460 while (*p >= '0' && *p <= '9')
461 n = n * 10 + *p++ - '0';
465 return (n < 127 ? n * sign : 127 * sign);
469 /* Read a horizontal and vertical combining positions from STR, and
470 store them in the place pointed by X and Y. The horizontal
471 position left, center, and right are represented by 0, 1, and 2
472 respectively. The vertical position top, center, bottom, and base
473 are represented by 0, 1, 2, and 3 respectively. If successfully
474 read, return 0, else return -1. */
477 read_combining_position (char *str, int *x, int *y)
482 /* Vertical position comes first. */
483 for (i = 0; i < 4; i++)
492 /* Then comse horizontal position. */
493 for (i = 0; i < 3; i++)
503 /* Return a combining code corresponding to SYM. */
506 get_combining_command (MSymbol sym)
508 char *str = msymbol_name (sym);
509 int base_x, base_y, add_x, add_y, off_x, off_y;
512 if (read_combining_position (str, &base_x, &base_y) < 0)
523 if (c == '+' || c == '-')
525 off_y = read_decimal_number (&str) + 128;
530 if (c == '<' || c == '>')
531 off_x = read_decimal_number (&str) + 128;
535 if (read_combining_position (str, &add_x, &add_y) < 0)
538 c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
539 return (COMBINING_CODE_TO_CMD_ID (c));
543 /* Load a command from PLIST into STAGE, and return that
544 identification number. If ID is not INVALID_CMD_ID, that means we
545 are loading a top level command or a macro. In that case, use ID
546 as the identification number of the command. Otherwise, generate a
547 new id number for the command. MACROS is a list of raw macros. */
550 load_command (FontLayoutStage *stage, MPlist *plist,
551 MPlist *macros, int id)
555 if (MPLIST_INTEGER_P (plist))
557 int code = MPLIST_INTEGER (plist);
560 MERROR (MERROR_DRAW, INVALID_CMD_ID);
563 else if (MPLIST_PLIST_P (plist))
565 /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
566 | ( ( INTEGER INTEGER ) ... )
567 | ( ( range INTEGER INTEGER ) ... ) */
568 MPlist *elt = MPLIST_PLIST (plist);
569 int len = MPLIST_LENGTH (elt) - 1;
572 if (id == INVALID_CMD_ID)
575 id = INDEX_TO_CMD_ID (stage->used);
576 MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
578 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
580 if (MPLIST_SYMBOL_P (elt))
582 FontLayoutCmdCond *cond;
584 if (MPLIST_SYMBOL (elt) != Mcond)
585 MERROR (MERROR_DRAW, INVALID_CMD_ID);
586 elt = MPLIST_NEXT (elt);
587 cmd->type = FontLayoutCmdTypeCond;
588 cond = &cmd->body.cond;
589 cond->seq_beg = cond->seq_end = -1;
590 cond->seq_from = cond->seq_to = 0;
592 MTABLE_CALLOC (cond->cmd_ids, len, MERROR_DRAW);
593 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
595 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
597 if (this_id == INVALID_CMD_ID)
598 MERROR (MERROR_DRAW, INVALID_CMD_ID);
599 /* The above load_command may relocate stage->cmds. */
600 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
601 cond = &cmd->body.cond;
602 cond->cmd_ids[i] = this_id;
603 if (this_id <= CMD_ID_OFFSET_INDEX)
605 FontLayoutCmd *this_cmd
606 = stage->cmds + CMD_ID_TO_INDEX (this_id);
608 if (this_cmd->type == FontLayoutCmdTypeRule
609 && this_cmd->body.rule.src_type == SRC_SEQ)
611 int first_char = this_cmd->body.rule.src.seq.codes[0];
613 if (cond->seq_beg < 0)
615 /* The first SEQ command. */
617 cond->seq_from = cond->seq_to = first_char;
619 else if (cond->seq_end < 0)
621 /* The following SEQ command. */
622 if (cond->seq_from > first_char)
623 cond->seq_from = first_char;
624 else if (cond->seq_to < first_char)
625 cond->seq_to = first_char;
630 if (cond->seq_beg >= 0 && cond->seq_end < 0)
631 /* The previous one is the last SEQ command. */
637 if (cond->seq_beg >= 0 && cond->seq_end < 0)
638 /* The previous one is the last SEQ command. */
642 if (cond->seq_beg >= 0 && cond->seq_end < 0)
643 /* The previous one is the last SEQ command. */
648 cmd->type = FontLayoutCmdTypeRule;
649 if (MPLIST_MTEXT_P (elt))
651 char *str = (char *) MTEXT_DATA (MPLIST_MTEXT (elt));
653 if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
654 MERROR (MERROR_FONT, INVALID_CMD_ID);
655 cmd->body.rule.src_type = SRC_REGEX;
656 cmd->body.rule.src.re.pattern = strdup (str);
658 else if (MPLIST_INTEGER_P (elt))
660 cmd->body.rule.src_type = SRC_INDEX;
661 cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
663 else if (MPLIST_PLIST_P (elt))
665 MPlist *pl = MPLIST_PLIST (elt);
666 int size = MPLIST_LENGTH (pl);
668 if (MPLIST_INTEGER_P (pl))
672 cmd->body.rule.src_type = SRC_SEQ;
673 cmd->body.rule.src.seq.n_codes = size;
674 MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
676 for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
678 if (! MPLIST_INTEGER_P (pl))
679 MERROR (MERROR_DRAW, INVALID_CMD_ID);
680 cmd->body.rule.src.seq.codes[i]
681 = (unsigned) MPLIST_INTEGER (pl);
684 else if (MPLIST_SYMBOL_P (pl) && size == 3)
686 cmd->body.rule.src_type = SRC_RANGE;
687 pl = MPLIST_NEXT (pl);
688 if (! MPLIST_INTEGER_P (pl))
689 MERROR (MERROR_DRAW, INVALID_CMD_ID);
690 cmd->body.rule.src.range.from
691 = (unsigned) MPLIST_INTEGER (pl);
692 pl = MPLIST_NEXT (pl);
693 if (! MPLIST_INTEGER_P (pl))
694 MERROR (MERROR_DRAW, INVALID_CMD_ID);
695 cmd->body.rule.src.range.to
696 = (unsigned) MPLIST_INTEGER (pl);
699 MERROR (MERROR_DRAW, INVALID_CMD_ID);
702 MERROR (MERROR_DRAW, INVALID_CMD_ID);
704 elt = MPLIST_NEXT (elt);
705 cmd->body.rule.n_cmds = len;
706 MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
707 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
709 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
711 if (this_id == INVALID_CMD_ID)
712 MERROR (MERROR_DRAW, INVALID_CMD_ID);
713 /* The above load_command may relocate stage->cmds. */
714 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
715 cmd->body.rule.cmd_ids[i] = this_id;
719 else if (MPLIST_SYMBOL_P (plist))
722 MSymbol sym = MPLIST_SYMBOL (plist);
723 char *name = msymbol_name (sym);
724 int len = strlen (name);
728 && ! strncmp (name, "otf:", 4)
729 && load_otf_command (&cmd, name + 3) >= 0)
731 if (id == INVALID_CMD_ID)
733 id = INDEX_TO_CMD_ID (stage->used);
734 MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
737 stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
745 else if (*name == '*')
746 return CMD_ID_REPEAT;
747 else if (*name == '<')
748 return CMD_ID_CLUSTER_BEGIN;
749 else if (*name == '>')
750 return CMD_ID_CLUSTER_END;
751 else if (*name == '|')
752 return CMD_ID_SEPARATOR;
753 else if (*name == '[')
754 return CMD_ID_LEFT_PADDING;
755 else if (*name == ']')
756 return CMD_ID_RIGHT_PADDING;
762 id = get_combining_command (sym);
768 MPLIST_DO (elt, macros)
770 if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
772 id = INDEX_TO_CMD_ID (i);
773 if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
774 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
780 MERROR (MERROR_DRAW, INVALID_CMD_ID);
783 MERROR (MERROR_DRAW, INVALID_CMD_ID);
789 free_flt_command (FontLayoutCmd *cmd)
791 if (cmd->type == FontLayoutCmdTypeRule)
793 FontLayoutCmdRule *rule = &cmd->body.rule;
795 if (rule->src_type == SRC_REGEX)
797 free (rule->src.re.pattern);
798 regfree (&rule->src.re.preg);
800 else if (rule->src_type == SRC_SEQ)
801 free (rule->src.seq.codes);
802 free (rule->cmd_ids);
804 else if (cmd->type == FontLayoutCmdTypeCond)
805 free (cmd->body.cond.cmd_ids);
808 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
809 and return it. PLIST has this form:
810 PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
813 static FontLayoutStage *
814 load_generator (MPlist *plist)
816 FontLayoutStage *stage;
820 MSTRUCT_CALLOC (stage, MERROR_DRAW);
821 MLIST_INIT1 (stage, cmds, 32);
822 dummy.type = FontLayoutCmdTypeMAX;
823 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
824 MPLIST_DO (elt, MPLIST_NEXT (plist))
826 if (! MPLIST_PLIST_P (elt))
827 MERROR (MERROR_FONT, NULL);
828 pl = MPLIST_PLIST (elt);
829 if (! MPLIST_SYMBOL_P (pl))
830 MERROR (MERROR_FONT, NULL);
831 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
834 /* Load the first command from PLIST into STAGE->cmds[0]. Macros
835 called in the first command are also loaded from MPLIST_NEXT
836 (PLIST) into STAGE->cmds[n]. */
837 if (load_command (stage, plist, MPLIST_NEXT (plist), INDEX_TO_CMD_ID (0))
840 MLIST_FREE1 (stage, cmds);
842 MERROR (MERROR_DRAW, NULL);
849 /* Load FLT of name LAYOUTER_NAME from the m17n database into a newly
850 allocated memory, and return it. */
852 static MFontLayoutTable *
853 load_flt (MSymbol layouter_name)
856 MPlist *top = NULL, *plist;
857 MSymbol Mcategory = msymbol ("category");
858 MSymbol Mgenerator = msymbol ("generator");
859 MFontLayoutTable *layouter = NULL;
860 MCharTable *category = NULL;
862 if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
863 MERROR_GOTO (MERROR_FONT, finish);
864 if (! (top = (MPlist *) mdatabase_load (mdb)))
865 MERROR_GOTO (0, finish);
866 if (! MPLIST_PLIST_P (top))
867 MERROR_GOTO (MERROR_FONT, finish);
869 MPLIST_DO (plist, top)
874 if (! MPLIST_PLIST (plist))
875 MERROR_GOTO (MERROR_FONT, finish);
876 elt = MPLIST_PLIST (plist);
877 if (! MPLIST_SYMBOL_P (elt))
878 MERROR_GOTO (MERROR_FONT, finish);
879 sym = MPLIST_SYMBOL (elt);
880 elt = MPLIST_NEXT (elt);
882 MERROR_GOTO (MERROR_FONT, finish);
883 if (sym == Mcategory)
886 M17N_OBJECT_UNREF (category);
887 category = load_category_table (elt);
889 else if (sym == Mgenerator)
891 FontLayoutStage *stage;
894 MERROR_GOTO (MERROR_FONT, finish);
895 stage = load_generator (elt);
897 MERROR_GOTO (MERROR_FONT, finish);
898 stage->category = category;
899 M17N_OBJECT_REF (category);
902 layouter = mplist ();
903 /* Here don't do M17N_OBJECT_REF (category) because we
904 don't unref the value of the element added below. */
905 mplist_add (layouter, Mcategory, category);
907 mplist_add (layouter, Mt, stage);
910 MERROR_GOTO (MERROR_FONT, finish);
914 M17N_OBJECT_UNREF (category);
917 M17N_OBJECT_UNREF (top);
918 mplist_add (flt_list, layouter_name, layouter);
924 free_flt_stage (FontLayoutStage *stage)
928 M17N_OBJECT_UNREF (stage->category);
929 for (i = 0; i < stage->used; i++)
930 free_flt_command (stage->cmds + i);
931 MLIST_FREE1 (stage, cmds);
936 static MFontLayoutTable *
937 get_font_layout_table (MSymbol layouter_name)
939 MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
941 return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
945 /* FLS (Font Layout Service) */
947 /* Structure to hold information about a context of FLS. */
951 /* Pointer to the current stage. */
952 FontLayoutStage *stage;
954 /* Encode each MGlyph->code by the current category table into this
955 array. An element is a category. */
957 /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
962 int cluster_begin_idx;
963 int cluster_begin_pos;
967 MRealizedFont *rfont;
970 static int run_command (int depth,
971 int, MGlyphString *, int, int, FontLayoutContext *);
977 FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
978 FontLayoutContext *ctx)
980 int *saved_match_indices = ctx->match_indices;
981 int match_indices[NMATCH * 2];
984 int orig_from = from;
986 if (ctx->cluster_begin_idx)
988 if (ctx->cluster_begin_pos > MGLYPH (from)->pos)
989 ctx->cluster_begin_pos = MGLYPH (from)->pos;
990 if (ctx->cluster_end_pos < MGLYPH (to)->pos)
991 ctx->cluster_end_pos = MGLYPH (to)->pos;
994 if (rule->src_type == SRC_SEQ)
998 len = rule->src.seq.n_codes;
999 if (len > (to - from))
1001 for (i = 0; i < len; i++)
1002 if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
1007 MDEBUG_PRINT1 (" (SEQ 0x%X", rule->src.seq.codes[0]);
1009 else if (rule->src_type == SRC_RANGE)
1015 head = gstring->glyphs[from].code;
1016 if (head < rule->src.range.from || head > rule->src.range.to)
1018 ctx->code_offset = head - rule->src.range.from;
1020 MDEBUG_PRINT2 (" (RANGE 0x%X-0x%X",
1021 rule->src.range.from, rule->src.range.to);
1023 else if (rule->src_type == SRC_REGEX)
1025 regmatch_t pmatch[NMATCH];
1031 saved_code = ctx->encoded[to - ctx->encoded_offset];
1032 ctx->encoded[to - ctx->encoded_offset] = '\0';
1033 result = regexec (&(rule->src.re.preg),
1034 ctx->encoded + from - ctx->encoded_offset,
1036 if (result == 0 && pmatch[0].rm_so == 0)
1038 MDEBUG_PRINT3 (" (REGEX \"%s\" \"%s\" %d",
1039 rule->src.re.pattern,
1040 ctx->encoded + from - ctx->encoded_offset,
1042 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1043 for (i = 0; i < NMATCH; i++)
1045 if (pmatch[i].rm_so < 0)
1046 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1049 match_indices[i * 2] = from + pmatch[i].rm_so;
1050 match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1053 ctx->match_indices = match_indices;
1054 to = match_indices[1];
1058 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1062 else if (rule->src_type == SRC_INDEX)
1064 if (rule->src.match_idx >= NMATCH)
1066 from = ctx->match_indices[rule->src.match_idx * 2];
1069 to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1070 MDEBUG_PRINT1 (" (INDEX %d", rule->src.match_idx);
1075 for (i = 0; i < rule->n_cmds; i++)
1079 if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1085 pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1087 MERROR (MERROR_DRAW, -1);
1088 consumed = pos > from;
1093 ctx->match_indices = saved_match_indices;
1095 return (rule->src_type == SRC_INDEX ? orig_from : to);
1099 run_cond (int depth,
1100 FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1101 FontLayoutContext *ctx)
1105 MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1107 for (i = 0; i < cond->n_cmds; i++)
1109 /* TODO: Write a code for optimization utilizaing the info
1111 if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1116 MERROR (MERROR_DRAW, -1);
1123 FontLayoutCmdOTF *otf_cmd, MGlyphString *gstring, int from, int to,
1124 FontLayoutContext *ctx)
1127 int gidx = gstring->used;
1129 to = mfont__ft_drive_otf (gstring, from, to, ctx->rfont,
1130 otf_cmd->script, otf_cmd->langsys,
1131 otf_cmd->gsub_features, otf_cmd->gpos_features);
1132 if (gidx < gstring->used)
1133 MGLYPH (gidx)->left_padding = ctx->left_padding;
1139 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1140 FontLayoutContext *ctx)
1148 /* Direct code (== id + ctx->code_offset) output.
1149 The source is not consumed. */
1151 g = *(MGLYPH (from));
1153 g = *(MGLYPH (from - 1));
1154 g.type = GLYPH_CHAR;
1155 g.code = ctx->code_offset + id;
1156 MDEBUG_PRINT1 (" (DIRECT 0x%X", g.code);
1157 if (ctx->combining_code)
1158 g.combining_code = ctx->combining_code;
1159 if (ctx->left_padding)
1160 g.left_padding = ctx->left_padding;
1161 for (i = from; i < to; i++)
1163 MGlyph *tmp = MGLYPH (i);
1165 if (g.pos > tmp->pos)
1167 else if (g.to < tmp->to)
1170 APPEND_GLYPH (gstring, g);
1171 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1176 if (id <= CMD_ID_OFFSET_INDEX)
1178 int idx = CMD_ID_TO_INDEX (id);
1181 if (idx >= ctx->stage->used)
1182 MERROR (MERROR_DRAW, -1);
1183 cmd = ctx->stage->cmds + idx;
1184 if (cmd->type == FontLayoutCmdTypeRule)
1185 to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1186 else if (cmd->type == FontLayoutCmdTypeCond)
1187 to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1188 else if (cmd->type == FontLayoutCmdTypeOTF)
1189 to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1196 if (id <= CMD_ID_OFFSET_COMBINING)
1198 ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1208 g = *(MGLYPH (from));
1209 if (ctx->combining_code)
1210 g.combining_code = ctx->combining_code;
1211 if (ctx->left_padding)
1212 g.left_padding = ctx->left_padding;
1213 APPEND_GLYPH (gstring, g);
1214 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1218 case CMD_ID_CLUSTER_BEGIN:
1219 if (! ctx->cluster_begin_idx)
1221 MDEBUG_PRINT1 (" <%d", MGLYPH (from)->pos);
1222 ctx->cluster_begin_idx = gstring->used;
1223 ctx->cluster_begin_pos = MGLYPH (from)->pos;
1224 ctx->cluster_end_pos = MGLYPH (from)->to;
1228 case CMD_ID_CLUSTER_END:
1229 if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1233 MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1234 for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1236 MGLYPH (i)->pos = ctx->cluster_begin_pos;
1237 MGLYPH (i)->to = ctx->cluster_end_pos;
1239 ctx->cluster_begin_idx = 0;
1243 case CMD_ID_SEPARATOR:
1246 g = *(MGLYPH (from));
1248 g = *(MGLYPH (from - 1));
1250 /* g.c = g.code = 0; */
1252 APPEND_GLYPH (gstring, g);
1256 case CMD_ID_LEFT_PADDING:
1257 ctx->left_padding = 1;
1260 case CMD_ID_RIGHT_PADDING:
1261 if (gstring->used > 0)
1262 gstring->glyphs[gstring->used - 1].right_padding = 1;
1266 MERROR (MERROR_DRAW, -1);
1273 mfont__flt_init (void)
1275 Mcond = msymbol ("cond");
1276 Mrange = msymbol ("range");
1277 Mlayouter = msymbol ("layouter");
1278 flt_list = mplist ();
1283 mfont__flt_fini (void)
1287 MPLIST_DO (plist, flt_list)
1289 pl = MPLIST_PLIST (plist);
1292 MPLIST_DO (pl, MPLIST_NEXT (pl))
1293 free_flt_stage (MPLIST_VAL (pl));
1294 pl = MPLIST_PLIST (plist);
1295 M17N_OBJECT_UNREF (pl);
1298 M17N_OBJECT_UNREF (flt_list);
1302 mfont__flt_encode_char (MSymbol layouter_name, int c)
1304 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1309 return MCHAR_INVALID_CODE;
1310 table = MPLIST_VAL (layouter);
1311 code = (unsigned) mchartable_lookup (table, c);
1312 return (code ? code : MCHAR_INVALID_CODE);
1316 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1321 FontLayoutContext ctx;
1324 int match_indices[NMATCH];
1325 MSymbol layouter_name = rface->rfont->layouter;
1326 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1327 MRealizedFace *ascii_rface = rface->ascii_rface;
1328 FontLayoutStage *stage;
1329 int from_pos, to_pos;
1334 /* FLT not found. Make all glyphs invisible. */
1336 gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1340 dummy = gstring->glyphs[from];
1341 MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1344 memset (&ctx, 0, sizeof ctx);
1345 ctx.rfont = rface->rfont;
1346 table = MPLIST_VAL (layouter);
1347 layouter = MPLIST_NEXT (layouter);
1348 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1350 /* Find previous glyphs that are also supported by the layouter. */
1352 && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1354 /* + 2 is for a separator ' ' and a terminator '\0'. */
1355 encoded_len = gstring->used - gidx + 2;
1356 ctx.encoded = (char *) alloca (encoded_len);
1358 for (i = 0; gidx < from; i++, gidx++)
1359 ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1361 ctx.encoded[i++] = ' ';
1362 ctx.encoded_offset = from - i;
1364 /* Now each MGlyph->code contains encoded char. Set it in
1365 ctx.encoded[], and set MGlyph->c to MGlyph->code. */
1366 for (gidx = from; gidx < to ; i++, gidx++)
1368 ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1369 MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1371 ctx.encoded[i++] = '\0';
1373 match_indices[0] = from;
1374 match_indices[1] = to;
1375 for (i = 2; i < NMATCH; i++)
1376 match_indices[i] = -1;
1377 ctx.match_indices = match_indices;
1379 from_pos = MGLYPH (from)->pos;
1380 to_pos = MGLYPH (to)->pos;
1382 for (stage_idx = 0; 1; stage_idx++)
1384 int len = to - from;
1387 MDEBUG_PRINT1 ("\n [FLT] (STAGE %d", stage_idx);
1388 gidx = gstring->used;
1391 result = run_command (2, INDEX_TO_CMD_ID (0), gstring,
1392 ctx.encoded_offset, to, &ctx);
1396 to = from + (gstring->used - gidx);
1397 REPLACE_GLYPHS (gstring, gidx, from, len);
1399 layouter = MPLIST_NEXT (layouter);
1400 /* If this is the last stage, break the loop. */
1401 if (MPLIST_TAIL_P (layouter))
1404 /* Otherwise, prepare for the next iteration. */
1405 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1406 table = stage->category;
1407 if (to - from >= encoded_len)
1409 encoded_len = to + 1;
1410 ctx.encoded = (char *) alloca (encoded_len);
1413 for (i = from; i < to; i++)
1415 MGlyph *g = MGLYPH (i);
1417 if (g->type == GLYPH_PAD)
1418 ctx.encoded[i - from] = ' ';
1419 else if (! g->otf_encoded)
1420 ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1421 #ifdef HAVE_FREETYPE
1424 int c = mfont__ft_decode_otf (g);
1428 c = (int) mchartable_lookup (table, c);
1432 ctx.encoded[i - from] = (c >= 0 ? c : 1);
1434 #endif /* HAVE_FREETYPE */
1436 ctx.encoded[i - from] = '\0';
1437 ctx.encoded_offset = from;
1438 ctx.match_indices[0] = from;
1439 ctx.match_indices[1] = to;
1442 MDEBUG_PRINT (")\n");
1446 /* Somehow there's no glyph contributing to characters between
1447 FROM_POS and TO_POS. We must add one dummy space glyph for
1448 those characters. */
1451 g.type = GLYPH_SPACE;
1452 g.c = ' ', g.code = ' ';
1453 g.pos = from_pos, g.to = to_pos;
1454 g.rface = ascii_rface;
1455 INSERT_GLYPH (gstring, from, g);
1460 /* Get actual glyph IDs of glyphs. Also check if all characters
1461 in the range is covered by some glyph(s). If not, change
1462 <pos> and <to> of glyphs to cover uncovered characters. */
1463 int len = to_pos - from_pos;
1465 MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1466 MGlyph *g, *gend = MGLYPH (to);
1467 MGlyph *latest = gend;
1469 for (g = MGLYPH (from); g != gend; g++)
1470 if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1472 = (rface->rfont->driver->encode_char) (rface->rfont, g->code);
1473 for (i = 0; i < len; i++)
1475 for (g = MGLYPH (from); g != gend; g++)
1477 if (g->pos < latest->pos)
1479 if (! glyphs[g->pos - from_pos])
1481 for (i = g->pos; i < g->to; i++)
1482 glyphs[i - from_pos] = g;
1489 for (g = latest; g->pos == pos; g++)
1493 for (; i < len; i++)
1497 for (g = latest; g->pos == latest->pos; g++)
1498 g->to = from_pos + i + 1;
1508 /* for debugging... */
1511 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1513 char *prefix = (char *) alloca (indent + 1);
1515 memset (prefix, 32, indent);
1519 fprintf (stderr, "0x%02X", id);
1520 else if (id <= CMD_ID_OFFSET_INDEX)
1522 int idx = CMD_ID_TO_INDEX (id);
1523 FontLayoutCmd *cmd = stage->cmds + idx;
1525 if (cmd->type == FontLayoutCmdTypeRule)
1527 FontLayoutCmdRule *rule = &cmd->body.rule;
1530 fprintf (stderr, "(rule ");
1531 if (rule->src_type == SRC_REGEX)
1532 fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1533 else if (rule->src_type == SRC_INDEX)
1534 fprintf (stderr, "%d", rule->src.match_idx);
1535 else if (rule->src_type == SRC_SEQ)
1536 fprintf (stderr, "(seq)");
1537 else if (rule->src_type == SRC_RANGE)
1538 fprintf (stderr, "(range)");
1540 fprintf (stderr, "(invalid src)");
1542 for (i = 0; i < rule->n_cmds; i++)
1544 fprintf (stderr, "\n%s ", prefix);
1545 dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1547 fprintf (stderr, ")");
1549 else if (cmd->type == FontLayoutCmdTypeCond)
1551 FontLayoutCmdCond *cond = &cmd->body.cond;
1554 fprintf (stderr, "(cond");
1555 for (i = 0; i < cond->n_cmds; i++)
1557 fprintf (stderr, "\n%s ", prefix);
1558 dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1560 fprintf (stderr, ")");
1562 else if (cmd->type == FontLayoutCmdTypeOTF)
1564 fprintf (stderr, "(otf)");
1567 fprintf (stderr, "(error-command)");
1569 else if (id <= CMD_ID_OFFSET_COMBINING)
1570 fprintf (stderr, "cominging-code");
1572 fprintf (stderr, "(predefiend %d)", id);
1576 dump_flt (MFontLayoutTable *flt, int indent)
1578 char *prefix = (char *) alloca (indent + 1);
1582 memset (prefix, 32, indent);
1584 fprintf (stderr, "(flt");
1585 MPLIST_DO (plist, flt)
1587 FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1590 fprintf (stderr, "\n%s (stage %d", prefix, stage_idx);
1591 for (i = 0; i < stage->used; i++)
1593 fprintf (stderr, "\n%s ", prefix);
1594 dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1596 fprintf (stderr, ")");
1599 fprintf (stderr, ")");