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., 51 Franklin Street, Fifth Floor,
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,
328 enum FontLayoutCmdType type;
330 FontLayoutCmdRule rule;
331 FontLayoutCmdCond cond;
332 MFontCapability *otf;
338 MCharTable *category;
343 typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */
345 /* Font layout table loader */
347 /* Load a category table from PLIST. PLIST has this form:
348 PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
352 load_category_table (MPlist *plist)
356 table = mchartable (Minteger, (void *) 0);
358 MPLIST_DO (plist, plist)
361 int from, to, category_code;
363 if (! MPLIST_PLIST (plist))
364 MERROR (MERROR_FONT, NULL);
365 elt = MPLIST_PLIST (plist);
366 if (! MPLIST_INTEGER_P (elt))
367 MERROR (MERROR_FONT, NULL);
368 from = MPLIST_INTEGER (elt);
369 elt = MPLIST_NEXT (elt);
370 if (! MPLIST_INTEGER_P (elt))
371 MERROR (MERROR_FONT, NULL);
372 to = MPLIST_INTEGER (elt);
373 elt = MPLIST_NEXT (elt);
374 if (MPLIST_TAIL_P (elt))
381 if (! MPLIST_INTEGER_P (elt))
382 MERROR (MERROR_FONT, NULL);
383 category_code = MPLIST_INTEGER (elt);
385 if (! isalpha (category_code))
386 MERROR (MERROR_FONT, NULL);
389 mchartable_set (table, from, (void *) category_code);
391 mchartable_set_range (table, from, to, (void *) category_code);
398 /* Parse OTF command name NAME and store the result in CMD.
400 :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
401 where GSUB-FEATURES and GPOS-FEATURES have this form:
402 [FEATURE[,FEATURE]*] | ' ' */
405 load_otf_command (FontLayoutCmd *cmd, MSymbol sym)
407 char *name = MSYMBOL_NAME (sym);
411 /* This is old format of "otf:...". Change it to ":otf=...". */
412 char *str = alloca (MSYMBOL_NAMELEN (sym) + 2);
414 sprintf (str, ":otf=");
415 strcat (str, name + 4);
419 cmd->body.otf = mfont__get_capability (sym);
422 if (cmd->body.otf->script == Mnil)
424 cmd->body.otf = NULL;
427 M17N_OBJECT_REF (cmd->body.otf);
428 cmd->type = FontLayoutCmdTypeOTF;
433 /* Read a decimal number from STR preceded by one of "+-><". '+' and
434 '>' means a plus sign, '-' and '<' means a minus sign. If the
435 number is greater than 127, limit it to 127. */
438 read_decimal_number (char **str)
441 int sign = (*p == '-' || *p == '<') ? -1 : 1;
445 while (*p >= '0' && *p <= '9')
446 n = n * 10 + *p++ - '0';
450 return (n < 127 ? n * sign : 127 * sign);
454 /* Read a horizontal and vertical combining positions from STR, and
455 store them in the place pointed by X and Y. The horizontal
456 position left, center, and right are represented by 0, 1, and 2
457 respectively. The vertical position top, center, bottom, and base
458 are represented by 0, 1, 2, and 3 respectively. If successfully
459 read, return 0, else return -1. */
462 read_combining_position (char *str, int *x, int *y)
467 /* Vertical position comes first. */
468 for (i = 0; i < 4; i++)
477 /* Then comse horizontal position. */
478 for (i = 0; i < 3; i++)
488 /* Return a combining code corresponding to SYM. */
491 get_combining_command (MSymbol sym)
493 char *str = msymbol_name (sym);
494 int base_x, base_y, add_x, add_y, off_x, off_y;
497 if (read_combining_position (str, &base_x, &base_y) < 0)
508 if (c == '+' || c == '-')
510 off_y = read_decimal_number (&str) + 128;
515 if (c == '<' || c == '>')
516 off_x = read_decimal_number (&str) + 128;
520 if (read_combining_position (str, &add_x, &add_y) < 0)
523 c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
524 return (COMBINING_CODE_TO_CMD_ID (c));
528 /* Load a command from PLIST into STAGE, and return that
529 identification number. If ID is not INVALID_CMD_ID, that means we
530 are loading a top level command or a macro. In that case, use ID
531 as the identification number of the command. Otherwise, generate a
532 new id number for the command. MACROS is a list of raw macros. */
535 load_command (FontLayoutStage *stage, MPlist *plist,
536 MPlist *macros, int id)
540 if (MPLIST_INTEGER_P (plist))
542 int code = MPLIST_INTEGER (plist);
545 MERROR (MERROR_DRAW, INVALID_CMD_ID);
548 else if (MPLIST_PLIST_P (plist))
550 /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
551 | ( ( INTEGER INTEGER ) ... )
552 | ( ( range INTEGER INTEGER ) ... ) */
553 MPlist *elt = MPLIST_PLIST (plist);
554 int len = MPLIST_LENGTH (elt) - 1;
557 if (id == INVALID_CMD_ID)
560 id = INDEX_TO_CMD_ID (stage->used);
561 MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
563 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
565 if (MPLIST_SYMBOL_P (elt))
567 FontLayoutCmdCond *cond;
569 if (MPLIST_SYMBOL (elt) != Mcond)
570 MERROR (MERROR_DRAW, INVALID_CMD_ID);
571 elt = MPLIST_NEXT (elt);
572 cmd->type = FontLayoutCmdTypeCond;
573 cond = &cmd->body.cond;
574 cond->seq_beg = cond->seq_end = -1;
575 cond->seq_from = cond->seq_to = 0;
577 MTABLE_CALLOC (cond->cmd_ids, len, MERROR_DRAW);
578 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
580 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
582 if (this_id == INVALID_CMD_ID)
583 MERROR (MERROR_DRAW, INVALID_CMD_ID);
584 /* The above load_command may relocate stage->cmds. */
585 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
586 cond = &cmd->body.cond;
587 cond->cmd_ids[i] = this_id;
588 if (this_id <= CMD_ID_OFFSET_INDEX)
590 FontLayoutCmd *this_cmd
591 = stage->cmds + CMD_ID_TO_INDEX (this_id);
593 if (this_cmd->type == FontLayoutCmdTypeRule
594 && this_cmd->body.rule.src_type == SRC_SEQ)
596 int first_char = this_cmd->body.rule.src.seq.codes[0];
598 if (cond->seq_beg < 0)
600 /* The first SEQ command. */
602 cond->seq_from = cond->seq_to = first_char;
604 else if (cond->seq_end < 0)
606 /* The following SEQ command. */
607 if (cond->seq_from > first_char)
608 cond->seq_from = first_char;
609 else if (cond->seq_to < first_char)
610 cond->seq_to = first_char;
615 if (cond->seq_beg >= 0 && cond->seq_end < 0)
616 /* The previous one is the last SEQ command. */
622 if (cond->seq_beg >= 0 && cond->seq_end < 0)
623 /* The previous one is the last SEQ command. */
627 if (cond->seq_beg >= 0 && cond->seq_end < 0)
628 /* The previous one is the last SEQ command. */
633 cmd->type = FontLayoutCmdTypeRule;
634 if (MPLIST_MTEXT_P (elt))
636 char *str = (char *) MTEXT_DATA (MPLIST_MTEXT (elt));
638 if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
639 MERROR (MERROR_FONT, INVALID_CMD_ID);
640 cmd->body.rule.src_type = SRC_REGEX;
641 cmd->body.rule.src.re.pattern = strdup (str);
643 else if (MPLIST_INTEGER_P (elt))
645 cmd->body.rule.src_type = SRC_INDEX;
646 cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
648 else if (MPLIST_PLIST_P (elt))
650 MPlist *pl = MPLIST_PLIST (elt);
651 int size = MPLIST_LENGTH (pl);
653 if (MPLIST_INTEGER_P (pl))
657 cmd->body.rule.src_type = SRC_SEQ;
658 cmd->body.rule.src.seq.n_codes = size;
659 MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
661 for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
663 if (! MPLIST_INTEGER_P (pl))
664 MERROR (MERROR_DRAW, INVALID_CMD_ID);
665 cmd->body.rule.src.seq.codes[i]
666 = (unsigned) MPLIST_INTEGER (pl);
669 else if (MPLIST_SYMBOL_P (pl) && size == 3)
671 cmd->body.rule.src_type = SRC_RANGE;
672 pl = MPLIST_NEXT (pl);
673 if (! MPLIST_INTEGER_P (pl))
674 MERROR (MERROR_DRAW, INVALID_CMD_ID);
675 cmd->body.rule.src.range.from
676 = (unsigned) MPLIST_INTEGER (pl);
677 pl = MPLIST_NEXT (pl);
678 if (! MPLIST_INTEGER_P (pl))
679 MERROR (MERROR_DRAW, INVALID_CMD_ID);
680 cmd->body.rule.src.range.to
681 = (unsigned) MPLIST_INTEGER (pl);
684 MERROR (MERROR_DRAW, INVALID_CMD_ID);
687 MERROR (MERROR_DRAW, INVALID_CMD_ID);
689 elt = MPLIST_NEXT (elt);
690 cmd->body.rule.n_cmds = len;
691 MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
692 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
694 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
696 if (this_id == INVALID_CMD_ID)
697 MERROR (MERROR_DRAW, INVALID_CMD_ID);
698 /* The above load_command may relocate stage->cmds. */
699 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
700 cmd->body.rule.cmd_ids[i] = this_id;
704 else if (MPLIST_SYMBOL_P (plist))
707 MSymbol sym = MPLIST_SYMBOL (plist);
708 char *name = msymbol_name (sym);
709 int len = strlen (name);
713 && ((name[0] == 'o' && name[1] == 't'
714 && name[2] == 'f' && name[3] == ':')
715 || (name[0] == ':' && name[1] == 'o' && name[2] == 't'
716 && name[3] == 'f' && name[4] == '='))
717 && load_otf_command (&cmd, sym) >= 0)
719 if (id == INVALID_CMD_ID)
721 id = INDEX_TO_CMD_ID (stage->used);
722 MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
725 stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
733 else if (*name == '*')
734 return CMD_ID_REPEAT;
735 else if (*name == '<')
736 return CMD_ID_CLUSTER_BEGIN;
737 else if (*name == '>')
738 return CMD_ID_CLUSTER_END;
739 else if (*name == '|')
740 return CMD_ID_SEPARATOR;
741 else if (*name == '[')
742 return CMD_ID_LEFT_PADDING;
743 else if (*name == ']')
744 return CMD_ID_RIGHT_PADDING;
750 id = get_combining_command (sym);
756 MPLIST_DO (elt, macros)
758 if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
760 id = INDEX_TO_CMD_ID (i);
761 if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
762 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
768 MERROR (MERROR_DRAW, INVALID_CMD_ID);
771 MERROR (MERROR_DRAW, INVALID_CMD_ID);
777 free_flt_command (FontLayoutCmd *cmd)
779 if (cmd->type == FontLayoutCmdTypeRule)
781 FontLayoutCmdRule *rule = &cmd->body.rule;
783 if (rule->src_type == SRC_REGEX)
785 free (rule->src.re.pattern);
786 regfree (&rule->src.re.preg);
788 else if (rule->src_type == SRC_SEQ)
789 free (rule->src.seq.codes);
790 free (rule->cmd_ids);
792 else if (cmd->type == FontLayoutCmdTypeCond)
793 free (cmd->body.cond.cmd_ids);
794 else if (cmd->type == FontLayoutCmdTypeOTF)
795 M17N_OBJECT_UNREF (cmd->body.otf);
798 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
799 and return it. PLIST has this form:
800 PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
803 static FontLayoutStage *
804 load_generator (MPlist *plist)
806 FontLayoutStage *stage;
810 MSTRUCT_CALLOC (stage, MERROR_DRAW);
811 MLIST_INIT1 (stage, cmds, 32);
812 dummy.type = FontLayoutCmdTypeMAX;
813 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
814 MPLIST_DO (elt, MPLIST_NEXT (plist))
816 if (! MPLIST_PLIST_P (elt))
817 MERROR (MERROR_FONT, NULL);
818 pl = MPLIST_PLIST (elt);
819 if (! MPLIST_SYMBOL_P (pl))
820 MERROR (MERROR_FONT, NULL);
821 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
824 /* Load the first command from PLIST into STAGE->cmds[0]. Macros
825 called in the first command are also loaded from MPLIST_NEXT
826 (PLIST) into STAGE->cmds[n]. */
827 if (load_command (stage, plist, MPLIST_NEXT (plist), INDEX_TO_CMD_ID (0))
830 MLIST_FREE1 (stage, cmds);
832 MERROR (MERROR_DRAW, NULL);
839 /* Load FLT of name LAYOUTER_NAME from the m17n database into a newly
840 allocated memory, and return it. */
842 static MFontLayoutTable *
843 load_flt (MSymbol layouter_name)
846 MPlist *top = NULL, *plist;
847 MSymbol Mcategory = msymbol ("category");
848 MSymbol Mgenerator = msymbol ("generator");
849 MSymbol Mend = msymbol ("end");
850 MFontLayoutTable *layouter = NULL;
851 MCharTable *category = NULL;
853 if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
854 MERROR_GOTO (MERROR_FONT, finish);
855 if (! (top = (MPlist *) mdatabase_load (mdb)))
856 MERROR_GOTO (0, finish);
857 if (! MPLIST_PLIST_P (top))
858 MERROR_GOTO (MERROR_FONT, finish);
860 MPLIST_DO (plist, top)
865 if (MPLIST_SYMBOL_P (plist)
866 && MPLIST_SYMBOL (plist) == Mend)
868 if (! MPLIST_PLIST (plist))
869 MERROR_GOTO (MERROR_FONT, finish);
870 elt = MPLIST_PLIST (plist);
871 if (! MPLIST_SYMBOL_P (elt))
872 MERROR_GOTO (MERROR_FONT, finish);
873 sym = MPLIST_SYMBOL (elt);
874 elt = MPLIST_NEXT (elt);
876 MERROR_GOTO (MERROR_FONT, finish);
877 if (sym == Mcategory)
880 M17N_OBJECT_UNREF (category);
881 category = load_category_table (elt);
883 else if (sym == Mgenerator)
885 FontLayoutStage *stage;
888 MERROR_GOTO (MERROR_FONT, finish);
889 stage = load_generator (elt);
891 MERROR_GOTO (MERROR_FONT, finish);
892 stage->category = category;
893 M17N_OBJECT_REF (category);
896 layouter = mplist ();
897 /* Here don't do M17N_OBJECT_REF (category) because we
898 don't unref the value of the element added below. */
899 mplist_add (layouter, Mcategory, category);
901 mplist_add (layouter, Mt, stage);
904 MERROR_GOTO (MERROR_FONT, finish);
908 M17N_OBJECT_UNREF (category);
911 M17N_OBJECT_UNREF (top);
912 mplist_add (flt_list, layouter_name, layouter);
918 free_flt_stage (FontLayoutStage *stage)
922 M17N_OBJECT_UNREF (stage->category);
923 for (i = 0; i < stage->used; i++)
924 free_flt_command (stage->cmds + i);
925 MLIST_FREE1 (stage, cmds);
930 static MFontLayoutTable *
931 get_font_layout_table (MSymbol layouter_name)
933 MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
935 return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
939 /* FLS (Font Layout Service) */
941 /* Structure to hold information about a context of FLS. */
945 /* Pointer to the current stage. */
946 FontLayoutStage *stage;
948 /* Encode each MGlyph->code by the current category table into this
949 array. An element is a category. */
951 /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
956 int cluster_begin_idx;
957 int cluster_begin_pos;
963 static int run_command (int depth,
964 int, MGlyphString *, int, int, FontLayoutContext *);
970 FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
971 FontLayoutContext *ctx)
973 int *saved_match_indices = ctx->match_indices;
974 int match_indices[NMATCH * 2];
977 int orig_from = from;
979 if (rule->src_type == SRC_SEQ)
983 len = rule->src.seq.n_codes;
984 if (len > (to - from))
986 for (i = 0; i < len; i++)
987 if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
992 MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "",
993 rule->src.seq.codes[0]);
995 else if (rule->src_type == SRC_RANGE)
1001 head = gstring->glyphs[from].code;
1002 if (head < rule->src.range.from || head > rule->src.range.to)
1004 ctx->code_offset = head - rule->src.range.from;
1006 MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "",
1007 rule->src.range.from, rule->src.range.to);
1009 else if (rule->src_type == SRC_REGEX)
1011 regmatch_t pmatch[NMATCH];
1017 saved_code = ctx->encoded[to - ctx->encoded_offset];
1018 ctx->encoded[to - ctx->encoded_offset] = '\0';
1019 result = regexec (&(rule->src.re.preg),
1020 ctx->encoded + from - ctx->encoded_offset,
1022 if (result == 0 && pmatch[0].rm_so == 0)
1024 MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "",
1025 rule->src.re.pattern,
1026 ctx->encoded + from - ctx->encoded_offset,
1028 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1029 for (i = 0; i < NMATCH; i++)
1031 if (pmatch[i].rm_so < 0)
1032 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
1035 match_indices[i * 2] = from + pmatch[i].rm_so;
1036 match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1039 ctx->match_indices = match_indices;
1040 to = match_indices[1];
1044 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1048 else if (rule->src_type == SRC_INDEX)
1050 if (rule->src.match_idx >= NMATCH)
1052 from = ctx->match_indices[rule->src.match_idx * 2];
1055 to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1056 MDEBUG_PRINT3 ("\n [FLT] %*s(INDEX %d", depth, "", rule->src.match_idx);
1061 for (i = 0; i < rule->n_cmds; i++)
1065 if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1071 pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1073 MERROR (MERROR_DRAW, -1);
1074 consumed = pos > from;
1079 ctx->match_indices = saved_match_indices;
1081 return (rule->src_type == SRC_INDEX ? orig_from : to);
1085 run_cond (int depth,
1086 FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1087 FontLayoutContext *ctx)
1091 MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1093 for (i = 0; i < cond->n_cmds; i++)
1095 /* TODO: Write a code for optimization utilizaing the info
1097 if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1102 MERROR (MERROR_DRAW, -1);
1109 MFontCapability *otf_cmd, MGlyphString *gstring, int from, int to,
1110 FontLayoutContext *ctx)
1113 int from_idx = gstring->used;
1115 MDEBUG_PRINT4 ("\n [FLT] %*s(OTF %s,%s)", depth, "",
1116 (! otf_cmd->features[MFONT_OTT_GSUB].str ? ""
1117 : otf_cmd->features[MFONT_OTT_GSUB].str),
1118 (! otf_cmd->features[MFONT_OTT_GPOS].str ? ""
1119 : otf_cmd->features[MFONT_OTT_GPOS].str));
1120 to = mfont__ft_drive_otf (gstring, from, to, otf_cmd);
1121 if (ctx->cluster_begin_idx)
1122 for (; from_idx < gstring->used; from_idx++)
1123 UPDATE_CLUSTER_RANGE (ctx, gstring->glyphs[from_idx]);
1128 extern char *dump_combining_code (int code);
1131 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1132 FontLayoutContext *ctx)
1140 /* Direct code (== id + ctx->code_offset) output.
1141 The source is not consumed. */
1143 g = *(MGLYPH (from));
1145 g = *(MGLYPH (from - 1));
1146 g.type = GLYPH_CHAR;
1147 g.code = ctx->code_offset + id;
1148 MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "", g.code);
1149 if (ctx->combining_code)
1150 g.combining_code = ctx->combining_code;
1151 if (ctx->left_padding)
1152 g.left_padding = ctx->left_padding;
1153 for (i = from; i < to; i++)
1155 MGlyph *tmp = MGLYPH (i);
1157 if (g.pos > tmp->pos)
1159 else if (g.to < tmp->to)
1162 APPEND_GLYPH (gstring, g);
1163 UPDATE_CLUSTER_RANGE (ctx, 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);
1192 MDEBUG_PRINT3 ("\n [FLT] %*s(CMB %s)", depth, "",
1193 dump_combining_code (ctx->combining_code));
1203 g = *(MGLYPH (from));
1204 if (ctx->combining_code)
1205 g.combining_code = ctx->combining_code;
1206 if (ctx->left_padding)
1207 g.left_padding = ctx->left_padding;
1208 APPEND_GLYPH (gstring, g);
1209 UPDATE_CLUSTER_RANGE (ctx, g);
1210 MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g.code);
1211 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1215 case CMD_ID_CLUSTER_BEGIN:
1216 if (! ctx->cluster_begin_idx)
1218 MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "", MGLYPH (from)->pos);
1219 ctx->cluster_begin_idx = gstring->used;
1220 ctx->cluster_begin_pos = MGLYPH (from)->pos;
1221 ctx->cluster_end_pos = MGLYPH (from)->to;
1225 case CMD_ID_CLUSTER_END:
1226 if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1230 MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1231 for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1233 MGLYPH (i)->pos = ctx->cluster_begin_pos;
1234 MGLYPH (i)->to = ctx->cluster_end_pos;
1236 ctx->cluster_begin_idx = 0;
1240 case CMD_ID_SEPARATOR:
1243 g = *(MGLYPH (from));
1245 g = *(MGLYPH (from - 1));
1247 /* g.c = g.code = 0; */
1249 APPEND_GLYPH (gstring, g);
1253 case CMD_ID_LEFT_PADDING:
1254 MDEBUG_PRINT2 ("\n [FLT] %*s[", depth, "");
1255 ctx->left_padding = 1;
1258 case CMD_ID_RIGHT_PADDING:
1259 if (gstring->used > 0)
1261 MDEBUG_PRINT2 ("\n [FLT] %*s]", depth, "");
1262 gstring->glyphs[gstring->used - 1].right_padding = 1;
1267 MERROR (MERROR_DRAW, -1);
1274 mfont__flt_init (void)
1276 Mcond = msymbol ("cond");
1277 Mrange = msymbol ("range");
1278 Mlayouter = msymbol ("layouter");
1279 flt_list = mplist ();
1284 mfont__flt_fini (void)
1288 MPLIST_DO (plist, flt_list)
1290 pl = MPLIST_PLIST (plist);
1293 MPLIST_DO (pl, MPLIST_NEXT (pl))
1294 free_flt_stage (MPLIST_VAL (pl));
1295 pl = MPLIST_PLIST (plist);
1296 M17N_OBJECT_UNREF (pl);
1299 M17N_OBJECT_UNREF (flt_list);
1303 mfont__flt_encode_char (MSymbol layouter_name, int c)
1305 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1310 return MCHAR_INVALID_CODE;
1311 table = MPLIST_VAL (layouter);
1312 code = (unsigned) mchartable_lookup (table, c);
1313 return (code ? code : MCHAR_INVALID_CODE);
1317 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1322 FontLayoutContext ctx;
1325 int match_indices[NMATCH];
1326 MSymbol layouter_name = rface->layouter;
1327 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1328 MRealizedFace *ascii_rface = rface->ascii_rface;
1329 FontLayoutStage *stage;
1330 int from_pos, to_pos;
1335 /* FLT not found. Make all glyphs invisible. */
1337 gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1341 dummy = gstring->glyphs[from];
1342 MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1345 memset (&ctx, 0, sizeof ctx);
1346 table = MPLIST_VAL (layouter);
1347 layouter = MPLIST_NEXT (layouter);
1348 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1350 /* Find previous glyphs that are also supported by the layouter. */
1352 && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1354 /* + 2 is for a separator ' ' and a terminator '\0'. */
1355 encoded_len = gstring->used - gidx + 2;
1356 ctx.encoded = (char *) alloca (encoded_len);
1358 for (i = 0; gidx < from; i++, gidx++)
1359 ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1361 ctx.encoded[i++] = ' ';
1362 ctx.encoded_offset = from - i;
1364 /* Now each MGlyph->code contains encoded char. Set it in
1365 ctx.encoded[], and set MGlyph->c to MGlyph->code. */
1366 for (gidx = from; gidx < to ; i++, gidx++)
1368 ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1369 MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1371 ctx.encoded[i++] = '\0';
1373 match_indices[0] = from;
1374 match_indices[1] = to;
1375 for (i = 2; i < NMATCH; i++)
1376 match_indices[i] = -1;
1377 ctx.match_indices = match_indices;
1379 from_pos = MGLYPH (from)->pos;
1380 to_pos = MGLYPH (to)->pos;
1382 for (stage_idx = 0; 1; stage_idx++)
1384 int len = to - from;
1387 ctx.code_offset = ctx.combining_code = ctx.left_padding = 0;
1388 MDEBUG_PRINT2 ("\n [FLT] (STAGE %d \"%s\"", stage_idx, ctx.encoded);
1389 if (mdebug__flag & mdebug_mask
1390 && ctx.encoded_offset < to)
1392 if (gstring->glyphs[ctx.encoded_offset].type == GLYPH_PAD)
1393 fprintf (stderr, " (|");
1395 fprintf (stderr, " (%X", gstring->glyphs[ctx.encoded_offset].code);
1396 for (i = ctx.encoded_offset + 1; i < to; i++)
1398 if (gstring->glyphs[i].type == GLYPH_PAD)
1399 fprintf (stderr, " |");
1401 fprintf (stderr, " %X", gstring->glyphs[i].code);
1403 fprintf (stderr, ")");
1406 gidx = gstring->used;
1409 result = run_command (4, INDEX_TO_CMD_ID (0), gstring,
1410 ctx.encoded_offset, to, &ctx);
1414 to = from + (gstring->used - gidx);
1415 REPLACE_GLYPHS (gstring, gidx, from, len);
1417 layouter = MPLIST_NEXT (layouter);
1418 /* If this is the last stage, break the loop. */
1419 if (MPLIST_TAIL_P (layouter))
1422 /* Otherwise, prepare for the next iteration. */
1423 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1424 table = stage->category;
1425 if (to - from >= encoded_len)
1427 encoded_len = to + 1;
1428 ctx.encoded = (char *) alloca (encoded_len);
1431 for (i = from; i < to; i++)
1433 MGlyph *g = MGLYPH (i);
1435 if (g->type == GLYPH_PAD)
1436 ctx.encoded[i - from] = ' ';
1437 else if (! g->otf_encoded)
1438 ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1439 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1442 int c = mfont__ft_decode_otf (g);
1446 c = (int) mchartable_lookup (table, c);
1450 ctx.encoded[i - from] = (c >= 0 ? c : 1);
1452 #endif /* HAVE_FREETYPE && HAVE_OTF */
1454 ctx.encoded[i - from] = '\0';
1455 ctx.encoded_offset = from;
1456 ctx.match_indices[0] = from;
1457 ctx.match_indices[1] = to;
1462 /* Somehow there's no glyph contributing to characters between
1463 FROM_POS and TO_POS. We must add one dummy space glyph for
1464 those characters. */
1467 g.type = GLYPH_SPACE;
1468 g.c = ' ', g.code = ' ';
1469 g.pos = from_pos, g.to = to_pos;
1470 g.rface = ascii_rface;
1471 INSERT_GLYPH (gstring, from, g);
1476 /* Get actual glyph IDs of glyphs. Also check if all characters
1477 in the range is covered by some glyph(s). If not, change
1478 <pos> and <to> of glyphs to cover uncovered characters. */
1479 int len = to_pos - from_pos;
1481 MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1482 MGlyph *g, *gend = MGLYPH (to);
1483 MGlyph *latest = gend;
1485 for (g = MGLYPH (from); g != gend; g++)
1486 if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1487 g->code = ((rface->rfont->driver->encode_char)
1488 (NULL, (MFont *) rface->rfont, NULL, g->code));
1489 for (i = 0; i < len; i++)
1491 for (g = MGLYPH (from); g != gend; g++)
1493 if (g->pos < latest->pos)
1495 if (! glyphs[g->pos - from_pos])
1497 for (i = g->pos; i < g->to; i++)
1498 glyphs[i - from_pos] = g;
1505 for (g = latest; g->pos == pos; g++)
1509 for (; i < len; i++)
1513 for (g = latest; g->pos == latest->pos; g++)
1514 g->to = from_pos + i + 1;
1520 MDEBUG_PRINT ("\n [FLT] (RESULT (");
1521 if (mdebug__flag & mdebug_mask
1522 && ctx.encoded_offset < to)
1524 if (gstring->glyphs[from].type == GLYPH_PAD)
1525 fprintf (stderr, "|");
1527 fprintf (stderr, "%X", gstring->glyphs[from].code);
1528 for (from++; from < to; from++)
1530 if (gstring->glyphs[from].type == GLYPH_PAD)
1531 fprintf (stderr, " |");
1533 fprintf (stderr, " %X", gstring->glyphs[from].code);
1535 fprintf (stderr, "))");
1537 MDEBUG_PRINT (")\n");
1543 /* for debugging... */
1546 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1548 char *prefix = (char *) alloca (indent + 1);
1550 memset (prefix, 32, indent);
1554 fprintf (stderr, "0x%02X", id);
1555 else if (id <= CMD_ID_OFFSET_INDEX)
1557 int idx = CMD_ID_TO_INDEX (id);
1558 FontLayoutCmd *cmd = stage->cmds + idx;
1560 if (cmd->type == FontLayoutCmdTypeRule)
1562 FontLayoutCmdRule *rule = &cmd->body.rule;
1565 fprintf (stderr, "(rule ");
1566 if (rule->src_type == SRC_REGEX)
1567 fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1568 else if (rule->src_type == SRC_INDEX)
1569 fprintf (stderr, "%d", rule->src.match_idx);
1570 else if (rule->src_type == SRC_SEQ)
1571 fprintf (stderr, "(seq)");
1572 else if (rule->src_type == SRC_RANGE)
1573 fprintf (stderr, "(range)");
1575 fprintf (stderr, "(invalid src)");
1577 for (i = 0; i < rule->n_cmds; i++)
1579 fprintf (stderr, "\n%s ", prefix);
1580 dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1582 fprintf (stderr, ")");
1584 else if (cmd->type == FontLayoutCmdTypeCond)
1586 FontLayoutCmdCond *cond = &cmd->body.cond;
1589 fprintf (stderr, "(cond");
1590 for (i = 0; i < cond->n_cmds; i++)
1592 fprintf (stderr, "\n%s ", prefix);
1593 dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1595 fprintf (stderr, ")");
1597 else if (cmd->type == FontLayoutCmdTypeOTF)
1599 fprintf (stderr, "(otf)");
1602 fprintf (stderr, "(error-command)");
1604 else if (id <= CMD_ID_OFFSET_COMBINING)
1605 fprintf (stderr, "cominging-code");
1607 fprintf (stderr, "(predefiend %d)", id);
1611 dump_flt (MFontLayoutTable *flt, int indent)
1613 char *prefix = (char *) alloca (indent + 1);
1617 memset (prefix, 32, indent);
1619 fprintf (stderr, "(flt");
1620 MPLIST_DO (plist, flt)
1622 FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1625 fprintf (stderr, "\n%s (stage %d", prefix, stage_idx);
1626 for (i = 0; i < stage->used; i++)
1628 fprintf (stderr, "\n%s ", prefix);
1629 dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1631 fprintf (stderr, ")");
1634 fprintf (stderr, ")");