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 MDEBUG_PRINT4 ("\n [FLT] %*s(OTF %s,%s)", depth, "",
1131 (otf_cmd->gsub_features == Mnil ? ""
1132 : MSYMBOL_NAME (otf_cmd->gsub_features)),
1133 (otf_cmd->gpos_features == Mnil ? ""
1134 : MSYMBOL_NAME (otf_cmd->gpos_features)));
1135 to = mfont__ft_drive_otf (gstring, from, to,
1136 otf_cmd->script, otf_cmd->langsys,
1137 otf_cmd->gsub_features, otf_cmd->gpos_features);
1143 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1144 FontLayoutContext *ctx)
1152 /* Direct code (== id + ctx->code_offset) output.
1153 The source is not consumed. */
1155 g = *(MGLYPH (from));
1157 g = *(MGLYPH (from - 1));
1158 g.type = GLYPH_CHAR;
1159 g.code = ctx->code_offset + id;
1160 MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "", g.code);
1161 if (ctx->combining_code)
1162 g.combining_code = ctx->combining_code;
1163 if (ctx->left_padding)
1164 g.left_padding = ctx->left_padding;
1165 for (i = from; i < to; i++)
1167 MGlyph *tmp = MGLYPH (i);
1169 if (g.pos > tmp->pos)
1171 else if (g.to < tmp->to)
1174 APPEND_GLYPH (gstring, g);
1175 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1180 if (id <= CMD_ID_OFFSET_INDEX)
1182 int idx = CMD_ID_TO_INDEX (id);
1185 if (idx >= ctx->stage->used)
1186 MERROR (MERROR_DRAW, -1);
1187 cmd = ctx->stage->cmds + idx;
1188 if (cmd->type == FontLayoutCmdTypeRule)
1189 to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1190 else if (cmd->type == FontLayoutCmdTypeCond)
1191 to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1192 else if (cmd->type == FontLayoutCmdTypeOTF)
1193 to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1200 if (id <= CMD_ID_OFFSET_COMBINING)
1202 ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1212 g = *(MGLYPH (from));
1213 if (ctx->combining_code)
1214 g.combining_code = ctx->combining_code;
1215 if (ctx->left_padding)
1216 g.left_padding = ctx->left_padding;
1217 APPEND_GLYPH (gstring, g);
1218 MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g.code);
1219 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1223 case CMD_ID_CLUSTER_BEGIN:
1224 if (! ctx->cluster_begin_idx)
1226 MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "", MGLYPH (from)->pos);
1227 ctx->cluster_begin_idx = gstring->used;
1228 ctx->cluster_begin_pos = MGLYPH (from)->pos;
1229 ctx->cluster_end_pos = MGLYPH (from)->to;
1233 case CMD_ID_CLUSTER_END:
1234 if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1238 MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1239 for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1241 MGLYPH (i)->pos = ctx->cluster_begin_pos;
1242 MGLYPH (i)->to = ctx->cluster_end_pos;
1244 ctx->cluster_begin_idx = 0;
1248 case CMD_ID_SEPARATOR:
1251 g = *(MGLYPH (from));
1253 g = *(MGLYPH (from - 1));
1255 /* g.c = g.code = 0; */
1257 APPEND_GLYPH (gstring, g);
1261 case CMD_ID_LEFT_PADDING:
1262 ctx->left_padding = 1;
1265 case CMD_ID_RIGHT_PADDING:
1266 if (gstring->used > 0)
1267 gstring->glyphs[gstring->used - 1].right_padding = 1;
1271 MERROR (MERROR_DRAW, -1);
1278 mfont__flt_init (void)
1280 Mcond = msymbol ("cond");
1281 Mrange = msymbol ("range");
1282 Mlayouter = msymbol ("layouter");
1283 flt_list = mplist ();
1288 mfont__flt_fini (void)
1292 MPLIST_DO (plist, flt_list)
1294 pl = MPLIST_PLIST (plist);
1297 MPLIST_DO (pl, MPLIST_NEXT (pl))
1298 free_flt_stage (MPLIST_VAL (pl));
1299 pl = MPLIST_PLIST (plist);
1300 M17N_OBJECT_UNREF (pl);
1303 M17N_OBJECT_UNREF (flt_list);
1307 mfont__flt_encode_char (MSymbol layouter_name, int c)
1309 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1314 return MCHAR_INVALID_CODE;
1315 table = MPLIST_VAL (layouter);
1316 code = (unsigned) mchartable_lookup (table, c);
1317 return (code ? code : MCHAR_INVALID_CODE);
1321 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1326 FontLayoutContext ctx;
1329 int match_indices[NMATCH];
1330 MSymbol layouter_name = rface->rfont->layouter;
1331 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1332 MRealizedFace *ascii_rface = rface->ascii_rface;
1333 FontLayoutStage *stage;
1334 int from_pos, to_pos;
1339 /* FLT not found. Make all glyphs invisible. */
1341 gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1345 dummy = gstring->glyphs[from];
1346 MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1349 memset (&ctx, 0, sizeof ctx);
1350 table = MPLIST_VAL (layouter);
1351 layouter = MPLIST_NEXT (layouter);
1352 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1354 /* Find previous glyphs that are also supported by the layouter. */
1356 && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1358 /* + 2 is for a separator ' ' and a terminator '\0'. */
1359 encoded_len = gstring->used - gidx + 2;
1360 ctx.encoded = (char *) alloca (encoded_len);
1362 for (i = 0; gidx < from; i++, gidx++)
1363 ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1365 ctx.encoded[i++] = ' ';
1366 ctx.encoded_offset = from - i;
1368 /* Now each MGlyph->code contains encoded char. Set it in
1369 ctx.encoded[], and set MGlyph->c to MGlyph->code. */
1370 for (gidx = from; gidx < to ; i++, gidx++)
1372 ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1373 MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1375 ctx.encoded[i++] = '\0';
1377 match_indices[0] = from;
1378 match_indices[1] = to;
1379 for (i = 2; i < NMATCH; i++)
1380 match_indices[i] = -1;
1381 ctx.match_indices = match_indices;
1383 from_pos = MGLYPH (from)->pos;
1384 to_pos = MGLYPH (to)->pos;
1386 for (stage_idx = 0; 1; stage_idx++)
1388 int len = to - from;
1391 MDEBUG_PRINT2 ("\n [FLT] (STAGE %d \"%s\"", stage_idx, ctx.encoded);
1392 if (mdebug__flag & mdebug_mask
1393 && ctx.encoded_offset < to)
1395 if (gstring->glyphs[ctx.encoded_offset].type == GLYPH_PAD)
1396 fprintf (stderr, " (|");
1398 fprintf (stderr, " (%X", gstring->glyphs[ctx.encoded_offset].code);
1399 for (i = ctx.encoded_offset + 1; i < to; i++)
1401 if (gstring->glyphs[i].type == GLYPH_PAD)
1402 fprintf (stderr, " |");
1404 fprintf (stderr, " %X", gstring->glyphs[i].code);
1406 fprintf (stderr, ")");
1409 gidx = gstring->used;
1412 result = run_command (4, INDEX_TO_CMD_ID (0), gstring,
1413 ctx.encoded_offset, to, &ctx);
1417 to = from + (gstring->used - gidx);
1418 REPLACE_GLYPHS (gstring, gidx, from, len);
1420 layouter = MPLIST_NEXT (layouter);
1421 /* If this is the last stage, break the loop. */
1422 if (MPLIST_TAIL_P (layouter))
1425 /* Otherwise, prepare for the next iteration. */
1426 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1427 table = stage->category;
1428 if (to - from >= encoded_len)
1430 encoded_len = to + 1;
1431 ctx.encoded = (char *) alloca (encoded_len);
1434 for (i = from; i < to; i++)
1436 MGlyph *g = MGLYPH (i);
1438 if (g->type == GLYPH_PAD)
1439 ctx.encoded[i - from] = ' ';
1440 else if (! g->otf_encoded)
1441 ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1442 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1445 int c = mfont__ft_decode_otf (g);
1449 c = (int) mchartable_lookup (table, c);
1453 ctx.encoded[i - from] = (c >= 0 ? c : 1);
1455 #endif /* HAVE_FREETYPE && HAVE_OTF */
1457 ctx.encoded[i - from] = '\0';
1458 ctx.encoded_offset = from;
1459 ctx.match_indices[0] = from;
1460 ctx.match_indices[1] = to;
1463 MDEBUG_PRINT (")\n");
1467 /* Somehow there's no glyph contributing to characters between
1468 FROM_POS and TO_POS. We must add one dummy space glyph for
1469 those characters. */
1472 g.type = GLYPH_SPACE;
1473 g.c = ' ', g.code = ' ';
1474 g.pos = from_pos, g.to = to_pos;
1475 g.rface = ascii_rface;
1476 INSERT_GLYPH (gstring, from, g);
1481 /* Get actual glyph IDs of glyphs. Also check if all characters
1482 in the range is covered by some glyph(s). If not, change
1483 <pos> and <to> of glyphs to cover uncovered characters. */
1484 int len = to_pos - from_pos;
1486 MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1487 MGlyph *g, *gend = MGLYPH (to);
1488 MGlyph *latest = gend;
1490 for (g = MGLYPH (from); g != gend; g++)
1491 if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1493 = (rface->rfont->driver->encode_char) (rface->rfont, g->code);
1494 for (i = 0; i < len; i++)
1496 for (g = MGLYPH (from); g != gend; g++)
1498 if (g->pos < latest->pos)
1500 if (! glyphs[g->pos - from_pos])
1502 for (i = g->pos; i < g->to; i++)
1503 glyphs[i - from_pos] = g;
1510 for (g = latest; g->pos == pos; g++)
1514 for (; i < len; i++)
1518 for (g = latest; g->pos == latest->pos; g++)
1519 g->to = from_pos + i + 1;
1529 /* for debugging... */
1532 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1534 char *prefix = (char *) alloca (indent + 1);
1536 memset (prefix, 32, indent);
1540 fprintf (stderr, "0x%02X", id);
1541 else if (id <= CMD_ID_OFFSET_INDEX)
1543 int idx = CMD_ID_TO_INDEX (id);
1544 FontLayoutCmd *cmd = stage->cmds + idx;
1546 if (cmd->type == FontLayoutCmdTypeRule)
1548 FontLayoutCmdRule *rule = &cmd->body.rule;
1551 fprintf (stderr, "(rule ");
1552 if (rule->src_type == SRC_REGEX)
1553 fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1554 else if (rule->src_type == SRC_INDEX)
1555 fprintf (stderr, "%d", rule->src.match_idx);
1556 else if (rule->src_type == SRC_SEQ)
1557 fprintf (stderr, "(seq)");
1558 else if (rule->src_type == SRC_RANGE)
1559 fprintf (stderr, "(range)");
1561 fprintf (stderr, "(invalid src)");
1563 for (i = 0; i < rule->n_cmds; i++)
1565 fprintf (stderr, "\n%s ", prefix);
1566 dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1568 fprintf (stderr, ")");
1570 else if (cmd->type == FontLayoutCmdTypeCond)
1572 FontLayoutCmdCond *cond = &cmd->body.cond;
1575 fprintf (stderr, "(cond");
1576 for (i = 0; i < cond->n_cmds; i++)
1578 fprintf (stderr, "\n%s ", prefix);
1579 dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1581 fprintf (stderr, ")");
1583 else if (cmd->type == FontLayoutCmdTypeOTF)
1585 fprintf (stderr, "(otf)");
1588 fprintf (stderr, "(error-command)");
1590 else if (id <= CMD_ID_OFFSET_COMBINING)
1591 fprintf (stderr, "cominging-code");
1593 fprintf (stderr, "(predefiend %d)", id);
1597 dump_flt (MFontLayoutTable *flt, int indent)
1599 char *prefix = (char *) alloca (indent + 1);
1603 memset (prefix, 32, indent);
1605 fprintf (stderr, "(flt");
1606 MPLIST_DO (plist, flt)
1608 FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1611 fprintf (stderr, "\n%s (stage %d", prefix, stage_idx);
1612 for (i = 0; i < stage->used; i++)
1614 fprintf (stderr, "\n%s ", prefix);
1615 dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1617 fprintf (stderr, ")");
1620 fprintf (stderr, ")");