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;
307 enum FontLayoutCmdType
309 FontLayoutCmdTypeRule,
310 FontLayoutCmdTypeCond,
311 FontLayoutCmdTypeOTF,
319 MSymbol gsub_features;
320 MSymbol gpos_features;
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 MSymbol Mend = msymbol ("end");
860 MFontLayoutTable *layouter = NULL;
861 MCharTable *category = NULL;
863 if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
864 MERROR_GOTO (MERROR_FONT, finish);
865 if (! (top = (MPlist *) mdatabase_load (mdb)))
866 MERROR_GOTO (0, finish);
867 if (! MPLIST_PLIST_P (top))
868 MERROR_GOTO (MERROR_FONT, finish);
870 MPLIST_DO (plist, top)
875 if (MPLIST_SYMBOL_P (plist)
876 && MPLIST_SYMBOL (plist) == Mend)
878 if (! MPLIST_PLIST (plist))
879 MERROR_GOTO (MERROR_FONT, finish);
880 elt = MPLIST_PLIST (plist);
881 if (! MPLIST_SYMBOL_P (elt))
882 MERROR_GOTO (MERROR_FONT, finish);
883 sym = MPLIST_SYMBOL (elt);
884 elt = MPLIST_NEXT (elt);
886 MERROR_GOTO (MERROR_FONT, finish);
887 if (sym == Mcategory)
890 M17N_OBJECT_UNREF (category);
891 category = load_category_table (elt);
893 else if (sym == Mgenerator)
895 FontLayoutStage *stage;
898 MERROR_GOTO (MERROR_FONT, finish);
899 stage = load_generator (elt);
901 MERROR_GOTO (MERROR_FONT, finish);
902 stage->category = category;
903 M17N_OBJECT_REF (category);
906 layouter = mplist ();
907 /* Here don't do M17N_OBJECT_REF (category) because we
908 don't unref the value of the element added below. */
909 mplist_add (layouter, Mcategory, category);
911 mplist_add (layouter, Mt, stage);
914 MERROR_GOTO (MERROR_FONT, finish);
918 M17N_OBJECT_UNREF (category);
921 M17N_OBJECT_UNREF (top);
922 mplist_add (flt_list, layouter_name, layouter);
928 free_flt_stage (FontLayoutStage *stage)
932 M17N_OBJECT_UNREF (stage->category);
933 for (i = 0; i < stage->used; i++)
934 free_flt_command (stage->cmds + i);
935 MLIST_FREE1 (stage, cmds);
940 static MFontLayoutTable *
941 get_font_layout_table (MSymbol layouter_name)
943 MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
945 return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
949 /* FLS (Font Layout Service) */
951 /* Structure to hold information about a context of FLS. */
955 /* Pointer to the current stage. */
956 FontLayoutStage *stage;
958 /* Encode each MGlyph->code by the current category table into this
959 array. An element is a category. */
961 /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
966 int cluster_begin_idx;
967 int cluster_begin_pos;
973 static int run_command (int depth,
974 int, MGlyphString *, int, int, FontLayoutContext *);
980 FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
981 FontLayoutContext *ctx)
983 int *saved_match_indices = ctx->match_indices;
984 int match_indices[NMATCH * 2];
987 int orig_from = from;
989 if (rule->src_type == SRC_SEQ)
993 len = rule->src.seq.n_codes;
994 if (len > (to - from))
996 for (i = 0; i < len; i++)
997 if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
1002 MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "",
1003 rule->src.seq.codes[0]);
1005 else if (rule->src_type == SRC_RANGE)
1011 head = gstring->glyphs[from].code;
1012 if (head < rule->src.range.from || head > rule->src.range.to)
1014 ctx->code_offset = head - rule->src.range.from;
1016 MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "",
1017 rule->src.range.from, rule->src.range.to);
1019 else if (rule->src_type == SRC_REGEX)
1021 regmatch_t pmatch[NMATCH];
1027 saved_code = ctx->encoded[to - ctx->encoded_offset];
1028 ctx->encoded[to - ctx->encoded_offset] = '\0';
1029 result = regexec (&(rule->src.re.preg),
1030 ctx->encoded + from - ctx->encoded_offset,
1032 if (result == 0 && pmatch[0].rm_so == 0)
1034 MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "",
1035 rule->src.re.pattern,
1036 ctx->encoded + from - ctx->encoded_offset,
1038 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1039 for (i = 0; i < NMATCH; i++)
1041 if (pmatch[i].rm_so < 0)
1042 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1045 match_indices[i * 2] = from + pmatch[i].rm_so;
1046 match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1049 ctx->match_indices = match_indices;
1050 to = match_indices[1];
1054 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1058 else if (rule->src_type == SRC_INDEX)
1060 if (rule->src.match_idx >= NMATCH)
1062 from = ctx->match_indices[rule->src.match_idx * 2];
1065 to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1066 MDEBUG_PRINT3 ("\n [FLT] %*s(INDEX %d", depth, "", rule->src.match_idx);
1071 for (i = 0; i < rule->n_cmds; i++)
1075 if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1081 if (ctx->cluster_begin_idx)
1083 if (ctx->cluster_begin_pos > MGLYPH (from)->pos)
1084 ctx->cluster_begin_pos = MGLYPH (from)->pos;
1085 if (ctx->cluster_end_pos < MGLYPH (to)->pos)
1086 ctx->cluster_end_pos = MGLYPH (to)->pos;
1088 pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1090 MERROR (MERROR_DRAW, -1);
1091 consumed = pos > from;
1096 ctx->match_indices = saved_match_indices;
1098 return (rule->src_type == SRC_INDEX ? orig_from : to);
1102 run_cond (int depth,
1103 FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1104 FontLayoutContext *ctx)
1108 MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1110 for (i = 0; i < cond->n_cmds; i++)
1112 /* TODO: Write a code for optimization utilizaing the info
1114 if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1119 MERROR (MERROR_DRAW, -1);
1126 FontLayoutCmdOTF *otf_cmd, MGlyphString *gstring, int from, int to,
1127 FontLayoutContext *ctx)
1130 to = mfont__ft_drive_otf (gstring, from, to,
1131 otf_cmd->script, otf_cmd->langsys,
1132 otf_cmd->gsub_features, otf_cmd->gpos_features);
1138 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1139 FontLayoutContext *ctx)
1147 /* Direct code (== id + ctx->code_offset) output.
1148 The source is not consumed. */
1150 g = *(MGLYPH (from));
1152 g = *(MGLYPH (from - 1));
1153 g.type = GLYPH_CHAR;
1154 g.code = ctx->code_offset + id;
1155 MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "", g.code);
1156 if (ctx->combining_code)
1157 g.combining_code = ctx->combining_code;
1158 if (ctx->left_padding)
1159 g.left_padding = ctx->left_padding;
1160 for (i = from; i < to; i++)
1162 MGlyph *tmp = MGLYPH (i);
1164 if (g.pos > tmp->pos)
1166 else if (g.to < tmp->to)
1169 APPEND_GLYPH (gstring, g);
1170 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1175 if (id <= CMD_ID_OFFSET_INDEX)
1177 int idx = CMD_ID_TO_INDEX (id);
1180 if (idx >= ctx->stage->used)
1181 MERROR (MERROR_DRAW, -1);
1182 cmd = ctx->stage->cmds + idx;
1183 if (cmd->type == FontLayoutCmdTypeRule)
1184 to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1185 else if (cmd->type == FontLayoutCmdTypeCond)
1186 to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1187 else if (cmd->type == FontLayoutCmdTypeOTF)
1188 to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1195 if (id <= CMD_ID_OFFSET_COMBINING)
1197 ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1207 g = *(MGLYPH (from));
1208 if (ctx->combining_code)
1209 g.combining_code = ctx->combining_code;
1210 if (ctx->left_padding)
1211 g.left_padding = ctx->left_padding;
1212 APPEND_GLYPH (gstring, g);
1213 MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g.code);
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_PRINT3 ("\n [FLT] %*s<%d", depth, "", 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 table = MPLIST_VAL (layouter);
1346 layouter = MPLIST_NEXT (layouter);
1347 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1349 /* Find previous glyphs that are also supported by the layouter. */
1351 && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1353 /* + 2 is for a separator ' ' and a terminator '\0'. */
1354 encoded_len = gstring->used - gidx + 2;
1355 ctx.encoded = (char *) alloca (encoded_len);
1357 for (i = 0; gidx < from; i++, gidx++)
1358 ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1360 ctx.encoded[i++] = ' ';
1361 ctx.encoded_offset = from - i;
1363 /* Now each MGlyph->code contains encoded char. Set it in
1364 ctx.encoded[], and set MGlyph->c to MGlyph->code. */
1365 for (gidx = from; gidx < to ; i++, gidx++)
1367 ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1368 MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1370 ctx.encoded[i++] = '\0';
1372 match_indices[0] = from;
1373 match_indices[1] = to;
1374 for (i = 2; i < NMATCH; i++)
1375 match_indices[i] = -1;
1376 ctx.match_indices = match_indices;
1378 from_pos = MGLYPH (from)->pos;
1379 to_pos = MGLYPH (to)->pos;
1381 for (stage_idx = 0; 1; stage_idx++)
1383 int len = to - from;
1386 MDEBUG_PRINT2 ("\n [FLT] (STAGE %d \"%s\"", stage_idx, ctx.encoded);
1387 if (mdebug__flag & mdebug_mask
1388 && ctx.encoded_offset < to)
1390 if (gstring->glyphs[ctx.encoded_offset].type == GLYPH_PAD)
1391 fprintf (stderr, " (|");
1393 fprintf (stderr, " (%X", gstring->glyphs[ctx.encoded_offset].code);
1394 for (i = ctx.encoded_offset + 1; i < to; i++)
1396 if (gstring->glyphs[i].type == GLYPH_PAD)
1397 fprintf (stderr, " |");
1399 fprintf (stderr, " %X", gstring->glyphs[i].code);
1401 fprintf (stderr, ")");
1404 gidx = gstring->used;
1407 result = run_command (4, INDEX_TO_CMD_ID (0), gstring,
1408 ctx.encoded_offset, to, &ctx);
1412 to = from + (gstring->used - gidx);
1413 REPLACE_GLYPHS (gstring, gidx, from, len);
1415 layouter = MPLIST_NEXT (layouter);
1416 /* If this is the last stage, break the loop. */
1417 if (MPLIST_TAIL_P (layouter))
1420 /* Otherwise, prepare for the next iteration. */
1421 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1422 table = stage->category;
1423 if (to - from >= encoded_len)
1425 encoded_len = to + 1;
1426 ctx.encoded = (char *) alloca (encoded_len);
1429 for (i = from; i < to; i++)
1431 MGlyph *g = MGLYPH (i);
1433 if (g->type == GLYPH_PAD)
1434 ctx.encoded[i - from] = ' ';
1435 else if (! g->otf_encoded)
1436 ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1437 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1440 int c = mfont__ft_decode_otf (g);
1444 c = (int) mchartable_lookup (table, c);
1448 ctx.encoded[i - from] = (c >= 0 ? c : 1);
1450 #endif /* HAVE_FREETYPE && HAVE_OTF */
1452 ctx.encoded[i - from] = '\0';
1453 ctx.encoded_offset = from;
1454 ctx.match_indices[0] = from;
1455 ctx.match_indices[1] = to;
1458 MDEBUG_PRINT (")\n");
1462 /* Somehow there's no glyph contributing to characters between
1463 FROM_POS and TO_POS. We must add one dummy space glyph for
1464 those characters. */
1467 g.type = GLYPH_SPACE;
1468 g.c = ' ', g.code = ' ';
1469 g.pos = from_pos, g.to = to_pos;
1470 g.rface = ascii_rface;
1471 INSERT_GLYPH (gstring, from, g);
1476 /* Get actual glyph IDs of glyphs. Also check if all characters
1477 in the range is covered by some glyph(s). If not, change
1478 <pos> and <to> of glyphs to cover uncovered characters. */
1479 int len = to_pos - from_pos;
1481 MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1482 MGlyph *g, *gend = MGLYPH (to);
1483 MGlyph *latest = gend;
1485 for (g = MGLYPH (from); g != gend; g++)
1486 if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1488 = (rface->rfont->driver->encode_char) (rface->rfont, g->code);
1489 for (i = 0; i < len; i++)
1491 for (g = MGLYPH (from); g != gend; g++)
1493 if (g->pos < latest->pos)
1495 if (! glyphs[g->pos - from_pos])
1497 for (i = g->pos; i < g->to; i++)
1498 glyphs[i - from_pos] = g;
1505 for (g = latest; g->pos == pos; g++)
1509 for (; i < len; i++)
1513 for (g = latest; g->pos == latest->pos; g++)
1514 g->to = from_pos + i + 1;
1524 /* for debugging... */
1527 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1529 char *prefix = (char *) alloca (indent + 1);
1531 memset (prefix, 32, indent);
1535 fprintf (stderr, "0x%02X", id);
1536 else if (id <= CMD_ID_OFFSET_INDEX)
1538 int idx = CMD_ID_TO_INDEX (id);
1539 FontLayoutCmd *cmd = stage->cmds + idx;
1541 if (cmd->type == FontLayoutCmdTypeRule)
1543 FontLayoutCmdRule *rule = &cmd->body.rule;
1546 fprintf (stderr, "(rule ");
1547 if (rule->src_type == SRC_REGEX)
1548 fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1549 else if (rule->src_type == SRC_INDEX)
1550 fprintf (stderr, "%d", rule->src.match_idx);
1551 else if (rule->src_type == SRC_SEQ)
1552 fprintf (stderr, "(seq)");
1553 else if (rule->src_type == SRC_RANGE)
1554 fprintf (stderr, "(range)");
1556 fprintf (stderr, "(invalid src)");
1558 for (i = 0; i < rule->n_cmds; i++)
1560 fprintf (stderr, "\n%s ", prefix);
1561 dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1563 fprintf (stderr, ")");
1565 else if (cmd->type == FontLayoutCmdTypeCond)
1567 FontLayoutCmdCond *cond = &cmd->body.cond;
1570 fprintf (stderr, "(cond");
1571 for (i = 0; i < cond->n_cmds; i++)
1573 fprintf (stderr, "\n%s ", prefix);
1574 dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1576 fprintf (stderr, ")");
1578 else if (cmd->type == FontLayoutCmdTypeOTF)
1580 fprintf (stderr, "(otf)");
1583 fprintf (stderr, "(error-command)");
1585 else if (id <= CMD_ID_OFFSET_COMBINING)
1586 fprintf (stderr, "cominging-code");
1588 fprintf (stderr, "(predefiend %d)", id);
1592 dump_flt (MFontLayoutTable *flt, int indent)
1594 char *prefix = (char *) alloca (indent + 1);
1598 memset (prefix, 32, indent);
1600 fprintf (stderr, "(flt");
1601 MPLIST_DO (plist, flt)
1603 FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1606 fprintf (stderr, "\n%s (stage %d", prefix, stage_idx);
1607 for (i = 0; i < stage->used; i++)
1609 fprintf (stderr, "\n%s ", prefix);
1610 dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1612 fprintf (stderr, ")");
1615 fprintf (stderr, ")");