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 #define UPDATE_CLUSTER_RANGE(ctx, g) \
268 if ((ctx)->cluster_begin_idx) \
270 if (ctx->cluster_begin_pos > (g).pos) \
271 ctx->cluster_begin_pos = (g).pos; \
272 if (ctx->cluster_end_pos < (g).to) \
273 ctx->cluster_end_pos = (g).to; \
277 enum FontLayoutCmdRuleSrcType
287 enum FontLayoutCmdRuleSrcType src_type;
309 /* Beginning and end indices of series of SEQ commands. */
310 int seq_beg, seq_end;
311 /* Range of the first character appears in the above series. */
312 int seq_from, seq_to;
318 enum FontLayoutCmdType
320 FontLayoutCmdTypeRule,
321 FontLayoutCmdTypeCond,
322 FontLayoutCmdTypeOTF,
330 MSymbol gsub_features;
331 MSymbol gpos_features;
336 enum FontLayoutCmdType type;
338 FontLayoutCmdRule rule;
339 FontLayoutCmdCond cond;
340 FontLayoutCmdOTF otf;
346 MCharTable *category;
351 typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */
353 /* Font layout table loader */
355 /* Load a category table from PLIST. PLIST has this form:
356 PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
360 load_category_table (MPlist *plist)
364 table = mchartable (Minteger, (void *) 0);
366 MPLIST_DO (plist, plist)
369 int from, to, category_code;
371 if (! MPLIST_PLIST (plist))
372 MERROR (MERROR_FONT, NULL);
373 elt = MPLIST_PLIST (plist);
374 if (! MPLIST_INTEGER_P (elt))
375 MERROR (MERROR_FONT, NULL);
376 from = MPLIST_INTEGER (elt);
377 elt = MPLIST_NEXT (elt);
378 if (! MPLIST_INTEGER_P (elt))
379 MERROR (MERROR_FONT, NULL);
380 to = MPLIST_INTEGER (elt);
381 elt = MPLIST_NEXT (elt);
382 if (MPLIST_TAIL_P (elt))
389 if (! MPLIST_INTEGER_P (elt))
390 MERROR (MERROR_FONT, NULL);
391 category_code = MPLIST_INTEGER (elt);
393 if (! isalpha (category_code))
394 MERROR (MERROR_FONT, NULL);
397 mchartable_set (table, from, (void *) category_code);
399 mchartable_set_range (table, from, to, (void *) category_code);
406 /* Parse OTF command name NAME and store the result in CMD.
408 :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
409 where GSUB-FEATURES and GPOS-FEATURES have this form:
410 [FEATURE[,FEATURE]*] | ' ' */
413 load_otf_command (FontLayoutCmd *cmd, char *name)
415 char *p = name, *beg;
417 cmd->type = FontLayoutCmdTypeOTF;
418 cmd->body.otf.script = cmd->body.otf.langsys = Mnil;
419 cmd->body.otf.gsub_features = cmd->body.otf.gpos_features = Mt;
425 for (beg = ++p; *p && *p != '/' && *p != '=' && *p != '+'; p++);
427 cmd->body.otf.script = msymbol__with_len (beg, p - beg);
431 for (beg = ++p; *p && *p != '=' && *p != '+'; p++);
433 cmd->body.otf.langsys = msymbol__with_len (beg, p - beg);
437 for (beg = ++p; *p && *p != '+'; p++);
439 cmd->body.otf.gsub_features = msymbol__with_len (beg, p - beg);
441 cmd->body.otf.gsub_features = Mnil;
445 for (beg = ++p; *p && *p != '+'; p++);
447 cmd->body.otf.gpos_features = msymbol__with_len (beg, p - beg);
449 cmd->body.otf.gpos_features = Mnil;
455 return (cmd->body.otf.script == Mnil ? -1 : 0);
459 /* Read a decimal number from STR preceded by one of "+-><". '+' and
460 '>' means a plus sign, '-' and '<' means a minus sign. If the
461 number is greater than 127, limit it to 127. */
464 read_decimal_number (char **str)
467 int sign = (*p == '-' || *p == '<') ? -1 : 1;
471 while (*p >= '0' && *p <= '9')
472 n = n * 10 + *p++ - '0';
476 return (n < 127 ? n * sign : 127 * sign);
480 /* Read a horizontal and vertical combining positions from STR, and
481 store them in the place pointed by X and Y. The horizontal
482 position left, center, and right are represented by 0, 1, and 2
483 respectively. The vertical position top, center, bottom, and base
484 are represented by 0, 1, 2, and 3 respectively. If successfully
485 read, return 0, else return -1. */
488 read_combining_position (char *str, int *x, int *y)
493 /* Vertical position comes first. */
494 for (i = 0; i < 4; i++)
503 /* Then comse horizontal position. */
504 for (i = 0; i < 3; i++)
514 /* Return a combining code corresponding to SYM. */
517 get_combining_command (MSymbol sym)
519 char *str = msymbol_name (sym);
520 int base_x, base_y, add_x, add_y, off_x, off_y;
523 if (read_combining_position (str, &base_x, &base_y) < 0)
534 if (c == '+' || c == '-')
536 off_y = read_decimal_number (&str) + 128;
541 if (c == '<' || c == '>')
542 off_x = read_decimal_number (&str) + 128;
546 if (read_combining_position (str, &add_x, &add_y) < 0)
549 c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
550 return (COMBINING_CODE_TO_CMD_ID (c));
554 /* Load a command from PLIST into STAGE, and return that
555 identification number. If ID is not INVALID_CMD_ID, that means we
556 are loading a top level command or a macro. In that case, use ID
557 as the identification number of the command. Otherwise, generate a
558 new id number for the command. MACROS is a list of raw macros. */
561 load_command (FontLayoutStage *stage, MPlist *plist,
562 MPlist *macros, int id)
566 if (MPLIST_INTEGER_P (plist))
568 int code = MPLIST_INTEGER (plist);
571 MERROR (MERROR_DRAW, INVALID_CMD_ID);
574 else if (MPLIST_PLIST_P (plist))
576 /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
577 | ( ( INTEGER INTEGER ) ... )
578 | ( ( range INTEGER INTEGER ) ... ) */
579 MPlist *elt = MPLIST_PLIST (plist);
580 int len = MPLIST_LENGTH (elt) - 1;
583 if (id == INVALID_CMD_ID)
586 id = INDEX_TO_CMD_ID (stage->used);
587 MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
589 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
591 if (MPLIST_SYMBOL_P (elt))
593 FontLayoutCmdCond *cond;
595 if (MPLIST_SYMBOL (elt) != Mcond)
596 MERROR (MERROR_DRAW, INVALID_CMD_ID);
597 elt = MPLIST_NEXT (elt);
598 cmd->type = FontLayoutCmdTypeCond;
599 cond = &cmd->body.cond;
600 cond->seq_beg = cond->seq_end = -1;
601 cond->seq_from = cond->seq_to = 0;
603 MTABLE_CALLOC (cond->cmd_ids, len, MERROR_DRAW);
604 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
606 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
608 if (this_id == INVALID_CMD_ID)
609 MERROR (MERROR_DRAW, INVALID_CMD_ID);
610 /* The above load_command may relocate stage->cmds. */
611 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
612 cond = &cmd->body.cond;
613 cond->cmd_ids[i] = this_id;
614 if (this_id <= CMD_ID_OFFSET_INDEX)
616 FontLayoutCmd *this_cmd
617 = stage->cmds + CMD_ID_TO_INDEX (this_id);
619 if (this_cmd->type == FontLayoutCmdTypeRule
620 && this_cmd->body.rule.src_type == SRC_SEQ)
622 int first_char = this_cmd->body.rule.src.seq.codes[0];
624 if (cond->seq_beg < 0)
626 /* The first SEQ command. */
628 cond->seq_from = cond->seq_to = first_char;
630 else if (cond->seq_end < 0)
632 /* The following SEQ command. */
633 if (cond->seq_from > first_char)
634 cond->seq_from = first_char;
635 else if (cond->seq_to < first_char)
636 cond->seq_to = first_char;
641 if (cond->seq_beg >= 0 && cond->seq_end < 0)
642 /* The previous one is the last SEQ command. */
648 if (cond->seq_beg >= 0 && cond->seq_end < 0)
649 /* The previous one is the last SEQ command. */
653 if (cond->seq_beg >= 0 && cond->seq_end < 0)
654 /* The previous one is the last SEQ command. */
659 cmd->type = FontLayoutCmdTypeRule;
660 if (MPLIST_MTEXT_P (elt))
662 char *str = (char *) MTEXT_DATA (MPLIST_MTEXT (elt));
664 if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
665 MERROR (MERROR_FONT, INVALID_CMD_ID);
666 cmd->body.rule.src_type = SRC_REGEX;
667 cmd->body.rule.src.re.pattern = strdup (str);
669 else if (MPLIST_INTEGER_P (elt))
671 cmd->body.rule.src_type = SRC_INDEX;
672 cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
674 else if (MPLIST_PLIST_P (elt))
676 MPlist *pl = MPLIST_PLIST (elt);
677 int size = MPLIST_LENGTH (pl);
679 if (MPLIST_INTEGER_P (pl))
683 cmd->body.rule.src_type = SRC_SEQ;
684 cmd->body.rule.src.seq.n_codes = size;
685 MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
687 for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
689 if (! MPLIST_INTEGER_P (pl))
690 MERROR (MERROR_DRAW, INVALID_CMD_ID);
691 cmd->body.rule.src.seq.codes[i]
692 = (unsigned) MPLIST_INTEGER (pl);
695 else if (MPLIST_SYMBOL_P (pl) && size == 3)
697 cmd->body.rule.src_type = SRC_RANGE;
698 pl = MPLIST_NEXT (pl);
699 if (! MPLIST_INTEGER_P (pl))
700 MERROR (MERROR_DRAW, INVALID_CMD_ID);
701 cmd->body.rule.src.range.from
702 = (unsigned) MPLIST_INTEGER (pl);
703 pl = MPLIST_NEXT (pl);
704 if (! MPLIST_INTEGER_P (pl))
705 MERROR (MERROR_DRAW, INVALID_CMD_ID);
706 cmd->body.rule.src.range.to
707 = (unsigned) MPLIST_INTEGER (pl);
710 MERROR (MERROR_DRAW, INVALID_CMD_ID);
713 MERROR (MERROR_DRAW, INVALID_CMD_ID);
715 elt = MPLIST_NEXT (elt);
716 cmd->body.rule.n_cmds = len;
717 MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
718 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
720 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
722 if (this_id == INVALID_CMD_ID)
723 MERROR (MERROR_DRAW, INVALID_CMD_ID);
724 /* The above load_command may relocate stage->cmds. */
725 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
726 cmd->body.rule.cmd_ids[i] = this_id;
730 else if (MPLIST_SYMBOL_P (plist))
733 MSymbol sym = MPLIST_SYMBOL (plist);
734 char *name = msymbol_name (sym);
735 int len = strlen (name);
739 && ! strncmp (name, "otf:", 4)
740 && load_otf_command (&cmd, name + 3) >= 0)
742 if (id == INVALID_CMD_ID)
744 id = INDEX_TO_CMD_ID (stage->used);
745 MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
748 stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
756 else if (*name == '*')
757 return CMD_ID_REPEAT;
758 else if (*name == '<')
759 return CMD_ID_CLUSTER_BEGIN;
760 else if (*name == '>')
761 return CMD_ID_CLUSTER_END;
762 else if (*name == '|')
763 return CMD_ID_SEPARATOR;
764 else if (*name == '[')
765 return CMD_ID_LEFT_PADDING;
766 else if (*name == ']')
767 return CMD_ID_RIGHT_PADDING;
773 id = get_combining_command (sym);
779 MPLIST_DO (elt, macros)
781 if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
783 id = INDEX_TO_CMD_ID (i);
784 if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
785 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
791 MERROR (MERROR_DRAW, INVALID_CMD_ID);
794 MERROR (MERROR_DRAW, INVALID_CMD_ID);
800 free_flt_command (FontLayoutCmd *cmd)
802 if (cmd->type == FontLayoutCmdTypeRule)
804 FontLayoutCmdRule *rule = &cmd->body.rule;
806 if (rule->src_type == SRC_REGEX)
808 free (rule->src.re.pattern);
809 regfree (&rule->src.re.preg);
811 else if (rule->src_type == SRC_SEQ)
812 free (rule->src.seq.codes);
813 free (rule->cmd_ids);
815 else if (cmd->type == FontLayoutCmdTypeCond)
816 free (cmd->body.cond.cmd_ids);
819 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
820 and return it. PLIST has this form:
821 PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
824 static FontLayoutStage *
825 load_generator (MPlist *plist)
827 FontLayoutStage *stage;
831 MSTRUCT_CALLOC (stage, MERROR_DRAW);
832 MLIST_INIT1 (stage, cmds, 32);
833 dummy.type = FontLayoutCmdTypeMAX;
834 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
835 MPLIST_DO (elt, MPLIST_NEXT (plist))
837 if (! MPLIST_PLIST_P (elt))
838 MERROR (MERROR_FONT, NULL);
839 pl = MPLIST_PLIST (elt);
840 if (! MPLIST_SYMBOL_P (pl))
841 MERROR (MERROR_FONT, NULL);
842 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
845 /* Load the first command from PLIST into STAGE->cmds[0]. Macros
846 called in the first command are also loaded from MPLIST_NEXT
847 (PLIST) into STAGE->cmds[n]. */
848 if (load_command (stage, plist, MPLIST_NEXT (plist), INDEX_TO_CMD_ID (0))
851 MLIST_FREE1 (stage, cmds);
853 MERROR (MERROR_DRAW, NULL);
860 /* Load FLT of name LAYOUTER_NAME from the m17n database into a newly
861 allocated memory, and return it. */
863 static MFontLayoutTable *
864 load_flt (MSymbol layouter_name)
867 MPlist *top = NULL, *plist;
868 MSymbol Mcategory = msymbol ("category");
869 MSymbol Mgenerator = msymbol ("generator");
870 MSymbol Mend = msymbol ("end");
871 MFontLayoutTable *layouter = NULL;
872 MCharTable *category = NULL;
874 if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
875 MERROR_GOTO (MERROR_FONT, finish);
876 if (! (top = (MPlist *) mdatabase_load (mdb)))
877 MERROR_GOTO (0, finish);
878 if (! MPLIST_PLIST_P (top))
879 MERROR_GOTO (MERROR_FONT, finish);
881 MPLIST_DO (plist, top)
886 if (MPLIST_SYMBOL_P (plist)
887 && MPLIST_SYMBOL (plist) == Mend)
889 if (! MPLIST_PLIST (plist))
890 MERROR_GOTO (MERROR_FONT, finish);
891 elt = MPLIST_PLIST (plist);
892 if (! MPLIST_SYMBOL_P (elt))
893 MERROR_GOTO (MERROR_FONT, finish);
894 sym = MPLIST_SYMBOL (elt);
895 elt = MPLIST_NEXT (elt);
897 MERROR_GOTO (MERROR_FONT, finish);
898 if (sym == Mcategory)
901 M17N_OBJECT_UNREF (category);
902 category = load_category_table (elt);
904 else if (sym == Mgenerator)
906 FontLayoutStage *stage;
909 MERROR_GOTO (MERROR_FONT, finish);
910 stage = load_generator (elt);
912 MERROR_GOTO (MERROR_FONT, finish);
913 stage->category = category;
914 M17N_OBJECT_REF (category);
917 layouter = mplist ();
918 /* Here don't do M17N_OBJECT_REF (category) because we
919 don't unref the value of the element added below. */
920 mplist_add (layouter, Mcategory, category);
922 mplist_add (layouter, Mt, stage);
925 MERROR_GOTO (MERROR_FONT, finish);
929 M17N_OBJECT_UNREF (category);
932 M17N_OBJECT_UNREF (top);
933 mplist_add (flt_list, layouter_name, layouter);
939 free_flt_stage (FontLayoutStage *stage)
943 M17N_OBJECT_UNREF (stage->category);
944 for (i = 0; i < stage->used; i++)
945 free_flt_command (stage->cmds + i);
946 MLIST_FREE1 (stage, cmds);
951 static MFontLayoutTable *
952 get_font_layout_table (MSymbol layouter_name)
954 MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
956 return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
960 /* FLS (Font Layout Service) */
962 /* Structure to hold information about a context of FLS. */
966 /* Pointer to the current stage. */
967 FontLayoutStage *stage;
969 /* Encode each MGlyph->code by the current category table into this
970 array. An element is a category. */
972 /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
977 int cluster_begin_idx;
978 int cluster_begin_pos;
984 static int run_command (int depth,
985 int, MGlyphString *, int, int, FontLayoutContext *);
991 FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
992 FontLayoutContext *ctx)
994 int *saved_match_indices = ctx->match_indices;
995 int match_indices[NMATCH * 2];
998 int orig_from = from;
1000 if (rule->src_type == SRC_SEQ)
1004 len = rule->src.seq.n_codes;
1005 if (len > (to - from))
1007 for (i = 0; i < len; i++)
1008 if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
1013 MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "",
1014 rule->src.seq.codes[0]);
1016 else if (rule->src_type == SRC_RANGE)
1022 head = gstring->glyphs[from].code;
1023 if (head < rule->src.range.from || head > rule->src.range.to)
1025 ctx->code_offset = head - rule->src.range.from;
1027 MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "",
1028 rule->src.range.from, rule->src.range.to);
1030 else if (rule->src_type == SRC_REGEX)
1032 regmatch_t pmatch[NMATCH];
1038 saved_code = ctx->encoded[to - ctx->encoded_offset];
1039 ctx->encoded[to - ctx->encoded_offset] = '\0';
1040 result = regexec (&(rule->src.re.preg),
1041 ctx->encoded + from - ctx->encoded_offset,
1043 if (result == 0 && pmatch[0].rm_so == 0)
1045 MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "",
1046 rule->src.re.pattern,
1047 ctx->encoded + from - ctx->encoded_offset,
1049 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1050 for (i = 0; i < NMATCH; i++)
1052 if (pmatch[i].rm_so < 0)
1053 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1056 match_indices[i * 2] = from + pmatch[i].rm_so;
1057 match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1060 ctx->match_indices = match_indices;
1061 to = match_indices[1];
1065 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1069 else if (rule->src_type == SRC_INDEX)
1071 if (rule->src.match_idx >= NMATCH)
1073 from = ctx->match_indices[rule->src.match_idx * 2];
1076 to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1077 MDEBUG_PRINT3 ("\n [FLT] %*s(INDEX %d", depth, "", rule->src.match_idx);
1082 for (i = 0; i < rule->n_cmds; i++)
1086 if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1092 pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1094 MERROR (MERROR_DRAW, -1);
1095 consumed = pos > from;
1100 ctx->match_indices = saved_match_indices;
1102 return (rule->src_type == SRC_INDEX ? orig_from : to);
1106 run_cond (int depth,
1107 FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1108 FontLayoutContext *ctx)
1112 MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1114 for (i = 0; i < cond->n_cmds; i++)
1116 /* TODO: Write a code for optimization utilizaing the info
1118 if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1123 MERROR (MERROR_DRAW, -1);
1130 FontLayoutCmdOTF *otf_cmd, MGlyphString *gstring, int from, int to,
1131 FontLayoutContext *ctx)
1134 int from_idx = gstring->used;
1136 MDEBUG_PRINT4 ("\n [FLT] %*s(OTF %s,%s)", depth, "",
1137 (otf_cmd->gsub_features == Mnil ? ""
1138 : MSYMBOL_NAME (otf_cmd->gsub_features)),
1139 (otf_cmd->gpos_features == Mnil ? ""
1140 : MSYMBOL_NAME (otf_cmd->gpos_features)));
1141 to = mfont__ft_drive_otf (gstring, from, to,
1142 otf_cmd->script, otf_cmd->langsys,
1143 otf_cmd->gsub_features, otf_cmd->gpos_features);
1144 if (ctx->cluster_begin_idx)
1145 for (; from_idx < gstring->used; from_idx++)
1146 UPDATE_CLUSTER_RANGE (ctx, gstring->glyphs[from_idx]);
1152 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1153 FontLayoutContext *ctx)
1161 /* Direct code (== id + ctx->code_offset) output.
1162 The source is not consumed. */
1164 g = *(MGLYPH (from));
1166 g = *(MGLYPH (from - 1));
1167 g.type = GLYPH_CHAR;
1168 g.code = ctx->code_offset + id;
1169 MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "", g.code);
1170 if (ctx->combining_code)
1171 g.combining_code = ctx->combining_code;
1172 if (ctx->left_padding)
1173 g.left_padding = ctx->left_padding;
1174 for (i = from; i < to; i++)
1176 MGlyph *tmp = MGLYPH (i);
1178 if (g.pos > tmp->pos)
1180 else if (g.to < tmp->to)
1183 APPEND_GLYPH (gstring, g);
1184 UPDATE_CLUSTER_RANGE (ctx, g);
1185 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1190 if (id <= CMD_ID_OFFSET_INDEX)
1192 int idx = CMD_ID_TO_INDEX (id);
1195 if (idx >= ctx->stage->used)
1196 MERROR (MERROR_DRAW, -1);
1197 cmd = ctx->stage->cmds + idx;
1198 if (cmd->type == FontLayoutCmdTypeRule)
1199 to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1200 else if (cmd->type == FontLayoutCmdTypeCond)
1201 to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1202 else if (cmd->type == FontLayoutCmdTypeOTF)
1203 to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1210 if (id <= CMD_ID_OFFSET_COMBINING)
1212 ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1222 g = *(MGLYPH (from));
1223 if (ctx->combining_code)
1224 g.combining_code = ctx->combining_code;
1225 if (ctx->left_padding)
1226 g.left_padding = ctx->left_padding;
1227 APPEND_GLYPH (gstring, g);
1228 UPDATE_CLUSTER_RANGE (ctx, g);
1229 MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g.code);
1230 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1234 case CMD_ID_CLUSTER_BEGIN:
1235 if (! ctx->cluster_begin_idx)
1237 MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "", MGLYPH (from)->pos);
1238 ctx->cluster_begin_idx = gstring->used;
1239 ctx->cluster_begin_pos = MGLYPH (from)->pos;
1240 ctx->cluster_end_pos = MGLYPH (from)->to;
1244 case CMD_ID_CLUSTER_END:
1245 if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1249 MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1250 for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1252 MGLYPH (i)->pos = ctx->cluster_begin_pos;
1253 MGLYPH (i)->to = ctx->cluster_end_pos;
1255 ctx->cluster_begin_idx = 0;
1259 case CMD_ID_SEPARATOR:
1262 g = *(MGLYPH (from));
1264 g = *(MGLYPH (from - 1));
1266 /* g.c = g.code = 0; */
1268 APPEND_GLYPH (gstring, g);
1272 case CMD_ID_LEFT_PADDING:
1273 ctx->left_padding = 1;
1276 case CMD_ID_RIGHT_PADDING:
1277 if (gstring->used > 0)
1278 gstring->glyphs[gstring->used - 1].right_padding = 1;
1282 MERROR (MERROR_DRAW, -1);
1289 mfont__flt_init (void)
1291 Mcond = msymbol ("cond");
1292 Mrange = msymbol ("range");
1293 Mlayouter = msymbol ("layouter");
1294 flt_list = mplist ();
1299 mfont__flt_fini (void)
1303 MPLIST_DO (plist, flt_list)
1305 pl = MPLIST_PLIST (plist);
1308 MPLIST_DO (pl, MPLIST_NEXT (pl))
1309 free_flt_stage (MPLIST_VAL (pl));
1310 pl = MPLIST_PLIST (plist);
1311 M17N_OBJECT_UNREF (pl);
1314 M17N_OBJECT_UNREF (flt_list);
1318 mfont__flt_encode_char (MSymbol layouter_name, int c)
1320 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1325 return MCHAR_INVALID_CODE;
1326 table = MPLIST_VAL (layouter);
1327 code = (unsigned) mchartable_lookup (table, c);
1328 return (code ? code : MCHAR_INVALID_CODE);
1332 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1337 FontLayoutContext ctx;
1340 int match_indices[NMATCH];
1341 MSymbol layouter_name = rface->rfont->layouter;
1342 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1343 MRealizedFace *ascii_rface = rface->ascii_rface;
1344 FontLayoutStage *stage;
1345 int from_pos, to_pos;
1350 /* FLT not found. Make all glyphs invisible. */
1352 gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1356 dummy = gstring->glyphs[from];
1357 MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1360 memset (&ctx, 0, sizeof ctx);
1361 table = MPLIST_VAL (layouter);
1362 layouter = MPLIST_NEXT (layouter);
1363 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1365 /* Find previous glyphs that are also supported by the layouter. */
1367 && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1369 /* + 2 is for a separator ' ' and a terminator '\0'. */
1370 encoded_len = gstring->used - gidx + 2;
1371 ctx.encoded = (char *) alloca (encoded_len);
1373 for (i = 0; gidx < from; i++, gidx++)
1374 ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1376 ctx.encoded[i++] = ' ';
1377 ctx.encoded_offset = from - i;
1379 /* Now each MGlyph->code contains encoded char. Set it in
1380 ctx.encoded[], and set MGlyph->c to MGlyph->code. */
1381 for (gidx = from; gidx < to ; i++, gidx++)
1383 ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1384 MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1386 ctx.encoded[i++] = '\0';
1388 match_indices[0] = from;
1389 match_indices[1] = to;
1390 for (i = 2; i < NMATCH; i++)
1391 match_indices[i] = -1;
1392 ctx.match_indices = match_indices;
1394 from_pos = MGLYPH (from)->pos;
1395 to_pos = MGLYPH (to)->pos;
1397 for (stage_idx = 0; 1; stage_idx++)
1399 int len = to - from;
1402 MDEBUG_PRINT2 ("\n [FLT] (STAGE %d \"%s\"", stage_idx, ctx.encoded);
1403 if (mdebug__flag & mdebug_mask
1404 && ctx.encoded_offset < to)
1406 if (gstring->glyphs[ctx.encoded_offset].type == GLYPH_PAD)
1407 fprintf (stderr, " (|");
1409 fprintf (stderr, " (%X", gstring->glyphs[ctx.encoded_offset].code);
1410 for (i = ctx.encoded_offset + 1; i < to; i++)
1412 if (gstring->glyphs[i].type == GLYPH_PAD)
1413 fprintf (stderr, " |");
1415 fprintf (stderr, " %X", gstring->glyphs[i].code);
1417 fprintf (stderr, ")");
1420 gidx = gstring->used;
1423 result = run_command (4, INDEX_TO_CMD_ID (0), gstring,
1424 ctx.encoded_offset, to, &ctx);
1428 to = from + (gstring->used - gidx);
1429 REPLACE_GLYPHS (gstring, gidx, from, len);
1431 layouter = MPLIST_NEXT (layouter);
1432 /* If this is the last stage, break the loop. */
1433 if (MPLIST_TAIL_P (layouter))
1436 /* Otherwise, prepare for the next iteration. */
1437 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1438 table = stage->category;
1439 if (to - from >= encoded_len)
1441 encoded_len = to + 1;
1442 ctx.encoded = (char *) alloca (encoded_len);
1445 for (i = from; i < to; i++)
1447 MGlyph *g = MGLYPH (i);
1449 if (g->type == GLYPH_PAD)
1450 ctx.encoded[i - from] = ' ';
1451 else if (! g->otf_encoded)
1452 ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1453 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1456 int c = mfont__ft_decode_otf (g);
1460 c = (int) mchartable_lookup (table, c);
1464 ctx.encoded[i - from] = (c >= 0 ? c : 1);
1466 #endif /* HAVE_FREETYPE && HAVE_OTF */
1468 ctx.encoded[i - from] = '\0';
1469 ctx.encoded_offset = from;
1470 ctx.match_indices[0] = from;
1471 ctx.match_indices[1] = to;
1474 MDEBUG_PRINT (")\n");
1478 /* Somehow there's no glyph contributing to characters between
1479 FROM_POS and TO_POS. We must add one dummy space glyph for
1480 those characters. */
1483 g.type = GLYPH_SPACE;
1484 g.c = ' ', g.code = ' ';
1485 g.pos = from_pos, g.to = to_pos;
1486 g.rface = ascii_rface;
1487 INSERT_GLYPH (gstring, from, g);
1492 /* Get actual glyph IDs of glyphs. Also check if all characters
1493 in the range is covered by some glyph(s). If not, change
1494 <pos> and <to> of glyphs to cover uncovered characters. */
1495 int len = to_pos - from_pos;
1497 MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1498 MGlyph *g, *gend = MGLYPH (to);
1499 MGlyph *latest = gend;
1501 for (g = MGLYPH (from); g != gend; g++)
1502 if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1504 = (rface->rfont->driver->encode_char) (rface->rfont, g->code);
1505 for (i = 0; i < len; i++)
1507 for (g = MGLYPH (from); g != gend; g++)
1509 if (g->pos < latest->pos)
1511 if (! glyphs[g->pos - from_pos])
1513 for (i = g->pos; i < g->to; i++)
1514 glyphs[i - from_pos] = g;
1521 for (g = latest; g->pos == pos; g++)
1525 for (; i < len; i++)
1529 for (g = latest; g->pos == latest->pos; g++)
1530 g->to = from_pos + i + 1;
1540 /* for debugging... */
1543 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1545 char *prefix = (char *) alloca (indent + 1);
1547 memset (prefix, 32, indent);
1551 fprintf (stderr, "0x%02X", id);
1552 else if (id <= CMD_ID_OFFSET_INDEX)
1554 int idx = CMD_ID_TO_INDEX (id);
1555 FontLayoutCmd *cmd = stage->cmds + idx;
1557 if (cmd->type == FontLayoutCmdTypeRule)
1559 FontLayoutCmdRule *rule = &cmd->body.rule;
1562 fprintf (stderr, "(rule ");
1563 if (rule->src_type == SRC_REGEX)
1564 fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1565 else if (rule->src_type == SRC_INDEX)
1566 fprintf (stderr, "%d", rule->src.match_idx);
1567 else if (rule->src_type == SRC_SEQ)
1568 fprintf (stderr, "(seq)");
1569 else if (rule->src_type == SRC_RANGE)
1570 fprintf (stderr, "(range)");
1572 fprintf (stderr, "(invalid src)");
1574 for (i = 0; i < rule->n_cmds; i++)
1576 fprintf (stderr, "\n%s ", prefix);
1577 dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1579 fprintf (stderr, ")");
1581 else if (cmd->type == FontLayoutCmdTypeCond)
1583 FontLayoutCmdCond *cond = &cmd->body.cond;
1586 fprintf (stderr, "(cond");
1587 for (i = 0; i < cond->n_cmds; i++)
1589 fprintf (stderr, "\n%s ", prefix);
1590 dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1592 fprintf (stderr, ")");
1594 else if (cmd->type == FontLayoutCmdTypeOTF)
1596 fprintf (stderr, "(otf)");
1599 fprintf (stderr, "(error-command)");
1601 else if (id <= CMD_ID_OFFSET_COMBINING)
1602 fprintf (stderr, "cominging-code");
1604 fprintf (stderr, "(predefiend %d)", id);
1608 dump_flt (MFontLayoutTable *flt, int indent)
1610 char *prefix = (char *) alloca (indent + 1);
1614 memset (prefix, 32, indent);
1616 fprintf (stderr, "(flt");
1617 MPLIST_DO (plist, flt)
1619 FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1622 fprintf (stderr, "\n%s (stage %d", prefix, stage_idx);
1623 for (i = 0; i < stage->used; i++)
1625 fprintf (stderr, "\n%s ", prefix);
1626 dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1628 fprintf (stderr, ")");
1631 fprintf (stderr, ")");