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,
317 enum FontLayoutCmdType type;
319 FontLayoutCmdRule rule;
320 FontLayoutCmdCond cond;
321 FontLayoutCmdOTF otf;
327 MCharTable *category;
332 typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */
334 /* Font layout table loader */
336 /* Load a category table from PLIST. PLIST has this form:
337 PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
341 load_category_table (MPlist *plist)
345 table = mchartable (Minteger, (void *) 0);
347 MPLIST_DO (plist, plist)
350 int from, to, category_code;
352 if (! MPLIST_PLIST (plist))
353 MERROR (MERROR_FONT, NULL);
354 elt = MPLIST_PLIST (plist);
355 if (! MPLIST_INTEGER_P (elt))
356 MERROR (MERROR_FONT, NULL);
357 from = MPLIST_INTEGER (elt);
358 elt = MPLIST_NEXT (elt);
359 if (! MPLIST_INTEGER_P (elt))
360 MERROR (MERROR_FONT, NULL);
361 to = MPLIST_INTEGER (elt);
362 elt = MPLIST_NEXT (elt);
363 if (MPLIST_TAIL_P (elt))
370 if (! MPLIST_INTEGER_P (elt))
371 MERROR (MERROR_FONT, NULL);
372 category_code = MPLIST_INTEGER (elt);
374 if (! isalpha (category_code))
375 MERROR (MERROR_FONT, NULL);
378 mchartable_set (table, from, (void *) category_code);
380 mchartable_set_range (table, from, to, (void *) category_code);
387 /* Parse OTF command name NAME and store the result in CMD.
389 :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
390 where GSUB-FEATURES and GPOS-FEATURES have this form:
391 [FEATURE[,FEATURE]*] | ' ' */
394 load_otf_command (FontLayoutCmd *cmd, char *name)
396 char *p = name, *beg;
398 cmd->type = FontLayoutCmdTypeOTF;
399 cmd->body.otf.script = cmd->body.otf.langsys = Mnil;
400 cmd->body.otf.gsub_features = cmd->body.otf.gpos_features = Mt;
406 for (beg = ++p; *p && *p != '/' && *p != '=' && *p != '+'; p++);
408 cmd->body.otf.script = msymbol__with_len (beg, p - beg);
412 for (beg = ++p; *p && *p != '=' && *p != '+'; p++);
414 cmd->body.otf.langsys = msymbol__with_len (beg, p - beg);
418 for (beg = ++p; *p && *p != '+'; p++);
420 cmd->body.otf.gsub_features = msymbol__with_len (beg, p - beg);
422 cmd->body.otf.gsub_features = Mnil;
426 for (beg = ++p; *p && *p != '+'; p++);
428 cmd->body.otf.gpos_features = msymbol__with_len (beg, p - beg);
430 cmd->body.otf.gpos_features = Mnil;
436 return (cmd->body.otf.script == Mnil ? -1 : 0);
440 /* Read a decimal number from STR preceded by one of "+-><". '+' and
441 '>' means a plus sign, '-' and '<' means a minus sign. If the
442 number is greater than 127, limit it to 127. */
445 read_decimal_number (char **str)
448 int sign = (*p == '-' || *p == '<') ? -1 : 1;
452 while (*p >= '0' && *p <= '9')
453 n = n * 10 + *p++ - '0';
457 return (n < 127 ? n * sign : 127 * sign);
461 /* Read a horizontal and vertical combining positions from STR, and
462 store them in the place pointed by X and Y. The horizontal
463 position left, center, and right are represented by 0, 1, and 2
464 respectively. The vertical position top, center, bottom, and base
465 are represented by 0, 1, 2, and 3 respectively. If successfully
466 read, return 0, else return -1. */
469 read_combining_position (char *str, int *x, int *y)
474 /* Vertical position comes first. */
475 for (i = 0; i < 4; i++)
484 /* Then comse horizontal position. */
485 for (i = 0; i < 3; i++)
495 /* Return a combining code corresponding to SYM. */
498 get_combining_command (MSymbol sym)
500 char *str = msymbol_name (sym);
501 int base_x, base_y, add_x, add_y, off_x, off_y;
504 if (read_combining_position (str, &base_x, &base_y) < 0)
515 if (c == '+' || c == '-')
517 off_y = read_decimal_number (&str) + 128;
522 if (c == '<' || c == '>')
523 off_x = read_decimal_number (&str) + 128;
527 if (read_combining_position (str, &add_x, &add_y) < 0)
530 c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
531 return (COMBINING_CODE_TO_CMD_ID (c));
535 /* Load a command from PLIST into STAGE, and return that
536 identification number. If ID is not INVALID_CMD_ID, that means we
537 are loading a top level command or a macro. In that case, use ID
538 as the identification number of the command. Otherwise, generate a
539 new id number for the command. MACROS is a list of raw macros. */
542 load_command (FontLayoutStage *stage, MPlist *plist,
543 MPlist *macros, int id)
547 if (MPLIST_INTEGER_P (plist))
549 int code = MPLIST_INTEGER (plist);
552 MERROR (MERROR_DRAW, INVALID_CMD_ID);
555 else if (MPLIST_PLIST_P (plist))
557 /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
558 | ( ( INTEGER INTEGER ) ... )
559 | ( ( range INTEGER INTEGER ) ... ) */
560 MPlist *elt = MPLIST_PLIST (plist);
561 int len = MPLIST_LENGTH (elt) - 1;
564 if (id == INVALID_CMD_ID)
567 id = INDEX_TO_CMD_ID (stage->used);
568 MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
570 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
572 if (MPLIST_SYMBOL_P (elt))
574 FontLayoutCmdCond *cond;
576 if (MPLIST_SYMBOL (elt) != Mcond)
577 MERROR (MERROR_DRAW, INVALID_CMD_ID);
578 elt = MPLIST_NEXT (elt);
579 cmd->type = FontLayoutCmdTypeCond;
580 cond = &cmd->body.cond;
581 cond->seq_beg = cond->seq_end = -1;
582 cond->seq_from = cond->seq_to = 0;
584 MTABLE_CALLOC (cond->cmd_ids, len, MERROR_DRAW);
585 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
587 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
589 if (this_id == INVALID_CMD_ID)
590 MERROR (MERROR_DRAW, INVALID_CMD_ID);
591 /* The above load_command may relocate stage->cmds. */
592 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
593 cond = &cmd->body.cond;
594 cond->cmd_ids[i] = this_id;
595 if (this_id <= CMD_ID_OFFSET_INDEX)
597 FontLayoutCmd *this_cmd
598 = stage->cmds + CMD_ID_TO_INDEX (this_id);
600 if (this_cmd->type == FontLayoutCmdTypeRule
601 && this_cmd->body.rule.src_type == SRC_SEQ)
603 int first_char = this_cmd->body.rule.src.seq.codes[0];
605 if (cond->seq_beg < 0)
607 /* The first SEQ command. */
609 cond->seq_from = cond->seq_to = first_char;
611 else if (cond->seq_end < 0)
613 /* The following SEQ command. */
614 if (cond->seq_from > first_char)
615 cond->seq_from = first_char;
616 else if (cond->seq_to < first_char)
617 cond->seq_to = first_char;
622 if (cond->seq_beg >= 0 && cond->seq_end < 0)
623 /* The previous one is the last SEQ command. */
629 if (cond->seq_beg >= 0 && cond->seq_end < 0)
630 /* The previous one is the last SEQ command. */
634 if (cond->seq_beg >= 0 && cond->seq_end < 0)
635 /* The previous one is the last SEQ command. */
640 cmd->type = FontLayoutCmdTypeRule;
641 if (MPLIST_MTEXT_P (elt))
643 char *str = (char *) MTEXT_DATA (MPLIST_MTEXT (elt));
645 if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
646 MERROR (MERROR_FONT, INVALID_CMD_ID);
647 cmd->body.rule.src_type = SRC_REGEX;
648 cmd->body.rule.src.re.pattern = strdup (str);
650 else if (MPLIST_INTEGER_P (elt))
652 cmd->body.rule.src_type = SRC_INDEX;
653 cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
655 else if (MPLIST_PLIST_P (elt))
657 MPlist *pl = MPLIST_PLIST (elt);
658 int size = MPLIST_LENGTH (pl);
660 if (MPLIST_INTEGER_P (pl))
664 cmd->body.rule.src_type = SRC_SEQ;
665 cmd->body.rule.src.seq.n_codes = size;
666 MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
668 for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
670 if (! MPLIST_INTEGER_P (pl))
671 MERROR (MERROR_DRAW, INVALID_CMD_ID);
672 cmd->body.rule.src.seq.codes[i]
673 = (unsigned) MPLIST_INTEGER (pl);
676 else if (MPLIST_SYMBOL_P (pl) && size == 3)
678 cmd->body.rule.src_type = SRC_RANGE;
679 pl = MPLIST_NEXT (pl);
680 if (! MPLIST_INTEGER_P (pl))
681 MERROR (MERROR_DRAW, INVALID_CMD_ID);
682 cmd->body.rule.src.range.from
683 = (unsigned) MPLIST_INTEGER (pl);
684 pl = MPLIST_NEXT (pl);
685 if (! MPLIST_INTEGER_P (pl))
686 MERROR (MERROR_DRAW, INVALID_CMD_ID);
687 cmd->body.rule.src.range.to
688 = (unsigned) MPLIST_INTEGER (pl);
691 MERROR (MERROR_DRAW, INVALID_CMD_ID);
694 MERROR (MERROR_DRAW, INVALID_CMD_ID);
696 elt = MPLIST_NEXT (elt);
697 cmd->body.rule.n_cmds = len;
698 MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
699 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
701 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
703 if (this_id == INVALID_CMD_ID)
704 MERROR (MERROR_DRAW, INVALID_CMD_ID);
705 /* The above load_command may relocate stage->cmds. */
706 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
707 cmd->body.rule.cmd_ids[i] = this_id;
711 else if (MPLIST_SYMBOL_P (plist))
714 MSymbol sym = MPLIST_SYMBOL (plist);
715 char *name = msymbol_name (sym);
716 int len = strlen (name);
720 && ! strncmp (name, "otf:", 4)
721 && load_otf_command (&cmd, name + 3) >= 0)
723 if (id == INVALID_CMD_ID)
725 id = INDEX_TO_CMD_ID (stage->used);
726 MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
729 stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
737 else if (*name == '*')
738 return CMD_ID_REPEAT;
739 else if (*name == '<')
740 return CMD_ID_CLUSTER_BEGIN;
741 else if (*name == '>')
742 return CMD_ID_CLUSTER_END;
743 else if (*name == '|')
744 return CMD_ID_SEPARATOR;
745 else if (*name == '[')
746 return CMD_ID_LEFT_PADDING;
747 else if (*name == ']')
748 return CMD_ID_RIGHT_PADDING;
754 id = get_combining_command (sym);
760 MPLIST_DO (elt, macros)
762 if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
764 id = INDEX_TO_CMD_ID (i);
765 if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
766 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
772 MERROR (MERROR_DRAW, INVALID_CMD_ID);
775 MERROR (MERROR_DRAW, INVALID_CMD_ID);
781 free_flt_command (FontLayoutCmd *cmd)
783 if (cmd->type == FontLayoutCmdTypeRule)
785 FontLayoutCmdRule *rule = &cmd->body.rule;
787 if (rule->src_type == SRC_REGEX)
789 free (rule->src.re.pattern);
790 regfree (&rule->src.re.preg);
792 else if (rule->src_type == SRC_SEQ)
793 free (rule->src.seq.codes);
794 free (rule->cmd_ids);
796 else if (cmd->type == FontLayoutCmdTypeCond)
797 free (cmd->body.cond.cmd_ids);
800 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
801 and return it. PLIST has this form:
802 PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
805 static FontLayoutStage *
806 load_generator (MPlist *plist)
808 FontLayoutStage *stage;
812 MSTRUCT_CALLOC (stage, MERROR_DRAW);
813 MLIST_INIT1 (stage, cmds, 32);
814 dummy.type = FontLayoutCmdTypeMAX;
815 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
816 MPLIST_DO (elt, MPLIST_NEXT (plist))
818 if (! MPLIST_PLIST_P (elt))
819 MERROR (MERROR_FONT, NULL);
820 pl = MPLIST_PLIST (elt);
821 if (! MPLIST_SYMBOL_P (pl))
822 MERROR (MERROR_FONT, NULL);
823 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
826 /* Load the first command from PLIST into STAGE->cmds[0]. Macros
827 called in the first command are also loaded from MPLIST_NEXT
828 (PLIST) into STAGE->cmds[n]. */
829 if (load_command (stage, plist, MPLIST_NEXT (plist), INDEX_TO_CMD_ID (0))
832 MLIST_FREE1 (stage, cmds);
834 MERROR (MERROR_DRAW, NULL);
841 /* Load FLT of name LAYOUTER_NAME from the m17n database into a newly
842 allocated memory, and return it. */
844 static MFontLayoutTable *
845 load_flt (MSymbol layouter_name)
848 MPlist *top = NULL, *plist;
849 MSymbol Mcategory = msymbol ("category");
850 MSymbol Mgenerator = msymbol ("generator");
851 MFontLayoutTable *layouter = NULL;
852 MCharTable *category = NULL;
854 if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
855 MERROR_GOTO (MERROR_FONT, finish);
856 if (! (top = (MPlist *) mdatabase_load (mdb)))
857 MERROR_GOTO (0, finish);
858 if (! MPLIST_PLIST_P (top))
859 MERROR_GOTO (MERROR_FONT, finish);
861 MPLIST_DO (plist, top)
866 if (! MPLIST_PLIST (plist))
867 MERROR_GOTO (MERROR_FONT, finish);
868 elt = MPLIST_PLIST (plist);
869 if (! MPLIST_SYMBOL_P (elt))
870 MERROR_GOTO (MERROR_FONT, finish);
871 sym = MPLIST_SYMBOL (elt);
872 elt = MPLIST_NEXT (elt);
874 MERROR_GOTO (MERROR_FONT, finish);
875 if (sym == Mcategory)
878 M17N_OBJECT_UNREF (category);
879 category = load_category_table (elt);
881 else if (sym == Mgenerator)
883 FontLayoutStage *stage;
886 MERROR_GOTO (MERROR_FONT, finish);
887 stage = load_generator (elt);
889 MERROR_GOTO (MERROR_FONT, finish);
890 stage->category = category;
891 M17N_OBJECT_REF (category);
894 layouter = mplist ();
895 /* Here don't do M17N_OBJECT_REF (category) because we
896 don't unref the value of the element added below. */
897 mplist_add (layouter, Mcategory, category);
899 mplist_add (layouter, Mt, stage);
902 MERROR_GOTO (MERROR_FONT, finish);
906 M17N_OBJECT_UNREF (category);
909 M17N_OBJECT_UNREF (top);
910 mplist_add (flt_list, layouter_name, layouter);
916 free_flt_stage (FontLayoutStage *stage)
920 M17N_OBJECT_UNREF (stage->category);
921 for (i = 0; i < stage->used; i++)
922 free_flt_command (stage->cmds + i);
923 MLIST_FREE1 (stage, cmds);
928 static MFontLayoutTable *
929 get_font_layout_table (MSymbol layouter_name)
931 MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
933 return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
937 /* FLS (Font Layout Service) */
939 /* Structure to hold information about a context of FLS. */
943 /* Pointer to the current stage. */
944 FontLayoutStage *stage;
946 /* Encode each MGlyph->code by the current category table into this
947 array. An element is a category. */
949 /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
954 int cluster_begin_idx;
955 int cluster_begin_pos;
961 static int run_command (int depth,
962 int, MGlyphString *, int, int, FontLayoutContext *);
968 FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
969 FontLayoutContext *ctx)
971 int *saved_match_indices = ctx->match_indices;
972 int match_indices[NMATCH * 2];
975 int orig_from = from;
977 if (ctx->cluster_begin_idx)
979 if (ctx->cluster_begin_pos > MGLYPH (from)->pos)
980 ctx->cluster_begin_pos = MGLYPH (from)->pos;
981 if (ctx->cluster_end_pos < MGLYPH (to)->pos)
982 ctx->cluster_end_pos = MGLYPH (to)->pos;
985 if (rule->src_type == SRC_SEQ)
989 len = rule->src.seq.n_codes;
990 if (len > (to - from))
992 for (i = 0; i < len; i++)
993 if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
998 MDEBUG_PRINT1 (" (SEQ 0x%X", rule->src.seq.codes[0]);
1000 else if (rule->src_type == SRC_RANGE)
1006 head = gstring->glyphs[from].code;
1007 if (head < rule->src.range.from || head > rule->src.range.to)
1009 ctx->code_offset = head - rule->src.range.from;
1011 MDEBUG_PRINT2 (" (RANGE 0x%X-0x%X",
1012 rule->src.range.from, rule->src.range.to);
1014 else if (rule->src_type == SRC_REGEX)
1016 regmatch_t pmatch[NMATCH];
1022 saved_code = ctx->encoded[to - ctx->encoded_offset];
1023 ctx->encoded[to - ctx->encoded_offset] = '\0';
1024 result = regexec (&(rule->src.re.preg),
1025 ctx->encoded + from - ctx->encoded_offset,
1027 if (result == 0 && pmatch[0].rm_so == 0)
1029 MDEBUG_PRINT3 (" (REGEX \"%s\" \"%s\" %d",
1030 rule->src.re.pattern,
1031 ctx->encoded + from - ctx->encoded_offset,
1033 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1034 for (i = 0; i < NMATCH; i++)
1036 if (pmatch[i].rm_so < 0)
1037 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1040 match_indices[i * 2] = from + pmatch[i].rm_so;
1041 match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1044 ctx->match_indices = match_indices;
1045 to = match_indices[1];
1049 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1053 else if (rule->src_type == SRC_INDEX)
1055 if (rule->src.match_idx >= NMATCH)
1057 from = ctx->match_indices[rule->src.match_idx * 2];
1060 to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1061 MDEBUG_PRINT1 (" (INDEX %d", rule->src.match_idx);
1066 for (i = 0; i < rule->n_cmds; i++)
1070 if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1076 pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1078 MERROR (MERROR_DRAW, -1);
1079 consumed = pos > from;
1084 ctx->match_indices = saved_match_indices;
1086 return (rule->src_type == SRC_INDEX ? orig_from : to);
1090 run_cond (int depth,
1091 FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1092 FontLayoutContext *ctx)
1096 MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1098 for (i = 0; i < cond->n_cmds; i++)
1100 /* TODO: Write a code for optimization utilizaing the info
1102 if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1107 MERROR (MERROR_DRAW, -1);
1114 FontLayoutCmdOTF *otf_cmd, MGlyphString *gstring, int from, int to,
1115 FontLayoutContext *ctx)
1118 int gidx = gstring->used;
1119 MGlyph *g = MGLYPH (from), *gend = MGLYPH (to);
1121 for (; g < gend; g++)
1122 g->otf_cmd = otf_cmd;
1124 to = mfont__ft_drive_gsub (gstring, from, to);
1125 if (gidx < gstring->used)
1126 MGLYPH (gidx)->left_padding = ctx->left_padding;
1132 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1133 FontLayoutContext *ctx)
1141 /* Direct code (== id + ctx->code_offset) output.
1142 The source is not consumed. */
1144 g = *(MGLYPH (from));
1146 g = *(MGLYPH (from - 1));
1147 g.type = GLYPH_CHAR;
1148 g.code = ctx->code_offset + id;
1149 MDEBUG_PRINT1 (" (DIRECT 0x%X", g.code);
1150 if (ctx->combining_code)
1151 g.combining_code = ctx->combining_code;
1152 if (ctx->left_padding)
1153 g.left_padding = ctx->left_padding;
1154 for (i = from; i < to; i++)
1156 MGlyph *tmp = MGLYPH (i);
1158 if (g.pos > tmp->pos)
1160 else if (g.to < tmp->to)
1163 APPEND_GLYPH (gstring, g);
1164 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1169 if (id <= CMD_ID_OFFSET_INDEX)
1171 int idx = CMD_ID_TO_INDEX (id);
1174 if (idx >= ctx->stage->used)
1175 MERROR (MERROR_DRAW, -1);
1176 cmd = ctx->stage->cmds + idx;
1177 if (cmd->type == FontLayoutCmdTypeRule)
1178 to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1179 else if (cmd->type == FontLayoutCmdTypeCond)
1180 to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1181 else if (cmd->type == FontLayoutCmdTypeOTF)
1182 to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1189 if (id <= CMD_ID_OFFSET_COMBINING)
1191 ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1201 g = *(MGLYPH (from));
1202 if (ctx->combining_code)
1203 g.combining_code = ctx->combining_code;
1204 if (ctx->left_padding)
1205 g.left_padding = ctx->left_padding;
1206 APPEND_GLYPH (gstring, g);
1207 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1211 case CMD_ID_CLUSTER_BEGIN:
1212 if (! ctx->cluster_begin_idx)
1214 MDEBUG_PRINT1 (" <%d", MGLYPH (from)->pos);
1215 ctx->cluster_begin_idx = gstring->used;
1216 ctx->cluster_begin_pos = MGLYPH (from)->pos;
1217 ctx->cluster_end_pos = MGLYPH (from)->to;
1221 case CMD_ID_CLUSTER_END:
1222 if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1226 MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1227 for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1229 MGLYPH (i)->pos = ctx->cluster_begin_pos;
1230 MGLYPH (i)->to = ctx->cluster_end_pos;
1232 ctx->cluster_begin_idx = 0;
1236 case CMD_ID_SEPARATOR:
1239 g = *(MGLYPH (from));
1241 g = *(MGLYPH (from - 1));
1243 /* g.c = g.code = 0; */
1245 APPEND_GLYPH (gstring, g);
1249 case CMD_ID_LEFT_PADDING:
1250 ctx->left_padding = 1;
1253 case CMD_ID_RIGHT_PADDING:
1254 if (gstring->used > 0)
1255 gstring->glyphs[gstring->used - 1].right_padding = 1;
1259 MERROR (MERROR_DRAW, -1);
1266 mfont__flt_init (void)
1268 Mcond = msymbol ("cond");
1269 Mrange = msymbol ("range");
1270 Mlayouter = msymbol ("layouter");
1271 flt_list = mplist ();
1276 mfont__flt_fini (void)
1280 MPLIST_DO (plist, flt_list)
1282 pl = MPLIST_PLIST (plist);
1285 MPLIST_DO (pl, MPLIST_NEXT (pl))
1286 free_flt_stage (MPLIST_VAL (pl));
1287 pl = MPLIST_PLIST (plist);
1288 M17N_OBJECT_UNREF (pl);
1291 M17N_OBJECT_UNREF (flt_list);
1295 mfont__flt_encode_char (MSymbol layouter_name, int c)
1297 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1302 return MCHAR_INVALID_CODE;
1303 table = MPLIST_VAL (layouter);
1304 code = (unsigned) mchartable_lookup (table, c);
1305 return (code ? code : MCHAR_INVALID_CODE);
1309 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1314 FontLayoutContext ctx;
1317 int match_indices[NMATCH];
1318 MSymbol layouter_name = rface->rfont->layouter;
1319 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1320 MRealizedFace *ascii_rface = rface->ascii_rface;
1321 FontLayoutStage *stage;
1322 int from_pos, to_pos;
1327 /* FLT not found. Make all glyphs invisible. */
1329 gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1333 dummy = gstring->glyphs[from];
1334 MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1337 memset (&ctx, 0, sizeof ctx);
1338 table = MPLIST_VAL (layouter);
1339 layouter = MPLIST_NEXT (layouter);
1340 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1342 /* Find previous glyphs that are also supported by the layouter. */
1344 && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1346 /* + 2 is for a separator ' ' and a terminator '\0'. */
1347 encoded_len = gstring->used - gidx + 2;
1348 ctx.encoded = (char *) alloca (encoded_len);
1350 for (i = 0; gidx < from; i++, gidx++)
1351 ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1353 ctx.encoded[i++] = ' ';
1354 ctx.encoded_offset = from - i;
1356 /* Now each MGlyph->code contains encoded char. Set it in
1357 ctx.encoded[], and set MGlyph->c to MGlyph->code. */
1358 for (gidx = from; gidx < to ; i++, gidx++)
1360 ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1361 MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1363 ctx.encoded[i++] = '\0';
1365 match_indices[0] = from;
1366 match_indices[1] = to;
1367 for (i = 2; i < NMATCH; i++)
1368 match_indices[i] = -1;
1369 ctx.match_indices = match_indices;
1371 from_pos = MGLYPH (from)->pos;
1372 to_pos = MGLYPH (to)->pos;
1374 for (stage_idx = 0; 1; stage_idx++)
1376 int len = to - from;
1379 MDEBUG_PRINT1 ("\n [FLT] (STAGE %d", stage_idx);
1380 gidx = gstring->used;
1383 result = run_command (2, INDEX_TO_CMD_ID (0), gstring,
1384 ctx.encoded_offset, to, &ctx);
1388 to = from + (gstring->used - gidx);
1389 REPLACE_GLYPHS (gstring, gidx, from, len);
1391 layouter = MPLIST_NEXT (layouter);
1392 /* If this is the last stage, break the loop. */
1393 if (MPLIST_TAIL_P (layouter))
1396 /* Otherwise, prepare for the next iteration. */
1397 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1398 table = stage->category;
1399 if (to - from >= encoded_len)
1401 encoded_len = to + 1;
1402 ctx.encoded = (char *) alloca (encoded_len);
1405 for (i = from; i < to; i++)
1407 MGlyph *g = MGLYPH (i);
1409 if (g->type == GLYPH_PAD)
1410 ctx.encoded[i - from] = ' ';
1411 else if (! g->otf_encoded)
1412 ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1413 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1416 int c = mfont__ft_decode_otf (g);
1420 c = (int) mchartable_lookup (table, c);
1424 ctx.encoded[i - from] = (c >= 0 ? c : 1);
1426 #endif /* HAVE_FREETYPE && HAVE_OTF */
1428 ctx.encoded[i - from] = '\0';
1429 ctx.encoded_offset = from;
1430 ctx.match_indices[0] = from;
1431 ctx.match_indices[1] = to;
1434 MDEBUG_PRINT (")\n");
1438 /* Somehow there's no glyph contributing to characters between
1439 FROM_POS and TO_POS. We must add one dummy space glyph for
1440 those characters. */
1443 g.type = GLYPH_SPACE;
1444 g.c = ' ', g.code = ' ';
1445 g.pos = from_pos, g.to = to_pos;
1446 g.rface = ascii_rface;
1447 INSERT_GLYPH (gstring, from, g);
1452 /* Get actual glyph IDs of glyphs. Also check if all characters
1453 in the range is covered by some glyph(s). If not, change
1454 <pos> and <to> of glyphs to cover uncovered characters. */
1455 int len = to_pos - from_pos;
1457 MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1458 MGlyph *g, *gend = MGLYPH (to);
1459 MGlyph *latest = gend;
1461 for (g = MGLYPH (from); g != gend; g++)
1462 if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1464 = (rface->rfont->driver->encode_char) (rface->rfont, g->code);
1465 for (i = 0; i < len; i++)
1467 for (g = MGLYPH (from); g != gend; g++)
1469 if (g->pos < latest->pos)
1471 if (! glyphs[g->pos - from_pos])
1473 for (i = g->pos; i < g->to; i++)
1474 glyphs[i - from_pos] = g;
1481 for (g = latest; g->pos == pos; g++)
1485 for (; i < len; i++)
1489 for (g = latest; g->pos == latest->pos; g++)
1490 g->to = from_pos + i + 1;
1500 /* for debugging... */
1503 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1505 char *prefix = (char *) alloca (indent + 1);
1507 memset (prefix, 32, indent);
1511 fprintf (stderr, "0x%02X", id);
1512 else if (id <= CMD_ID_OFFSET_INDEX)
1514 int idx = CMD_ID_TO_INDEX (id);
1515 FontLayoutCmd *cmd = stage->cmds + idx;
1517 if (cmd->type == FontLayoutCmdTypeRule)
1519 FontLayoutCmdRule *rule = &cmd->body.rule;
1522 fprintf (stderr, "(rule ");
1523 if (rule->src_type == SRC_REGEX)
1524 fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1525 else if (rule->src_type == SRC_INDEX)
1526 fprintf (stderr, "%d", rule->src.match_idx);
1527 else if (rule->src_type == SRC_SEQ)
1528 fprintf (stderr, "(seq)");
1529 else if (rule->src_type == SRC_RANGE)
1530 fprintf (stderr, "(range)");
1532 fprintf (stderr, "(invalid src)");
1534 for (i = 0; i < rule->n_cmds; i++)
1536 fprintf (stderr, "\n%s ", prefix);
1537 dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1539 fprintf (stderr, ")");
1541 else if (cmd->type == FontLayoutCmdTypeCond)
1543 FontLayoutCmdCond *cond = &cmd->body.cond;
1546 fprintf (stderr, "(cond");
1547 for (i = 0; i < cond->n_cmds; i++)
1549 fprintf (stderr, "\n%s ", prefix);
1550 dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1552 fprintf (stderr, ")");
1554 else if (cmd->type == FontLayoutCmdTypeOTF)
1556 fprintf (stderr, "(otf)");
1559 fprintf (stderr, "(error-command)");
1561 else if (id <= CMD_ID_OFFSET_COMBINING)
1562 fprintf (stderr, "cominging-code");
1564 fprintf (stderr, "(predefiend %d)", id);
1568 dump_flt (MFontLayoutTable *flt, int indent)
1570 char *prefix = (char *) alloca (indent + 1);
1574 memset (prefix, 32, indent);
1576 fprintf (stderr, "(flt");
1577 MPLIST_DO (plist, flt)
1579 FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1582 fprintf (stderr, "\n%s (stage %d", prefix, stage_idx);
1583 for (i = 0; i < stage->used; i++)
1585 fprintf (stderr, "\n%s ", prefix);
1586 dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1588 fprintf (stderr, ")");
1591 fprintf (stderr, ")");