1 /* font-flt.c -- Font Layout Table sub-module.
2 Copyright (C) 2003, 2004
3 National Institute of Advanced Industrial Science and Technology (AIST)
4 Registration Number H15PRO112
6 This file is part of the m17n library.
8 The m17n library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public License
10 as published by the Free Software Foundation; either version 2.1 of
11 the License, or (at your option) any later version.
13 The m17n library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with the m17n library; if not, write to the Free
20 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
29 #include <sys/types.h>
33 #include "m17n-misc.h"
38 #include "internal-gui.h"
44 /* Font Layout Table (FLT)
46 Predefined terms: SYMBOL, INTEGER, STRING
48 FLT ::= '(' STAGE + ')'
50 STAGE ::= CATEGORY-TABLE ? FONT-LAYOUT-RULE
52 ;; Each STAGE consumes a source (code sequence) and produces another
53 ;; code sequence that is given to the next STAGE as a source. The
54 ;; source given to the first stage is a sequence of character codes
55 ;; that are assigned category codes by CATEGORY-TABLE. The output of
56 ;; the last stage is a glyph code sequence given to the renderer.
59 '(' 'category' CATEGORY-SPEC + ')'
61 '(' CODE [ CODE ] CATEGORY ')'
64 ;; ASCII character codes of alphabet ('A' .. 'Z' 'a' .. 'z').
67 ;; (0x0900 0x097F ?E) ; All Devanagari characters
68 ;; (0x093C ?N)) ; DEVANAGARI-LETTER NUKTA
69 ;; Assign the category 'E' to all Devanagari characters but 0x093C,
70 ;; assign the category 'N' to 0x093C.
73 '(' 'generator' RULE MACRO-DEF * ')'
75 RULE ::= COMMAND | REGEXP-RULE | MATCH-RULE | MAP-RULE
76 | COND-STRUCT | MACRO-NAME
79 DIRECT-CODE | COMBINING | PREDEFIND-COMMAND | OTF-COMMAND
81 DIRECT-CODE ::= INTEGER
82 ;; Always succeed. Produce the code. Consume no source.
85 '=' | '*' | '<' | '>' | '|'
87 ;; '=': Succeed when the current run contains at least one code.
88 ;; Consume the first code in the current run, and produce it as is.
90 ;; '*': If the the previous command succeeded, repeat it until it
93 ;; '<': Produce a special code that indicates the start of grapheme
94 ;; cluster. Succeed always, consume nothing.
96 ;; '>': Produce a special code that indicates the end of grapheme
97 ;; cluster. Succeed always, consume nothing.
99 ;; '|': Produce a special code whose category is ' '. Succeed always,
103 'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]]
104 ;; Run the Open Type Layout Table on the current run. Succeed always,
108 ;; OTF's ScriptTag name (four letters) listed at:
109 ;; <http://www.microsoft.om/typograph/otspec/scripttags.htm>
111 ;; OTF's Language System name (four letters) listed at:
112 ;; <http://www.microsoft.om/typograph/otspec/languagetags.htm>
114 GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
115 GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
117 ;; OTF's Feature name (four letters) listed at:
118 ;; <http://www.microsoft.om/typograph/otspec/???.htm>
120 OTF-TAG ::= PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR
124 ;; Run all features in the default langsys of 'deva' script.
125 ;; 'otf:deva::nukt:haln'
126 ;; Run all GSUB features, run 'nukt' and 'haln' GPOS features.
128 ;; Run all GSUB features, run no GPOS features.
131 '(' REGEXP RULE * ')'
133 ;; Succeed if REGXP matches the head of source. Run RULEs while
134 ;; limiting the source to the matching part. Consume that part.
137 ;; Must be composed only from ASCII characters. 'A' - 'Z', 'a' - 'z'
138 ;; correspond to CATEGORY.
145 '(' MATCH-IDX RULE * ')'
147 ;; Succeed if the previous REGEXP-RULE found a matching part for
148 ;; MATCH-IDX. Run RULEs while limiting the source to the matching
149 ;; part. If MATCH-IDX is zero, consume the whole part, else consume
152 MATCH-IDX ::= INTEGER
159 '(' ( SOURCE-SEQ | SOURCE-RANGE ) RULE * ')'
161 ;; Succeed if the source matches SOURCE-SEQ or SOURCE-RANGE. Run
162 ;; RULEs while limiting the source to the matching part. Consume that
168 '(' 'range' CODE CODE ')'
170 ;; ((0x0915 0x094D) 0x43)
171 ;; If the source code sequence is 0x0915 0x094D, produce 0x43.
172 ;; ((range 0x0F40 0x0F6A) 0x2221)
173 ;; If the first source code CODE is in the range 0x0F40..0x0F6A,
174 ;; produce (0x2221 + (CODE - 0x0F40)).
177 '(' 'cond' RULE + ')'
179 ;; Try each rule in sequence until one succeeds. Succeed if one
180 ;; succeeds. Consume nothing.
184 ;; ((0x0915 0x094D) 0x43)
185 ;; ((range 0x0F40 0x0F6A) 0x2221)
188 COMBINING ::= 'V''H''O''V''H'
189 V ::= ( 't' | 'c' | 'b' | 'B' )
190 H ::= ( 'l' | 'c' | 'r' )
191 O ::= ( '.' | XOFF | YOFF | XOFF YOFF )
192 XOFF ::= '<'INTEGER | '>'INTEGER
193 YOFF ::= '+'INTEGER | '-'INTEGER
194 ;; INTEGER must be integer 0..127
196 ;; VH pair indicates 12 reference points of a glyph as below:
198 ;; 0----1----2 <---- ascent 0:tl (top-left)
199 ;; | | 1:tc (top-center)
200 ;; | | 2:tr (top-right)
201 ;; | | 3:Bl (base-left)
202 ;; 9 10 11 <---- center 4:Bc (base-center)
203 ;; | | 5:Br (base-right)
204 ;; --3----4----5-- <-- baseline 6:bl (bottom-left)
205 ;; | | 7:bc (bottom-center)
206 ;; 6----7----8 <---- descent 8:br (bottom-right)
207 ;; 9:cl (center-left)
208 ;; | | | 10:cc (center-center)
209 ;; left center right 11:cr (center-right)
213 ;; Align top-left point of the previous glyph and bottom-center
214 ;; point of the current glyph.
216 ;; Align 20% left and 10% below of base-left point of the previous
217 ;; glyph and base-right point of the current glyph.
220 '(' MACRO-NAME RULE + ')'
221 MACRO-NAME ::= SYMBOL
225 static int mdebug_mask = MDEBUG_FONT_FLT;
229 static MPlist *flt_list;
234 -0x0F .. -2 : builtin commands
235 -0x100000F .. -0x10 : combining code
236 ... -0x1000010: index to FontLayoutStage->cmds
239 #define INVALID_CMD_ID -1
240 #define CMD_ID_OFFSET_BUILTIN -2
241 #define CMD_ID_OFFSET_COMBINING -0x10
242 #define CMD_ID_OFFSET_INDEX -0x1000010
244 /* Builtin commands. */
245 #define CMD_ID_COPY -2 /* '=' */
246 #define CMD_ID_REPEAT -3 /* '*' */
247 #define CMD_ID_CLUSTER_BEGIN -4 /* '<' */
248 #define CMD_ID_CLUSTER_END -5 /* '>' */
249 #define CMD_ID_SEPARATOR -6 /* '|' */
250 #define CMD_ID_LEFT_PADDING -7 /* '[' */
251 #define CMD_ID_RIGHT_PADDING -8 /* ']' */
253 #define CMD_ID_TO_COMBINING_CODE(id) (CMD_ID_OFFSET_COMBINING - (id))
254 #define COMBINING_CODE_TO_CMD_ID(code) (CMD_ID_OFFSET_COMBINING - (code))
256 #define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id))
257 #define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx))
259 static MSymbol Mcond, Mrange;
261 #define GLYPH_CODE_P(code) \
262 ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX)
264 #define GLYPH_CODE_INDEX(code) ((code) - GLYPH_CODE_MIN)
266 enum FontLayoutCmdRuleSrcType
276 enum FontLayoutCmdRuleSrcType src_type;
306 MSymbol gsub_features;
307 MSymbol gpos_features;
310 enum FontLayoutCmdType
312 FontLayoutCmdTypeRule,
313 FontLayoutCmdTypeCond,
314 FontLayoutCmdTypeOTF,
320 enum FontLayoutCmdType type;
322 FontLayoutCmdRule rule;
323 FontLayoutCmdCond cond;
324 FontLayoutCmdOTF otf;
330 MCharTable *category;
335 typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */
337 /* Font layout table loader */
339 /* Load a category table from PLIST. PLIST has this form:
340 PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
344 load_category_table (MPlist *plist)
348 table = mchartable (Minteger, (void *) 0);
350 MPLIST_DO (plist, plist)
353 int from, to, category_code;
355 if (! MPLIST_PLIST (plist))
356 MERROR (MERROR_FONT, NULL);
357 elt = MPLIST_PLIST (plist);
358 if (! MPLIST_INTEGER_P (elt))
359 MERROR (MERROR_FONT, NULL);
360 from = MPLIST_INTEGER (elt);
361 elt = MPLIST_NEXT (elt);
362 if (! MPLIST_INTEGER_P (elt))
363 MERROR (MERROR_FONT, NULL);
364 to = MPLIST_INTEGER (elt);
365 elt = MPLIST_NEXT (elt);
366 if (MPLIST_TAIL_P (elt))
373 if (! MPLIST_INTEGER_P (elt))
374 MERROR (MERROR_FONT, NULL);
375 category_code = MPLIST_INTEGER (elt);
377 if (! isalpha (category_code))
378 MERROR (MERROR_FONT, NULL);
381 mchartable_set (table, from, (void *) category_code);
383 mchartable_set_range (table, from, to, (void *) category_code);
390 /* Parse OTF command name NAME and store the result in CMD.
392 :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]]
393 where GSUB-FEATURES and GPOS-FEATURES have this form:
394 [FEATURE[,FEATURE]*] | ' ' */
397 load_otf_command (FontLayoutCmd *cmd, char *name)
399 char *p = name, *beg;
401 cmd->type = FontLayoutCmdTypeOTF;
402 cmd->body.otf.script = cmd->body.otf.langsys = Mnil;
403 cmd->body.otf.gsub_features = cmd->body.otf.gpos_features = Mt;
409 for (beg = ++p; *p && *p != '/' && *p != '=' && *p != '+'; p++);
411 cmd->body.otf.script = msymbol__with_len (beg, p - beg);
415 for (beg = ++p; *p && *p != '=' && *p != '+'; p++);
417 cmd->body.otf.langsys = msymbol__with_len (beg, p - beg);
421 for (beg = ++p; *p && *p != '+'; p++);
423 cmd->body.otf.gsub_features = msymbol__with_len (beg, p - beg);
425 cmd->body.otf.gsub_features = Mnil;
429 for (beg = ++p; *p && *p != '+'; p++);
431 cmd->body.otf.gpos_features = msymbol__with_len (beg, p - beg);
433 cmd->body.otf.gpos_features = Mnil;
439 return (cmd->body.otf.script == Mnil ? -1 : 0);
443 /* Read a decimal number from STR preceded by one of "+-><". '+' and
444 '>' means a plus sign, '-' and '<' means a minus sign. If the
445 number is greater than 127, limit it to 127. */
448 read_decimal_number (char **str)
451 int sign = (*p == '-' || *p == '<') ? -1 : 1;
455 while (*p >= '0' && *p <= '9')
456 n = n * 10 + *p++ - '0';
460 return (n < 127 ? n * sign : 127 * sign);
464 /* Read a horizontal and vertical combining positions from STR, and
465 store them in the place pointed by X and Y. The horizontal
466 position left, center, and right are represented by 0, 1, and 2
467 respectively. The vertical position top, center, bottom, and base
468 are represented by 0, 1, 2, and 3 respectively. If successfully
469 read, return 0, else return -1. */
472 read_combining_position (char *str, int *x, int *y)
477 /* Vertical position comes first. */
478 for (i = 0; i < 4; i++)
487 /* Then comse horizontal position. */
488 for (i = 0; i < 3; i++)
498 /* Return a combining code corresponding to SYM. */
501 get_combining_command (MSymbol sym)
503 char *str = msymbol_name (sym);
504 int base_x, base_y, add_x, add_y, off_x, off_y;
507 if (read_combining_position (str, &base_x, &base_y) < 0)
518 if (c == '+' || c == '-')
520 off_y = read_decimal_number (&str) + 128;
525 if (c == '<' || c == '>')
526 off_x = read_decimal_number (&str) + 128;
530 if (read_combining_position (str, &add_x, &add_y) < 0)
533 c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x);
534 return (COMBINING_CODE_TO_CMD_ID (c));
538 /* Load a command from PLIST into STAGE, and return that
539 identification number. If ID is not INVALID_CMD_ID, that means we
540 are loading a top level command or a macro. In that case, use ID
541 as the identification number of the command. Otherwise, generate a
542 new id number for the command. MACROS is a list of raw macros. */
545 load_command (FontLayoutStage *stage, MPlist *plist,
546 MPlist *macros, int id)
550 if (MPLIST_INTEGER_P (plist))
552 int code = MPLIST_INTEGER (plist);
555 MERROR (MERROR_DRAW, INVALID_CMD_ID);
558 else if (MPLIST_PLIST_P (plist))
560 /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
561 | ( ( INTEGER INTEGER ) ... )
562 | ( ( range INTEGER INTEGER ) ... ) */
563 MPlist *elt = MPLIST_PLIST (plist);
564 int len = MPLIST_LENGTH (elt) - 1;
567 if (id == INVALID_CMD_ID)
570 id = INDEX_TO_CMD_ID (stage->used);
571 MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW);
573 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
575 if (MPLIST_SYMBOL_P (elt))
577 if (MPLIST_SYMBOL (elt) != Mcond)
578 MERROR (MERROR_DRAW, INVALID_CMD_ID);
579 elt = MPLIST_NEXT (elt);
580 cmd->type = FontLayoutCmdTypeCond;
581 cmd->body.cond.n_cmds = len;
582 MTABLE_CALLOC (cmd->body.cond.cmd_ids, len, MERROR_DRAW);
583 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
585 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
587 if (this_id == INVALID_CMD_ID)
588 MERROR (MERROR_DRAW, INVALID_CMD_ID);
589 /* The above load_command may relocate stage->cmds. */
590 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
591 cmd->body.cond.cmd_ids[i] = this_id;
596 cmd->type = FontLayoutCmdTypeRule;
597 if (MPLIST_MTEXT_P (elt))
599 char *str = (char *) MTEXT_DATA (MPLIST_MTEXT (elt));
601 if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED))
602 MERROR (MERROR_FONT, INVALID_CMD_ID);
603 cmd->body.rule.src_type = SRC_REGEX;
604 cmd->body.rule.src.re.pattern = strdup (str);
606 else if (MPLIST_INTEGER_P (elt))
608 cmd->body.rule.src_type = SRC_INDEX;
609 cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt);
611 else if (MPLIST_PLIST_P (elt))
613 MPlist *pl = MPLIST_PLIST (elt);
614 int size = MPLIST_LENGTH (pl);
616 if (MPLIST_INTEGER_P (pl))
620 cmd->body.rule.src_type = SRC_SEQ;
621 cmd->body.rule.src.seq.n_codes = size;
622 MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size,
624 for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl))
626 if (! MPLIST_INTEGER_P (pl))
627 MERROR (MERROR_DRAW, INVALID_CMD_ID);
628 cmd->body.rule.src.seq.codes[i]
629 = (unsigned) MPLIST_INTEGER (pl);
632 else if (MPLIST_SYMBOL_P (pl) && size == 3)
634 cmd->body.rule.src_type = SRC_RANGE;
635 pl = MPLIST_NEXT (pl);
636 if (! MPLIST_INTEGER_P (pl))
637 MERROR (MERROR_DRAW, INVALID_CMD_ID);
638 cmd->body.rule.src.range.from
639 = (unsigned) MPLIST_INTEGER (pl);
640 pl = MPLIST_NEXT (pl);
641 if (! MPLIST_INTEGER_P (pl))
642 MERROR (MERROR_DRAW, INVALID_CMD_ID);
643 cmd->body.rule.src.range.to
644 = (unsigned) MPLIST_INTEGER (pl);
647 MERROR (MERROR_DRAW, INVALID_CMD_ID);
650 MERROR (MERROR_DRAW, INVALID_CMD_ID);
652 elt = MPLIST_NEXT (elt);
653 cmd->body.rule.n_cmds = len;
654 MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW);
655 for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
657 int this_id = load_command (stage, elt, macros, INVALID_CMD_ID);
659 if (this_id == INVALID_CMD_ID)
660 MERROR (MERROR_DRAW, INVALID_CMD_ID);
661 /* The above load_command may relocate stage->cmds. */
662 cmd = stage->cmds + CMD_ID_TO_INDEX (id);
663 cmd->body.rule.cmd_ids[i] = this_id;
667 else if (MPLIST_SYMBOL_P (plist))
670 MSymbol sym = MPLIST_SYMBOL (plist);
671 char *name = msymbol_name (sym);
672 int len = strlen (name);
676 && ! strncmp (name, "otf:", 4)
677 && load_otf_command (&cmd, name + 3) >= 0)
679 if (id == INVALID_CMD_ID)
681 id = INDEX_TO_CMD_ID (stage->used);
682 MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW);
685 stage->cmds[CMD_ID_TO_INDEX (id)] = cmd;
693 else if (*name == '*')
694 return CMD_ID_REPEAT;
695 else if (*name == '<')
696 return CMD_ID_CLUSTER_BEGIN;
697 else if (*name == '>')
698 return CMD_ID_CLUSTER_END;
699 else if (*name == '|')
700 return CMD_ID_SEPARATOR;
701 else if (*name == '[')
702 return CMD_ID_LEFT_PADDING;
703 else if (*name == ']')
704 return CMD_ID_RIGHT_PADDING;
710 id = get_combining_command (sym);
716 MPLIST_DO (elt, macros)
718 if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt)))
720 id = INDEX_TO_CMD_ID (i);
721 if (stage->cmds[i].type == FontLayoutCmdTypeMAX)
722 id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)),
728 MERROR (MERROR_DRAW, INVALID_CMD_ID);
731 MERROR (MERROR_DRAW, INVALID_CMD_ID);
737 free_flt_command (FontLayoutCmd *cmd)
739 if (cmd->type == FontLayoutCmdTypeRule)
741 FontLayoutCmdRule *rule = &cmd->body.rule;
743 if (rule->src_type == SRC_REGEX)
745 free (rule->src.re.pattern);
746 regfree (&rule->src.re.preg);
748 else if (rule->src_type == SRC_SEQ)
749 free (rule->src.seq.codes);
750 free (rule->cmd_ids);
752 else if (cmd->type == FontLayoutCmdTypeCond)
753 free (cmd->body.cond.cmd_ids);
756 /* Load a generator from PLIST into a newly allocated FontLayoutStage,
757 and return it. PLIST has this form:
758 PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * )
761 static FontLayoutStage *
762 load_generator (MPlist *plist)
764 FontLayoutStage *stage;
768 MSTRUCT_CALLOC (stage, MERROR_DRAW);
769 MLIST_INIT1 (stage, cmds, 32);
770 dummy.type = FontLayoutCmdTypeMAX;
771 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
772 MPLIST_DO (elt, MPLIST_NEXT (plist))
774 if (! MPLIST_PLIST_P (elt))
775 MERROR (MERROR_FONT, NULL);
776 pl = MPLIST_PLIST (elt);
777 if (! MPLIST_SYMBOL_P (pl))
778 MERROR (MERROR_FONT, NULL);
779 MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT);
782 /* Load the first command from PLIST into STAGE->cmds[0]. Macros
783 called in the first command are also loaded from MPLIST_NEXT
784 (PLIST) into STAGE->cmds[n]. */
785 if (load_command (stage, plist, MPLIST_NEXT (plist), INDEX_TO_CMD_ID (0))
788 MLIST_FREE1 (stage, cmds);
790 MERROR (MERROR_DRAW, NULL);
797 /* Load FLT of name LAYOUTER_NAME from the m17n database into a newly
798 allocated memory, and return it. */
800 static MFontLayoutTable *
801 load_flt (MSymbol layouter_name)
804 MPlist *top = NULL, *plist;
805 MSymbol Mcategory = msymbol ("category");
806 MSymbol Mgenerator = msymbol ("generator");
807 MFontLayoutTable *layouter = NULL;
808 MCharTable *category = NULL;
810 if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil)))
811 MERROR_GOTO (MERROR_FONT, finish);
812 if (! (top = (MPlist *) mdatabase_load (mdb)))
813 MERROR_GOTO (0, finish);
814 if (! MPLIST_PLIST_P (top))
815 MERROR_GOTO (MERROR_FONT, finish);
817 MPLIST_DO (plist, top)
822 if (! MPLIST_PLIST (plist))
823 MERROR_GOTO (MERROR_FONT, finish);
824 elt = MPLIST_PLIST (plist);
825 if (! MPLIST_SYMBOL_P (elt))
826 MERROR_GOTO (MERROR_FONT, finish);
827 sym = MPLIST_SYMBOL (elt);
828 elt = MPLIST_NEXT (elt);
830 MERROR_GOTO (MERROR_FONT, finish);
831 if (sym == Mcategory)
834 M17N_OBJECT_UNREF (category);
835 category = load_category_table (elt);
837 else if (sym == Mgenerator)
839 FontLayoutStage *stage;
842 MERROR_GOTO (MERROR_FONT, finish);
843 stage = load_generator (elt);
845 MERROR_GOTO (MERROR_FONT, finish);
846 stage->category = category;
847 M17N_OBJECT_REF (category);
850 layouter = mplist ();
851 /* Here don't do M17N_OBJECT_REF (category) because we
852 don't unref the value of the element added below. */
853 mplist_add (layouter, Mcategory, category);
855 mplist_add (layouter, Mt, stage);
858 MERROR_GOTO (MERROR_FONT, finish);
862 M17N_OBJECT_UNREF (category);
865 M17N_OBJECT_UNREF (top);
866 mplist_add (flt_list, layouter_name, layouter);
872 free_flt_stage (FontLayoutStage *stage)
876 M17N_OBJECT_UNREF (stage->category);
877 for (i = 0; i < stage->used; i++)
878 free_flt_command (stage->cmds + i);
879 MLIST_FREE1 (stage, cmds);
884 static MFontLayoutTable *
885 get_font_layout_table (MSymbol layouter_name)
887 MPlist *plist = mplist_find_by_key (flt_list, layouter_name);
889 return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name));
893 /* FLS (Font Layout Service) */
895 /* Structure to hold information about a context of FLS. */
899 /* Pointer to the current stage. */
900 FontLayoutStage *stage;
902 /* Encode each MGlyph->code by the current category table into this
903 array. An element is a category. */
905 /* <encoded>[GIDX - <encoded_offset>] gives a category for the glyph
910 int cluster_begin_idx;
911 int cluster_begin_pos;
917 static int run_command (int depth,
918 int, MGlyphString *, int, int, FontLayoutContext *);
924 FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
925 FontLayoutContext *ctx)
927 int *saved_match_indices = ctx->match_indices;
928 int match_indices[NMATCH * 2];
931 int orig_from = from;
933 if (ctx->cluster_begin_idx)
935 if (ctx->cluster_begin_pos > MGLYPH (from)->pos)
936 ctx->cluster_begin_pos = MGLYPH (from)->pos;
937 if (ctx->cluster_end_pos < MGLYPH (to)->pos)
938 ctx->cluster_end_pos = MGLYPH (to)->pos;
941 if (rule->src_type == SRC_SEQ)
945 len = rule->src.seq.n_codes;
946 if (len > (to - from))
948 for (i = 0; i < len; i++)
949 if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
954 MDEBUG_PRINT1 (" (SEQ 0x%X", rule->src.seq.codes[0]);
956 else if (rule->src_type == SRC_RANGE)
962 head = gstring->glyphs[from].code;
963 if (head < rule->src.range.from || head > rule->src.range.to)
965 ctx->code_offset = head - rule->src.range.from;
967 MDEBUG_PRINT2 (" (RANGE 0x%X-0x%X",
968 rule->src.range.from, rule->src.range.to);
970 else if (rule->src_type == SRC_REGEX)
972 regmatch_t pmatch[NMATCH];
978 saved_code = ctx->encoded[to - ctx->encoded_offset];
979 ctx->encoded[to - ctx->encoded_offset] = '\0';
980 result = regexec (&(rule->src.re.preg),
981 ctx->encoded + from - ctx->encoded_offset,
983 if (result == 0 && pmatch[0].rm_so == 0)
985 MDEBUG_PRINT3 (" (REGEX \"%s\" \"%s\" %d",
986 rule->src.re.pattern,
987 ctx->encoded + from - ctx->encoded_offset,
989 ctx->encoded[to - ctx->encoded_offset] = saved_code;
990 for (i = 0; i < NMATCH; i++)
992 if (pmatch[i].rm_so < 0)
993 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
996 match_indices[i * 2] = from + pmatch[i].rm_so;
997 match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1000 ctx->match_indices = match_indices;
1001 to = match_indices[1];
1005 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1009 else if (rule->src_type == SRC_INDEX)
1011 if (rule->src.match_idx >= NMATCH)
1013 from = ctx->match_indices[rule->src.match_idx * 2];
1016 to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1017 MDEBUG_PRINT1 (" (INDEX %d", rule->src.match_idx);
1022 for (i = 0; i < rule->n_cmds; i++)
1026 if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1032 pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1034 MERROR (MERROR_DRAW, -1);
1035 consumed = pos > from;
1040 ctx->match_indices = saved_match_indices;
1042 return (rule->src_type == SRC_INDEX ? orig_from : to);
1046 run_cond (int depth,
1047 FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1048 FontLayoutContext *ctx)
1052 MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1054 for (i = 0; i < cond->n_cmds; i++)
1055 if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1059 MERROR (MERROR_DRAW, -1);
1066 FontLayoutCmdOTF *otf_cmd, MGlyphString *gstring, int from, int to,
1067 FontLayoutContext *ctx)
1070 int gidx = gstring->used;
1072 to = mfont__ft_drive_otf (gstring, from, to,
1073 otf_cmd->script, otf_cmd->langsys,
1074 otf_cmd->gsub_features, otf_cmd->gpos_features);
1075 if (gidx < gstring->used)
1076 MGLYPH (gidx)->left_padding = ctx->left_padding;
1082 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1083 FontLayoutContext *ctx)
1091 /* Direct code (== id + ctx->code_offset) output.
1092 The source is not consumed. */
1094 g = *(MGLYPH (from));
1096 g = *(MGLYPH (from - 1));
1097 g.type = GLYPH_CHAR;
1098 g.code = ctx->code_offset + id;
1099 MDEBUG_PRINT1 (" (DIRECT 0x%X", g.code);
1100 if (ctx->combining_code)
1101 g.combining_code = ctx->combining_code;
1102 if (ctx->left_padding)
1103 g.left_padding = ctx->left_padding;
1104 for (i = from; i < to; i++)
1106 MGlyph *tmp = MGLYPH (i);
1108 if (g.pos > tmp->pos)
1110 else if (g.to < tmp->to)
1113 APPEND_GLYPH (gstring, g);
1114 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1119 if (id <= CMD_ID_OFFSET_INDEX)
1121 int idx = CMD_ID_TO_INDEX (id);
1124 if (idx >= ctx->stage->used)
1125 MERROR (MERROR_DRAW, -1);
1126 cmd = ctx->stage->cmds + idx;
1127 if (cmd->type == FontLayoutCmdTypeRule)
1128 to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1129 else if (cmd->type == FontLayoutCmdTypeCond)
1130 to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1131 else if (cmd->type == FontLayoutCmdTypeOTF)
1132 to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1139 if (id <= CMD_ID_OFFSET_COMBINING)
1141 ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1151 g = *(MGLYPH (from));
1152 if (ctx->combining_code)
1153 g.combining_code = ctx->combining_code;
1154 if (ctx->left_padding)
1155 g.left_padding = ctx->left_padding;
1156 APPEND_GLYPH (gstring, g);
1157 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1161 case CMD_ID_CLUSTER_BEGIN:
1162 if (! ctx->cluster_begin_idx)
1164 MDEBUG_PRINT1 (" <%d", MGLYPH (from)->pos);
1165 ctx->cluster_begin_idx = gstring->used;
1166 ctx->cluster_begin_pos = MGLYPH (from)->pos;
1167 ctx->cluster_end_pos = MGLYPH (from)->to;
1171 case CMD_ID_CLUSTER_END:
1172 if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1176 MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1177 for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1179 MGLYPH (i)->pos = ctx->cluster_begin_pos;
1180 MGLYPH (i)->to = ctx->cluster_end_pos;
1182 ctx->cluster_begin_idx = 0;
1186 case CMD_ID_SEPARATOR:
1189 g = *(MGLYPH (from));
1191 g = *(MGLYPH (from - 1));
1193 /* g.c = g.code = 0; */
1195 APPEND_GLYPH (gstring, g);
1199 case CMD_ID_LEFT_PADDING:
1200 ctx->left_padding = 1;
1203 case CMD_ID_RIGHT_PADDING:
1204 if (gstring->used > 0)
1205 gstring->glyphs[gstring->used - 1].right_padding = 1;
1209 MERROR (MERROR_DRAW, -1);
1216 mfont__flt_init (void)
1218 Mcond = msymbol ("cond");
1219 Mrange = msymbol ("range");
1220 Mlayouter = msymbol ("layouter");
1221 flt_list = mplist ();
1226 mfont__flt_fini (void)
1230 MPLIST_DO (plist, flt_list)
1232 pl = MPLIST_PLIST (plist);
1235 MPLIST_DO (pl, MPLIST_NEXT (pl))
1236 free_flt_stage (MPLIST_VAL (pl));
1237 pl = MPLIST_PLIST (plist);
1238 M17N_OBJECT_UNREF (pl);
1241 M17N_OBJECT_UNREF (flt_list);
1245 mfont__flt_encode_char (MSymbol layouter_name, int c)
1247 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1252 return MCHAR_INVALID_CODE;
1253 table = MPLIST_VAL (layouter);
1254 code = (unsigned) mchartable_lookup (table, c);
1255 return (code ? code : MCHAR_INVALID_CODE);
1259 mfont__flt_run (MGlyphString *gstring, int from, int to,
1260 MSymbol layouter_name, MRealizedFace *ascii_rface)
1265 FontLayoutContext ctx;
1268 int match_indices[NMATCH];
1269 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1270 FontLayoutStage *stage;
1271 int from_pos, to_pos;
1276 /* FLT not found. Make all glyphs invisible. */
1278 gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1282 dummy = gstring->glyphs[from];
1283 MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1286 memset (&ctx, 0, sizeof ctx);
1287 table = MPLIST_VAL (layouter);
1288 layouter = MPLIST_NEXT (layouter);
1289 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1291 /* Find previous glyphs that are also supported by the layouter. */
1293 && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1295 /* + 2 is for a separator ' ' and a terminator '\0'. */
1296 encoded_len = gstring->used - gidx + 2;
1297 ctx.encoded = (char *) alloca (encoded_len);
1299 for (i = 0; gidx < from; i++, gidx++)
1300 ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1302 ctx.encoded[i++] = ' ';
1303 ctx.encoded_offset = from - i;
1305 /* Now each MGlyph->code contains encoded char. Set it in
1306 ctx.encoded[], and set MGlyph->c to MGlyph->code. */
1307 for (gidx = from; gidx < to ; i++, gidx++)
1309 ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1310 MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1312 ctx.encoded[i++] = '\0';
1314 match_indices[0] = from;
1315 match_indices[1] = to;
1316 for (i = 2; i < NMATCH; i++)
1317 match_indices[i] = -1;
1318 ctx.match_indices = match_indices;
1320 from_pos = MGLYPH (from)->pos;
1321 to_pos = MGLYPH (to)->pos;
1323 for (stage_idx = 0; 1; stage_idx++)
1325 int len = to - from;
1328 MDEBUG_PRINT1 ("\n [FLT] (STAGE %d", stage_idx);
1329 gidx = gstring->used;
1332 result = run_command (2, INDEX_TO_CMD_ID (0), gstring,
1333 ctx.encoded_offset, to, &ctx);
1337 to = from + (gstring->used - gidx);
1338 REPLACE_GLYPHS (gstring, gidx, from, len);
1340 layouter = MPLIST_NEXT (layouter);
1341 /* If this is the last stage, break the loop. */
1342 if (MPLIST_TAIL_P (layouter))
1345 /* Otherwise, prepare for the next iteration. */
1346 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1347 table = stage->category;
1348 if (to - from >= encoded_len)
1350 encoded_len = to + 1;
1351 ctx.encoded = (char *) alloca (encoded_len);
1354 for (i = from; i < to; i++)
1356 MGlyph *g = MGLYPH (i);
1358 if (g->type == GLYPH_PAD)
1359 ctx.encoded[i - from] = ' ';
1360 else if (! g->otf_encoded)
1361 ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1362 #ifdef HAVE_FREETYPE
1365 int c = mfont__ft_decode_otf (g);
1369 c = (int) mchartable_lookup (table, c);
1373 ctx.encoded[i - from] = (c >= 0 ? c : 1);
1375 #endif /* HAVE_FREETYPE */
1377 ctx.encoded[i - from] = '\0';
1378 ctx.encoded_offset = from;
1379 ctx.match_indices[0] = from;
1380 ctx.match_indices[1] = to;
1383 MDEBUG_PRINT (")\n");
1387 /* Somehow there's no glyph contributing to characters between
1388 FROM_POS and TO_POS. We must add one dummy space glyph for
1389 those characters. */
1392 g.type = GLYPH_SPACE;
1393 g.c = ' ', g.code = ' ';
1394 g.pos = from_pos, g.to = to_pos;
1395 g.rface = ascii_rface;
1396 INSERT_GLYPH (gstring, from, g);
1401 /* Here we must check if all characters in the range is covered
1402 by some glyph(s). If not, change <pos> and <to> of glyphs to
1403 cover uncovered characters. */
1404 int len = to_pos - from_pos;
1406 MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1407 MGlyph *g, *gend = MGLYPH (to);
1408 MGlyph *latest = gend;
1410 for (i = 0; i < len; i++)
1412 for (g = MGLYPH (from); g != gend; g++)
1414 if (g->pos < latest->pos)
1416 if (! glyphs[g->pos - from_pos])
1418 for (i = g->pos; i < g->to; i++)
1419 glyphs[i - from_pos] = g;
1426 for (g = latest; g->pos == pos; g++)
1430 for (; i < len; i++)
1434 for (g = latest; g->pos == latest->pos; g++)
1435 g->to = from_pos + i + 1;
1445 /* for debugging... */
1448 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1450 char *prefix = (char *) alloca (indent + 1);
1452 memset (prefix, 32, indent);
1456 fprintf (stderr, "0x%02X", id);
1457 else if (id <= CMD_ID_OFFSET_INDEX)
1459 int idx = CMD_ID_TO_INDEX (id);
1460 FontLayoutCmd *cmd = stage->cmds + idx;
1462 if (cmd->type == FontLayoutCmdTypeRule)
1464 FontLayoutCmdRule *rule = &cmd->body.rule;
1467 fprintf (stderr, "(rule ");
1468 if (rule->src_type == SRC_REGEX)
1469 fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1470 else if (rule->src_type == SRC_INDEX)
1471 fprintf (stderr, "%d", rule->src.match_idx);
1472 else if (rule->src_type == SRC_SEQ)
1473 fprintf (stderr, "(seq)");
1474 else if (rule->src_type == SRC_RANGE)
1475 fprintf (stderr, "(range)");
1477 fprintf (stderr, "(invalid src)");
1479 for (i = 0; i < rule->n_cmds; i++)
1481 fprintf (stderr, "\n%s ", prefix);
1482 dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1484 fprintf (stderr, ")");
1486 else if (cmd->type == FontLayoutCmdTypeCond)
1488 FontLayoutCmdCond *cond = &cmd->body.cond;
1491 fprintf (stderr, "(cond");
1492 for (i = 0; i < cond->n_cmds; i++)
1494 fprintf (stderr, "\n%s ", prefix);
1495 dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1497 fprintf (stderr, ")");
1499 else if (cmd->type == FontLayoutCmdTypeOTF)
1501 fprintf (stderr, "(otf)");
1504 fprintf (stderr, "(error-command)");
1506 else if (id <= CMD_ID_OFFSET_COMBINING)
1507 fprintf (stderr, "cominging-code");
1509 fprintf (stderr, "(predefiend %d)", id);
1513 dump_flt (MFontLayoutTable *flt, int indent)
1515 char *prefix = (char *) alloca (indent + 1);
1519 memset (prefix, 32, indent);
1521 fprintf (stderr, "(flt");
1522 MPLIST_DO (plist, flt)
1524 FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1527 fprintf (stderr, "\n%s (stage %d", prefix, stage_idx);
1528 for (i = 0; i < stage->used; i++)
1530 fprintf (stderr, "\n%s ", prefix);
1531 dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1533 fprintf (stderr, ")");
1536 fprintf (stderr, ")");