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,
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 ctx->left_padding = 1;
1257 case CMD_ID_RIGHT_PADDING:
1258 if (gstring->used > 0)
1259 gstring->glyphs[gstring->used - 1].right_padding = 1;
1263 MERROR (MERROR_DRAW, -1);
1270 mfont__flt_init (void)
1272 Mcond = msymbol ("cond");
1273 Mrange = msymbol ("range");
1274 Mlayouter = msymbol ("layouter");
1275 flt_list = mplist ();
1280 mfont__flt_fini (void)
1284 MPLIST_DO (plist, flt_list)
1286 pl = MPLIST_PLIST (plist);
1289 MPLIST_DO (pl, MPLIST_NEXT (pl))
1290 free_flt_stage (MPLIST_VAL (pl));
1291 pl = MPLIST_PLIST (plist);
1292 M17N_OBJECT_UNREF (pl);
1295 M17N_OBJECT_UNREF (flt_list);
1299 mfont__flt_encode_char (MSymbol layouter_name, int c)
1301 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1306 return MCHAR_INVALID_CODE;
1307 table = MPLIST_VAL (layouter);
1308 code = (unsigned) mchartable_lookup (table, c);
1309 return (code ? code : MCHAR_INVALID_CODE);
1313 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1318 FontLayoutContext ctx;
1321 int match_indices[NMATCH];
1322 MSymbol layouter_name = rface->layouter;
1323 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1324 MRealizedFace *ascii_rface = rface->ascii_rface;
1325 FontLayoutStage *stage;
1326 int from_pos, to_pos;
1331 /* FLT not found. Make all glyphs invisible. */
1333 gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1337 dummy = gstring->glyphs[from];
1338 MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1341 memset (&ctx, 0, sizeof ctx);
1342 table = MPLIST_VAL (layouter);
1343 layouter = MPLIST_NEXT (layouter);
1344 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1346 /* Find previous glyphs that are also supported by the layouter. */
1348 && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1350 /* + 2 is for a separator ' ' and a terminator '\0'. */
1351 encoded_len = gstring->used - gidx + 2;
1352 ctx.encoded = (char *) alloca (encoded_len);
1354 for (i = 0; gidx < from; i++, gidx++)
1355 ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1357 ctx.encoded[i++] = ' ';
1358 ctx.encoded_offset = from - i;
1360 /* Now each MGlyph->code contains encoded char. Set it in
1361 ctx.encoded[], and set MGlyph->c to MGlyph->code. */
1362 for (gidx = from; gidx < to ; i++, gidx++)
1364 ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1365 MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1367 ctx.encoded[i++] = '\0';
1369 match_indices[0] = from;
1370 match_indices[1] = to;
1371 for (i = 2; i < NMATCH; i++)
1372 match_indices[i] = -1;
1373 ctx.match_indices = match_indices;
1375 from_pos = MGLYPH (from)->pos;
1376 to_pos = MGLYPH (to)->pos;
1378 for (stage_idx = 0; 1; stage_idx++)
1380 int len = to - from;
1383 ctx.code_offset = ctx.combining_code = ctx.left_padding = 0;
1384 MDEBUG_PRINT2 ("\n [FLT] (STAGE %d \"%s\"", stage_idx, ctx.encoded);
1385 if (mdebug__flag & mdebug_mask
1386 && ctx.encoded_offset < to)
1388 if (gstring->glyphs[ctx.encoded_offset].type == GLYPH_PAD)
1389 fprintf (stderr, " (|");
1391 fprintf (stderr, " (%X", gstring->glyphs[ctx.encoded_offset].code);
1392 for (i = ctx.encoded_offset + 1; i < to; i++)
1394 if (gstring->glyphs[i].type == GLYPH_PAD)
1395 fprintf (stderr, " |");
1397 fprintf (stderr, " %X", gstring->glyphs[i].code);
1399 fprintf (stderr, ")");
1402 gidx = gstring->used;
1405 result = run_command (4, INDEX_TO_CMD_ID (0), gstring,
1406 ctx.encoded_offset, to, &ctx);
1410 to = from + (gstring->used - gidx);
1411 REPLACE_GLYPHS (gstring, gidx, from, len);
1413 layouter = MPLIST_NEXT (layouter);
1414 /* If this is the last stage, break the loop. */
1415 if (MPLIST_TAIL_P (layouter))
1418 /* Otherwise, prepare for the next iteration. */
1419 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1420 table = stage->category;
1421 if (to - from >= encoded_len)
1423 encoded_len = to + 1;
1424 ctx.encoded = (char *) alloca (encoded_len);
1427 for (i = from; i < to; i++)
1429 MGlyph *g = MGLYPH (i);
1431 if (g->type == GLYPH_PAD)
1432 ctx.encoded[i - from] = ' ';
1433 else if (! g->otf_encoded)
1434 ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1435 #if defined (HAVE_FREETYPE) && defined (HAVE_OTF)
1438 int c = mfont__ft_decode_otf (g);
1442 c = (int) mchartable_lookup (table, c);
1446 ctx.encoded[i - from] = (c >= 0 ? c : 1);
1448 #endif /* HAVE_FREETYPE && HAVE_OTF */
1450 ctx.encoded[i - from] = '\0';
1451 ctx.encoded_offset = from;
1452 ctx.match_indices[0] = from;
1453 ctx.match_indices[1] = to;
1458 /* Somehow there's no glyph contributing to characters between
1459 FROM_POS and TO_POS. We must add one dummy space glyph for
1460 those characters. */
1463 g.type = GLYPH_SPACE;
1464 g.c = ' ', g.code = ' ';
1465 g.pos = from_pos, g.to = to_pos;
1466 g.rface = ascii_rface;
1467 INSERT_GLYPH (gstring, from, g);
1472 /* Get actual glyph IDs of glyphs. Also check if all characters
1473 in the range is covered by some glyph(s). If not, change
1474 <pos> and <to> of glyphs to cover uncovered characters. */
1475 int len = to_pos - from_pos;
1477 MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1478 MGlyph *g, *gend = MGLYPH (to);
1479 MGlyph *latest = gend;
1481 for (g = MGLYPH (from); g != gend; g++)
1482 if (g->type == GLYPH_CHAR && ! g->otf_encoded)
1483 g->code = ((rface->rfont->driver->encode_char)
1484 (NULL, (MFont *) rface->rfont, NULL, g->code));
1485 for (i = 0; i < len; i++)
1487 for (g = MGLYPH (from); g != gend; g++)
1489 if (g->pos < latest->pos)
1491 if (! glyphs[g->pos - from_pos])
1493 for (i = g->pos; i < g->to; i++)
1494 glyphs[i - from_pos] = g;
1501 for (g = latest; g->pos == pos; g++)
1505 for (; i < len; i++)
1509 for (g = latest; g->pos == latest->pos; g++)
1510 g->to = from_pos + i + 1;
1516 MDEBUG_PRINT ("\n [FLT] (RESULT (");
1517 if (mdebug__flag & mdebug_mask
1518 && ctx.encoded_offset < to)
1520 if (gstring->glyphs[from].type == GLYPH_PAD)
1521 fprintf (stderr, "|");
1523 fprintf (stderr, "%X", gstring->glyphs[from].code);
1524 for (from++; from < to; from++)
1526 if (gstring->glyphs[from].type == GLYPH_PAD)
1527 fprintf (stderr, " |");
1529 fprintf (stderr, " %X", gstring->glyphs[from].code);
1531 fprintf (stderr, "))");
1533 MDEBUG_PRINT (")\n");
1539 /* for debugging... */
1542 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1544 char *prefix = (char *) alloca (indent + 1);
1546 memset (prefix, 32, indent);
1550 fprintf (stderr, "0x%02X", id);
1551 else if (id <= CMD_ID_OFFSET_INDEX)
1553 int idx = CMD_ID_TO_INDEX (id);
1554 FontLayoutCmd *cmd = stage->cmds + idx;
1556 if (cmd->type == FontLayoutCmdTypeRule)
1558 FontLayoutCmdRule *rule = &cmd->body.rule;
1561 fprintf (stderr, "(rule ");
1562 if (rule->src_type == SRC_REGEX)
1563 fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1564 else if (rule->src_type == SRC_INDEX)
1565 fprintf (stderr, "%d", rule->src.match_idx);
1566 else if (rule->src_type == SRC_SEQ)
1567 fprintf (stderr, "(seq)");
1568 else if (rule->src_type == SRC_RANGE)
1569 fprintf (stderr, "(range)");
1571 fprintf (stderr, "(invalid src)");
1573 for (i = 0; i < rule->n_cmds; i++)
1575 fprintf (stderr, "\n%s ", prefix);
1576 dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1578 fprintf (stderr, ")");
1580 else if (cmd->type == FontLayoutCmdTypeCond)
1582 FontLayoutCmdCond *cond = &cmd->body.cond;
1585 fprintf (stderr, "(cond");
1586 for (i = 0; i < cond->n_cmds; i++)
1588 fprintf (stderr, "\n%s ", prefix);
1589 dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1591 fprintf (stderr, ")");
1593 else if (cmd->type == FontLayoutCmdTypeOTF)
1595 fprintf (stderr, "(otf)");
1598 fprintf (stderr, "(error-command)");
1600 else if (id <= CMD_ID_OFFSET_COMBINING)
1601 fprintf (stderr, "cominging-code");
1603 fprintf (stderr, "(predefiend %d)", id);
1607 dump_flt (MFontLayoutTable *flt, int indent)
1609 char *prefix = (char *) alloca (indent + 1);
1613 memset (prefix, 32, indent);
1615 fprintf (stderr, "(flt");
1616 MPLIST_DO (plist, flt)
1618 FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1621 fprintf (stderr, "\n%s (stage %d", prefix, stage_idx);
1622 for (i = 0; i < stage->used; i++)
1624 fprintf (stderr, "\n%s ", prefix);
1625 dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1627 fprintf (stderr, ")");
1630 fprintf (stderr, ")");