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;
915 MRealizedFont *rfont;
918 static int run_command (int depth,
919 int, MGlyphString *, int, int, FontLayoutContext *);
925 FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to,
926 FontLayoutContext *ctx)
928 int *saved_match_indices = ctx->match_indices;
929 int match_indices[NMATCH * 2];
932 int orig_from = from;
934 if (ctx->cluster_begin_idx)
936 if (ctx->cluster_begin_pos > MGLYPH (from)->pos)
937 ctx->cluster_begin_pos = MGLYPH (from)->pos;
938 if (ctx->cluster_end_pos < MGLYPH (to)->pos)
939 ctx->cluster_end_pos = MGLYPH (to)->pos;
942 if (rule->src_type == SRC_SEQ)
946 len = rule->src.seq.n_codes;
947 if (len > (to - from))
949 for (i = 0; i < len; i++)
950 if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code)
955 MDEBUG_PRINT1 (" (SEQ 0x%X", rule->src.seq.codes[0]);
957 else if (rule->src_type == SRC_RANGE)
963 head = gstring->glyphs[from].code;
964 if (head < rule->src.range.from || head > rule->src.range.to)
966 ctx->code_offset = head - rule->src.range.from;
968 MDEBUG_PRINT2 (" (RANGE 0x%X-0x%X",
969 rule->src.range.from, rule->src.range.to);
971 else if (rule->src_type == SRC_REGEX)
973 regmatch_t pmatch[NMATCH];
979 saved_code = ctx->encoded[to - ctx->encoded_offset];
980 ctx->encoded[to - ctx->encoded_offset] = '\0';
981 result = regexec (&(rule->src.re.preg),
982 ctx->encoded + from - ctx->encoded_offset,
984 if (result == 0 && pmatch[0].rm_so == 0)
986 MDEBUG_PRINT3 (" (REGEX \"%s\" \"%s\" %d",
987 rule->src.re.pattern,
988 ctx->encoded + from - ctx->encoded_offset,
990 ctx->encoded[to - ctx->encoded_offset] = saved_code;
991 for (i = 0; i < NMATCH; i++)
993 if (pmatch[i].rm_so < 0)
994 match_indices[i * 2] = match_indices[i * 2 + 1] = -1;
997 match_indices[i * 2] = from + pmatch[i].rm_so;
998 match_indices[i * 2 + 1] = from + pmatch[i].rm_eo;
1001 ctx->match_indices = match_indices;
1002 to = match_indices[1];
1006 ctx->encoded[to - ctx->encoded_offset] = saved_code;
1010 else if (rule->src_type == SRC_INDEX)
1012 if (rule->src.match_idx >= NMATCH)
1014 from = ctx->match_indices[rule->src.match_idx * 2];
1017 to = ctx->match_indices[rule->src.match_idx * 2 + 1];
1018 MDEBUG_PRINT1 (" (INDEX %d", rule->src.match_idx);
1023 for (i = 0; i < rule->n_cmds; i++)
1027 if (rule->cmd_ids[i] == CMD_ID_REPEAT)
1033 pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx);
1035 MERROR (MERROR_DRAW, -1);
1036 consumed = pos > from;
1041 ctx->match_indices = saved_match_indices;
1043 return (rule->src_type == SRC_INDEX ? orig_from : to);
1047 run_cond (int depth,
1048 FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to,
1049 FontLayoutContext *ctx)
1053 MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, "");
1055 for (i = 0; i < cond->n_cmds; i++)
1056 if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx))
1060 MERROR (MERROR_DRAW, -1);
1067 FontLayoutCmdOTF *otf_cmd, MGlyphString *gstring, int from, int to,
1068 FontLayoutContext *ctx)
1071 int gidx = gstring->used;
1073 to = mfont__ft_drive_otf (gstring, from, to, ctx->rfont,
1074 otf_cmd->script, otf_cmd->langsys,
1075 otf_cmd->gsub_features, otf_cmd->gpos_features);
1076 if (gidx < gstring->used)
1077 MGLYPH (gidx)->left_padding = ctx->left_padding;
1083 run_command (int depth, int id, MGlyphString *gstring, int from, int to,
1084 FontLayoutContext *ctx)
1092 /* Direct code (== id + ctx->code_offset) output.
1093 The source is not consumed. */
1095 g = *(MGLYPH (from));
1097 g = *(MGLYPH (from - 1));
1098 g.type = GLYPH_CHAR;
1099 g.code = ctx->code_offset + id;
1100 MDEBUG_PRINT1 (" (DIRECT 0x%X", g.code);
1101 if (ctx->combining_code)
1102 g.combining_code = ctx->combining_code;
1103 if (ctx->left_padding)
1104 g.left_padding = ctx->left_padding;
1105 for (i = from; i < to; i++)
1107 MGlyph *tmp = MGLYPH (i);
1109 if (g.pos > tmp->pos)
1111 else if (g.to < tmp->to)
1114 APPEND_GLYPH (gstring, g);
1115 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1120 if (id <= CMD_ID_OFFSET_INDEX)
1122 int idx = CMD_ID_TO_INDEX (id);
1125 if (idx >= ctx->stage->used)
1126 MERROR (MERROR_DRAW, -1);
1127 cmd = ctx->stage->cmds + idx;
1128 if (cmd->type == FontLayoutCmdTypeRule)
1129 to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx);
1130 else if (cmd->type == FontLayoutCmdTypeCond)
1131 to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx);
1132 else if (cmd->type == FontLayoutCmdTypeOTF)
1133 to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx);
1140 if (id <= CMD_ID_OFFSET_COMBINING)
1142 ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id);
1152 g = *(MGLYPH (from));
1153 if (ctx->combining_code)
1154 g.combining_code = ctx->combining_code;
1155 if (ctx->left_padding)
1156 g.left_padding = ctx->left_padding;
1157 APPEND_GLYPH (gstring, g);
1158 ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
1162 case CMD_ID_CLUSTER_BEGIN:
1163 if (! ctx->cluster_begin_idx)
1165 MDEBUG_PRINT1 (" <%d", MGLYPH (from)->pos);
1166 ctx->cluster_begin_idx = gstring->used;
1167 ctx->cluster_begin_pos = MGLYPH (from)->pos;
1168 ctx->cluster_end_pos = MGLYPH (from)->to;
1172 case CMD_ID_CLUSTER_END:
1173 if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used)
1177 MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos);
1178 for (i = ctx->cluster_begin_idx; i < gstring->used; i++)
1180 MGLYPH (i)->pos = ctx->cluster_begin_pos;
1181 MGLYPH (i)->to = ctx->cluster_end_pos;
1183 ctx->cluster_begin_idx = 0;
1187 case CMD_ID_SEPARATOR:
1190 g = *(MGLYPH (from));
1192 g = *(MGLYPH (from - 1));
1194 /* g.c = g.code = 0; */
1196 APPEND_GLYPH (gstring, g);
1200 case CMD_ID_LEFT_PADDING:
1201 ctx->left_padding = 1;
1204 case CMD_ID_RIGHT_PADDING:
1205 if (gstring->used > 0)
1206 gstring->glyphs[gstring->used - 1].right_padding = 1;
1210 MERROR (MERROR_DRAW, -1);
1217 mfont__flt_init (void)
1219 Mcond = msymbol ("cond");
1220 Mrange = msymbol ("range");
1221 Mlayouter = msymbol ("layouter");
1222 flt_list = mplist ();
1227 mfont__flt_fini (void)
1231 MPLIST_DO (plist, flt_list)
1233 pl = MPLIST_PLIST (plist);
1236 MPLIST_DO (pl, MPLIST_NEXT (pl))
1237 free_flt_stage (MPLIST_VAL (pl));
1238 pl = MPLIST_PLIST (plist);
1239 M17N_OBJECT_UNREF (pl);
1242 M17N_OBJECT_UNREF (flt_list);
1246 mfont__flt_encode_char (MSymbol layouter_name, int c)
1248 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1253 return MCHAR_INVALID_CODE;
1254 table = MPLIST_VAL (layouter);
1255 code = (unsigned) mchartable_lookup (table, c);
1256 return (code ? code : MCHAR_INVALID_CODE);
1260 mfont__flt_run (MGlyphString *gstring, int from, int to, MRealizedFace *rface)
1265 FontLayoutContext ctx;
1268 int match_indices[NMATCH];
1269 MSymbol layouter_name = rface->rfont->layouter;
1270 MFontLayoutTable *layouter = get_font_layout_table (layouter_name);
1271 MRealizedFace *ascii_rface = rface->ascii_rface;
1272 FontLayoutStage *stage;
1273 int from_pos, to_pos;
1278 /* FLT not found. Make all glyphs invisible. */
1280 gstring->glyphs[from++].code = MCHAR_INVALID_CODE;
1284 dummy = gstring->glyphs[from];
1285 MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name));
1288 memset (&ctx, 0, sizeof ctx);
1289 ctx.rfont = rface->rfont;
1290 table = MPLIST_VAL (layouter);
1291 layouter = MPLIST_NEXT (layouter);
1292 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1294 /* Find previous glyphs that are also supported by the layouter. */
1296 && mchartable_lookup (table, MGLYPH (gidx - 1)->c))
1298 /* + 2 is for a separator ' ' and a terminator '\0'. */
1299 encoded_len = gstring->used - gidx + 2;
1300 ctx.encoded = (char *) alloca (encoded_len);
1302 for (i = 0; gidx < from; i++, gidx++)
1303 ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c);
1305 ctx.encoded[i++] = ' ';
1306 ctx.encoded_offset = from - i;
1308 /* Now each MGlyph->code contains encoded char. Set it in
1309 ctx.encoded[], and set MGlyph->c to MGlyph->code. */
1310 for (gidx = from; gidx < to ; i++, gidx++)
1312 ctx.encoded[i] = (int) MGLYPH (gidx)->code;
1313 MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c;
1315 ctx.encoded[i++] = '\0';
1317 match_indices[0] = from;
1318 match_indices[1] = to;
1319 for (i = 2; i < NMATCH; i++)
1320 match_indices[i] = -1;
1321 ctx.match_indices = match_indices;
1323 from_pos = MGLYPH (from)->pos;
1324 to_pos = MGLYPH (to)->pos;
1326 for (stage_idx = 0; 1; stage_idx++)
1328 int len = to - from;
1331 MDEBUG_PRINT1 ("\n [FLT] (STAGE %d", stage_idx);
1332 gidx = gstring->used;
1335 result = run_command (2, INDEX_TO_CMD_ID (0), gstring,
1336 ctx.encoded_offset, to, &ctx);
1340 to = from + (gstring->used - gidx);
1341 REPLACE_GLYPHS (gstring, gidx, from, len);
1343 layouter = MPLIST_NEXT (layouter);
1344 /* If this is the last stage, break the loop. */
1345 if (MPLIST_TAIL_P (layouter))
1348 /* Otherwise, prepare for the next iteration. */
1349 stage = (FontLayoutStage *) MPLIST_VAL (layouter);
1350 table = stage->category;
1351 if (to - from >= encoded_len)
1353 encoded_len = to + 1;
1354 ctx.encoded = (char *) alloca (encoded_len);
1357 for (i = from; i < to; i++)
1359 MGlyph *g = MGLYPH (i);
1361 if (g->type == GLYPH_PAD)
1362 ctx.encoded[i - from] = ' ';
1363 else if (! g->otf_encoded)
1364 ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code);
1365 #ifdef HAVE_FREETYPE
1368 int c = mfont__ft_decode_otf (g);
1372 c = (int) mchartable_lookup (table, c);
1376 ctx.encoded[i - from] = (c >= 0 ? c : 1);
1378 #endif /* HAVE_FREETYPE */
1380 ctx.encoded[i - from] = '\0';
1381 ctx.encoded_offset = from;
1382 ctx.match_indices[0] = from;
1383 ctx.match_indices[1] = to;
1386 MDEBUG_PRINT (")\n");
1390 /* Somehow there's no glyph contributing to characters between
1391 FROM_POS and TO_POS. We must add one dummy space glyph for
1392 those characters. */
1395 g.type = GLYPH_SPACE;
1396 g.c = ' ', g.code = ' ';
1397 g.pos = from_pos, g.to = to_pos;
1398 g.rface = ascii_rface;
1399 INSERT_GLYPH (gstring, from, g);
1404 /* Here we must check if all characters in the range is covered
1405 by some glyph(s). If not, change <pos> and <to> of glyphs to
1406 cover uncovered characters. */
1407 int len = to_pos - from_pos;
1409 MGlyph **glyphs = alloca (sizeof (MGlyph) * len);
1410 MGlyph *g, *gend = MGLYPH (to);
1411 MGlyph *latest = gend;
1413 for (i = 0; i < len; i++)
1415 for (g = MGLYPH (from); g != gend; g++)
1417 if (g->pos < latest->pos)
1419 if (! glyphs[g->pos - from_pos])
1421 for (i = g->pos; i < g->to; i++)
1422 glyphs[i - from_pos] = g;
1429 for (g = latest; g->pos == pos; g++)
1433 for (; i < len; i++)
1437 for (g = latest; g->pos == latest->pos; g++)
1438 g->to = from_pos + i + 1;
1448 /* for debugging... */
1451 dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
1453 char *prefix = (char *) alloca (indent + 1);
1455 memset (prefix, 32, indent);
1459 fprintf (stderr, "0x%02X", id);
1460 else if (id <= CMD_ID_OFFSET_INDEX)
1462 int idx = CMD_ID_TO_INDEX (id);
1463 FontLayoutCmd *cmd = stage->cmds + idx;
1465 if (cmd->type == FontLayoutCmdTypeRule)
1467 FontLayoutCmdRule *rule = &cmd->body.rule;
1470 fprintf (stderr, "(rule ");
1471 if (rule->src_type == SRC_REGEX)
1472 fprintf (stderr, "\"%s\"", rule->src.re.pattern);
1473 else if (rule->src_type == SRC_INDEX)
1474 fprintf (stderr, "%d", rule->src.match_idx);
1475 else if (rule->src_type == SRC_SEQ)
1476 fprintf (stderr, "(seq)");
1477 else if (rule->src_type == SRC_RANGE)
1478 fprintf (stderr, "(range)");
1480 fprintf (stderr, "(invalid src)");
1482 for (i = 0; i < rule->n_cmds; i++)
1484 fprintf (stderr, "\n%s ", prefix);
1485 dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
1487 fprintf (stderr, ")");
1489 else if (cmd->type == FontLayoutCmdTypeCond)
1491 FontLayoutCmdCond *cond = &cmd->body.cond;
1494 fprintf (stderr, "(cond");
1495 for (i = 0; i < cond->n_cmds; i++)
1497 fprintf (stderr, "\n%s ", prefix);
1498 dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
1500 fprintf (stderr, ")");
1502 else if (cmd->type == FontLayoutCmdTypeOTF)
1504 fprintf (stderr, "(otf)");
1507 fprintf (stderr, "(error-command)");
1509 else if (id <= CMD_ID_OFFSET_COMBINING)
1510 fprintf (stderr, "cominging-code");
1512 fprintf (stderr, "(predefiend %d)", id);
1516 dump_flt (MFontLayoutTable *flt, int indent)
1518 char *prefix = (char *) alloca (indent + 1);
1522 memset (prefix, 32, indent);
1524 fprintf (stderr, "(flt");
1525 MPLIST_DO (plist, flt)
1527 FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
1530 fprintf (stderr, "\n%s (stage %d", prefix, stage_idx);
1531 for (i = 0; i < stage->used; i++)
1533 fprintf (stderr, "\n%s ", prefix);
1534 dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
1536 fprintf (stderr, ")");
1539 fprintf (stderr, ")");