From d6be6d3a5a5043f44325549c58a25cd39f932eef Mon Sep 17 00:00:00 2001 From: handa Date: Wed, 15 Aug 2007 11:14:21 +0000 Subject: [PATCH] New file created by modifying font-flt.c. --- src/m17n-flt.c | 2213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2213 insertions(+) create mode 100644 src/m17n-flt.c diff --git a/src/m17n-flt.c b/src/m17n-flt.c new file mode 100644 index 0000000..c131213 --- /dev/null +++ b/src/m17n-flt.c @@ -0,0 +1,2213 @@ +/* m17n-flt.c -- Font Layout Table sub-module. + Copyright (C) 2003, 2004, 2007 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + 02111-1307, USA. */ + +/***en + @addtogroup m17nFLT + @brief FLT support for a window system. + + This section defines the m17n FLT API concerning character + layouting facility using FLT (Font Layout Table). */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "m17n.h" +#include "m17n-flt.h" +#include "m17n-misc.h" +#include "internal.h" +#include "mtext.h" +#include "symbol.h" +#include "plist.h" +#include "internal-flt.h" + +/* Font Layouter */ + +/* Font Layout Table (FLT) + +Predefined terms: SYMBOL, INTEGER, STRING + +FLT ::= '(' STAGE + ')' + +STAGE ::= CATEGORY-TABLE ? FONT-LAYOUT-RULE + +;; Each STAGE consumes a source (code sequence) and produces another +;; code sequence that is given to the next STAGE as a source. The +;; source given to the first stage is a sequence of character codes +;; that are assigned category codes by CATEGORY-TABLE. The output of +;; the last stage is a glyph code sequence given to the renderer. + +CATEGORY-TABLE ::= + '(' 'category' CATEGORY-SPEC + ')' +CATEGORY-SPEC ::= + '(' CODE [ CODE ] CATEGORY ')' +CODE ::= INTEGER +CATEGORY ::= INTEGER +;; ASCII character codes of alphabet ('A' .. 'Z' 'a' .. 'z'). +;; Ex: CATEGORY-TABLE +;; (category +;; (0x0900 0x097F ?E) ; All Devanagari characters +;; (0x093C ?N)) ; DEVANAGARI-LETTER NUKTA +;; Assign the category 'E' to all Devanagari characters but 0x093C, +;; assign the category 'N' to 0x093C. + +FONT-LAYOUT-RULE ::= + '(' 'generator' RULE MACRO-DEF * ')' + +RULE ::= COMMAND | REGEXP-RULE | MATCH-RULE | MAP-RULE + | COND-STRUCT | MACRO-NAME + +COMMAND ::= + DIRECT-CODE | COMBINING | PREDEFIND-COMMAND | OTF-COMMAND + +DIRECT-CODE ::= INTEGER +;; Always succeed. Produce the code. Consume no source. + +PREDEFIND-COMMAND ::= + '=' | '*' | '<' | '>' | '|' + +;; '=': Succeed when the current run contains at least one code. +;; Consume the first code in the current run, and produce it as is. + +;; '*': If the the previous command succeeded, repeat it until it +;; fails. + +;; '<': Produce a special code that indicates the start of grapheme +;; cluster. Succeed always, consume nothing. + +;; '>': Produce a special code that indicates the end of grapheme +;; cluster. Succeed always, consume nothing. + +;; '|': Produce a special code whose category is ' '. Succeed always, +;; consume nothing. + +OTF-COMMAND ::= + 'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]] +;; Run the Open Type Layout Table on the current run. Succeed always, +;; consume nothing. + +SCRIPT ::= OTF-TAG +;; OTF's ScriptTag name (four letters) listed at: +;; +LANGSYS ::= OTF-TAG +;; OTF's Language System name (four letters) listed at: +;; + +GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' ' +GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' ' +FEATURE ::= OTF-TAG +;; OTF's Feature name (four letters) listed at: +;; + +OTF-TAG ::= PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR + +;; Ex. OTF-COMMAND +;; 'otf:deva' +;; Run all features in the default langsys of 'deva' script. +;; 'otf:deva::nukt:haln' +;; Run all GSUB features, run 'nukt' and 'haln' GPOS features. +;; 'otf:deva:: :' +;; Run all GSUB features, run no GPOS features. + +REGEXP-RULE ::= + '(' REGEXP RULE * ')' + +;; Succeed if REGXP matches the head of source. Run RULEs while +;; limiting the source to the matching part. Consume that part. + +REGEXP ::= STRING +;; Must be composed only from ASCII characters. 'A' - 'Z', 'a' - 'z' +;; correspond to CATEGORY. + +;; Ex: REGEXP-RULE +;; ("VA?" +;; < | vowel * | >) + +MATCH-RULE ::= + '(' MATCH-IDX RULE * ')' + +;; Succeed if the previous REGEXP-RULE found a matching part for +;; MATCH-IDX. Run RULEs while limiting the source to the matching +;; part. If MATCH-IDX is zero, consume the whole part, else consume +;; nothing. + +MATCH-IDX ::= INTEGER +;; Must be 0..20. + +;; Ex. MATCH-RULE +;; (2 consonant *) + +MAP-RULE ::= + '(' ( SOURCE-SEQ | SOURCE-RANGE ) RULE * ')' + +;; Succeed if the source matches SOURCE-SEQ or SOURCE-RANGE. Run +;; RULEs while limiting the source to the matching part. Consume that +;; part. + +SOURCE-SEQ ::= + '(' CODE + ')' +SOURCE-RANGE ::= + '(' 'range' CODE CODE ')' +;; Ex. MAP-RULE +;; ((0x0915 0x094D) 0x43) +;; If the source code sequence is 0x0915 0x094D, produce 0x43. +;; ((range 0x0F40 0x0F6A) 0x2221) +;; If the first source code CODE is in the range 0x0F40..0x0F6A, +;; produce (0x2221 + (CODE - 0x0F40)). + +COND-STRUCT ::= + '(' 'cond' RULE + ')' + +;; Try each rule in sequence until one succeeds. Succeed if one +;; succeeds. Consume nothing. + +;; Ex. COND-STRUCT +;; (cond +;; ((0x0915 0x094D) 0x43) +;; ((range 0x0F40 0x0F6A) 0x2221) +;; = ) + +COMBINING ::= 'V''H''O''V''H' +V ::= ( 't' | 'c' | 'b' | 'B' ) +H ::= ( 'l' | 'c' | 'r' ) +O ::= ( '.' | XOFF | YOFF | XOFF YOFF ) +XOFF ::= '<'INTEGER | '>'INTEGER +YOFF ::= '+'INTEGER | '-'INTEGER +;; INTEGER must be integer 0..127 + +;; VH pair indicates 12 reference points of a glyph as below: +;; +;; 0----1----2 <---- ascent 0:tl (top-left) +;; | | 1:tc (top-center) +;; | | 2:tr (top-right) +;; | | 3:Bl (base-left) +;; 9 10 11 <---- center 4:Bc (base-center) +;; | | 5:Br (base-right) +;; --3----4----5-- <-- baseline 6:bl (bottom-left) +;; | | 7:bc (bottom-center) +;; 6----7----8 <---- descent 8:br (bottom-right) +;; 9:cl (center-left) +;; | | | 10:cc (center-center) +;; left center right 11:cr (center-right) +;; +;; Ex. COMBINING +;; 'tc.bc': +;; Align top-left point of the previous glyph and bottom-center +;; point of the current glyph. +;; 'Bl<20-10Br' +;; Align 20% left and 10% below of base-left point of the previous +;; glyph and base-right point of the current glyph. + +MACRO-DEF ::= + '(' MACRO-NAME RULE + ')' +MACRO-NAME ::= SYMBOL + +*/ + +static int mdebug_flag = MDEBUG_FONT_FLT; + +MSymbol Mfont, Mlayouter; + +static MPlist *flt_list; + +enum GlyphInfoMask +{ + CombiningCodeMask = 0xFFFFFFF, + LeftPaddingMask = 1 << 28, + RightPaddingMask = 1 << 29, + EncodedMask = 1 << 30, + PositionedMask = 1 << 31 +}; + +#define SET_GLYPH_INFO(g, mask, ctx, info) \ + ((g)->internal = (((g)->internal & ~(mask)) | (info)), \ + (ctx)->check_mask |= (mask)) + +#define GET_COMBINING_CODE(g) ((g)->internal & CombiningCodeMask) +#define SET_COMBINING_CODE(g, ctx, code) \ + SET_GLYPH_INFO (g, CombiningCodeMask, ctx, code) +#define GET_LEFT_PADDING(g) ((g)->internal & LeftPaddingMask) +#define SET_LEFT_PADDING(g, ctx, flag) \ + SET_GLYPH_INFO (g, LeftPaddingMask, ctx, flag) +#define GET_RIGHT_PADDING(g) ((g)->internal & RightPaddingMask) +#define SET_RIGHT_PADDING(g, ctx, flag) \ + SET_GLYPH_INFO (g, RightPaddingMask, ctx, flag) +#define GET_ENCODED(g) ((g)->internal & EncodedMask) +#define SET_ENCODED(g, ctx, flag) \ + SET_GLYPH_INFO (g, EncodedMask, ctx, flag) +#define GET_POSITIONED(g) ((g)->internal & PositionedMask) +#define SET_POSITIONED(g, ctx, flag) \ + SET_GLYPH_INFO (g, PositionedMask, ctx, flag) + +#define GINIT(gstring, n) \ + do { \ + if (! (gstring)->glyph_size) \ + (gstring)->glyph_size = sizeof (MFLTGlyph); \ + if ((n) > 0) \ + { \ + (gstring)->glyphs = alloca ((gstring)->glyph_size * (n)); \ + (gstring)->allocated = (n); \ + (gstring)->used = 0; \ + } \ + } while (0) + +#define GALLOCA (gstring) \ + ((MFLTGlyph *) alloca ((gstring)->glyph_size)) + +#define GREF(gstring, idx) \ + ((MFLTGlyph *) ((char *) ((gstring)->glyphs) + (gstring)->glyph_size * (idx))) + +#define GAPPEND(gstring, g) \ + do { \ + if ((gstring)->allocated <= (gstring)->used) \ + return -2; \ + memcpy ((char *) (((gstring)->glyphs) \ + + (gstring)->glyph_size * (gstring)->used), \ + (char *) g, (gstring)->glyph_size); \ + } while (0) + +#define GDUP(ctx, idx) \ + do { \ + MFLTGlyphString *src = ctx->in; \ + MFLTGlyphString *tgt = ctx->out; \ + if ((tgt)->allocated <= (tgt)->used) \ + return -2; \ + memcpy ((char *) (tgt->glyphs) + tgt->glyph_size * tgt->used, \ + (char *) (src->glyphs) + src->glyph_size * (idx), \ + (src)->glyph_size); \ + tgt->used++; \ + } while (0) + +static int +GREPLACE (MFLTGlyphString *src, int src_from, int src_to, + MFLTGlyphString *tgt, int tgt_from, int tgt_to) +{ + int src_len = src_to - src_from; + int tgt_len = tgt_to - tgt_from; + int inc = src_len - tgt_len; + + if (tgt->allocated < tgt->used + inc) + return -2; + if (inc != 0 && tgt_to < tgt->used) + memmove ((char *) tgt->glyphs + tgt->glyph_size * (tgt_from + src_len), + (char *) tgt->glyphs + tgt->glyph_size * tgt_to, + tgt->glyph_size * (tgt->used - tgt_to)); + if (src_len) + memcpy ((char *) tgt->glyphs + tgt->glyph_size * tgt_from, + (char *) src->glyphs + src->glyph_size * src_from, + src->glyph_size * src_len); + tgt->used += inc; + return 0; +} + + +/* Command ID: + 0 ... : direct code + -1 : invalid + -0x0F .. -2 : builtin commands + -0x100000F .. -0x10 : combining code + ... -0x1000010: index to FontLayoutStage->cmds + */ + +#define INVALID_CMD_ID -1 +#define CMD_ID_OFFSET_BUILTIN -3 +#define CMD_ID_OFFSET_COMBINING -0x10 +#define CMD_ID_OFFSET_INDEX -0x1000010 + +/* Builtin commands. */ +#define CMD_ID_COPY -3 /* '=' */ +#define CMD_ID_REPEAT -4 /* '*' */ +#define CMD_ID_CLUSTER_BEGIN -5 /* '<' */ +#define CMD_ID_CLUSTER_END -6 /* '>' */ +#define CMD_ID_SEPARATOR -7 /* '|' */ +#define CMD_ID_LEFT_PADDING -8 /* '[' */ +#define CMD_ID_RIGHT_PADDING -9 /* ']' */ + +#define CMD_ID_TO_COMBINING_CODE(id) (CMD_ID_OFFSET_COMBINING - (id)) +#define COMBINING_CODE_TO_CMD_ID(code) (CMD_ID_OFFSET_COMBINING - (code)) + +#define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id)) +#define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx)) + +static MSymbol Mcond, Mrange; + +#define GLYPH_CODE_P(code) \ + ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX) + +#define GLYPH_CODE_INDEX(code) ((code) - GLYPH_CODE_MIN) + +#define UPDATE_CLUSTER_RANGE(ctx, g) \ + do { \ + if ((ctx)->cluster_begin_idx) \ + { \ + if (ctx->cluster_begin_pos > (g)->from) \ + ctx->cluster_begin_pos = (g)->from; \ + if (ctx->cluster_end_pos < (g)->to) \ + ctx->cluster_end_pos = (g)->to; \ + } \ + } while (0) + +enum FontLayoutCmdRuleSrcType + { + SRC_REGEX, + SRC_INDEX, + SRC_SEQ, + SRC_RANGE + }; + +typedef struct +{ + enum FontLayoutCmdRuleSrcType src_type; + union { + struct { + char *pattern; + regex_t preg; + } re; + int match_idx; + struct { + int n_codes; + int *codes; + } seq; + struct { + int from, to; + } range; + } src; + + int n_cmds; + int *cmd_ids; +} FontLayoutCmdRule; + +typedef struct +{ + /* Beginning and end indices of series of SEQ commands. */ + int seq_beg, seq_end; + /* Range of the first character appears in the above series. */ + int seq_from, seq_to; + + int n_cmds; + int *cmd_ids; +} FontLayoutCmdCond; + +enum FontLayoutCmdType + { + FontLayoutCmdTypeRule, + FontLayoutCmdTypeCond, + FontLayoutCmdTypeOTF, + FontLayoutCmdTypeMAX + }; + +typedef struct +{ + enum FontLayoutCmdType type; + union { + FontLayoutCmdRule rule; + FontLayoutCmdCond cond; + MFLT_OTF_Spec otf; + } body; +} FontLayoutCmd; + +typedef struct +{ + MCharTable *category; + int size, inc, used; + FontLayoutCmd *cmds; +} FontLayoutStage; + +typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */ + +/* Font layout table loader */ + +/* Load a category table from PLIST. PLIST has this form: + PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) * +*/ + +static MCharTable * +load_category_table (MPlist *plist) +{ + MCharTable *table; + + table = mchartable (Minteger, (void *) 0); + + MPLIST_DO (plist, plist) + { + MPlist *elt; + int from, to, category_code; + + if (! MPLIST_PLIST (plist)) + MERROR (MERROR_FONT, NULL); + elt = MPLIST_PLIST (plist); + if (! MPLIST_INTEGER_P (elt)) + MERROR (MERROR_FONT, NULL); + from = MPLIST_INTEGER (elt); + elt = MPLIST_NEXT (elt); + if (! MPLIST_INTEGER_P (elt)) + MERROR (MERROR_FONT, NULL); + to = MPLIST_INTEGER (elt); + elt = MPLIST_NEXT (elt); + if (MPLIST_TAIL_P (elt)) + { + category_code = to; + to = from; + } + else + { + if (! MPLIST_INTEGER_P (elt)) + MERROR (MERROR_FONT, NULL); + category_code = MPLIST_INTEGER (elt); + } + if (! isalnum (category_code)) + MERROR (MERROR_FONT, NULL); + + if (from == to) + mchartable_set (table, from, (void *) category_code); + else + mchartable_set_range (table, from, to, (void *) category_code); + } + + return table; +} + +static unsigned int +gen_otf_tag (char *p) +{ + unsigned int tag = 0; + int i; + + for (i = 0; i < 4 && *p; i++, p++) + tag = (tag << 8) | *p; + return (i < 4 ? 0 : tag); +} + +static char * +otf_count_features (char *p, char *end, char stopper, int *count) +{ + *count = 0; + if (*p != stopper && *p != '\0') + while (1) + { + (*count)++; + if (*p == '*') + { + p++; + if (*p == stopper || *p == '\0') + break; + return NULL; + } + if (*p == '~') + p += 5; + else + p += 4; + if (p > end) + return NULL; + if (*p == stopper || *p == '\0') + break; + if (*p != ',') + return NULL; + p++; + if (! *p) + return NULL; + } + return p; +} + +static void +otf_store_features (char *p, char *end, int count, unsigned *buf) +{ + int i; + + for (i = 0; p < end;) + { + if (*p == '*') + buf[i++] = 0, p += 2; + else if (*p == '~') + buf[--count] = gen_otf_tag (p + 1), p += 6; + else + buf[i++] = gen_otf_tag (p), p += 5; + } +} + +static int +parse_otf_command (MSymbol symbol, MFLT_OTF_Spec *spec) +{ + char *str = MSYMBOL_NAME (symbol); + char *end = str + MSYMBOL_NAMELEN (symbol); + unsigned int script, langsys; + unsigned int *gsub, *gpos; + int gsub_count = 0, gpos_count = 0; + char *p; + + spec->sym = symbol; + str += 5; /* skip the heading ":otf=" */ + script = gen_otf_tag (str); + if (! script) + return 0; + str += 4; + if (*str == '/') + { + langsys = gen_otf_tag (str); + if (! langsys) + MERROR (MERROR_FLT, -1); + str += 4; + } + else + langsys = 0; + if (*str != '=') + { + /* Apply all GSUB features. */ + gsub_count = 1; + gsub = alloca (sizeof *gsub); + *gsub = 0; + } + else + { + p = str + 1; + str = otf_count_features (p, end, '+', &gsub_count); + if (! str) + MERROR (MERROR_FLT, -1); + if (gsub_count > 0) + { + gsub = alloca (sizeof (unsigned int) * gsub_count); + otf_store_features (p, str, gsub_count, gsub); + } + } + if (*str != '+') + { + /* Apply all GPOS features. */ + gpos_count = 1; + gpos = alloca (sizeof *gpos); + *gpos = 0; + } + else + { + p = str + 1; + str = otf_count_features (p, end, '\0', &gpos_count); + if (! str) + MERROR (MERROR_FLT, -1); + if (gpos_count > 0) + { + gpos = alloca (sizeof (unsigned int) * gpos_count); + otf_store_features (p, str, gpos_count, gpos); + } + } + + spec->script = script; + spec->langsys = langsys; + spec->gsub_count = gsub_count; + spec->gpos_count = gpos_count; + if (gsub_count + gpos_count > 0) + { + spec->gsub = malloc (sizeof (int) * (gsub_count + gpos_count)); + if (! spec->gsub) + MERROR (MERROR_FLT, -2); + spec->gpos = spec->gsub + gsub_count; + if (gsub_count > 0) + memcpy (spec->gsub, gsub, sizeof (int) * gsub_count); + if (gpos_count > 0) + memcpy (spec->gpos, gpos, sizeof (int) * gpos_count); + } + return 0; +} + + +/* Parse OTF command name NAME and store the result in CMD. + NAME has this form: + :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]] + where GSUB-FEATURES and GPOS-FEATURES have this form: + [FEATURE[,FEATURE]*] | ' ' */ + +static int +load_otf_command (FontLayoutCmd *cmd, MSymbol sym) +{ + char *name = MSYMBOL_NAME (sym); + int result; + + if (name[0] != ':') + { + /* This is old format of "otf:...". Change it to ":otf=...". */ + char *str = alloca (MSYMBOL_NAMELEN (sym) + 2); + + sprintf (str, ":otf="); + strcat (str, name + 4); + sym = msymbol (str); + } + + result = parse_otf_command (sym, &cmd->body.otf); + if (result == -2) + return result; + if (result < 0) + cmd->body.otf.gsub_count = cmd->body.otf.gpos_count = 0; + cmd->type = FontLayoutCmdTypeOTF; + return 0; +} + + +/* Read a decimal number from STR preceded by one of "+-><". '+' and + '>' means a plus sign, '-' and '<' means a minus sign. If the + number is greater than 127, limit it to 127. */ + +static int +read_decimal_number (char **str) +{ + char *p = *str; + int sign = (*p == '-' || *p == '<') ? -1 : 1; + int n = 0; + + p++; + while (*p >= '0' && *p <= '9') + n = n * 10 + *p++ - '0'; + *str = p; + if (n == 0) + n = 5; + return (n < 127 ? n * sign : 127 * sign); +} + + +/* Read a horizontal and vertical combining positions from STR, and + store them in the place pointed by X and Y. The horizontal + position left, center, and right are represented by 0, 1, and 2 + respectively. The vertical position top, center, bottom, and base + are represented by 0, 1, 2, and 3 respectively. If successfully + read, return 0, else return -1. */ + +static int +read_combining_position (char *str, int *x, int *y) +{ + int c = *str++; + int i; + + /* Vertical position comes first. */ + for (i = 0; i < 4; i++) + if (c == "tcbB"[i]) + { + *y = i; + break; + } + if (i == 4) + return -1; + c = *str; + /* Then comse horizontal position. */ + for (i = 0; i < 3; i++) + if (c == "lcr"[i]) + { + *x = i; + return 0; + } + return -1; +} + + +/* Return a combining code corresponding to SYM. */ + +static int +get_combining_command (MSymbol sym) +{ + char *str = msymbol_name (sym); + int base_x, base_y, add_x, add_y, off_x, off_y; + int c; + + if (read_combining_position (str, &base_x, &base_y) < 0) + return 0; + str += 2; + c = *str; + if (c == '.') + { + off_x = off_y = 128; + str++; + } + else + { + if (c == '+' || c == '-') + { + off_y = read_decimal_number (&str) + 128; + c = *str; + } + else + off_y = 128; + if (c == '<' || c == '>') + off_x = read_decimal_number (&str) + 128; + else + off_x = 128; + } + if (read_combining_position (str, &add_x, &add_y) < 0) + return 0; + + c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x); + return (COMBINING_CODE_TO_CMD_ID (c)); +} + + +/* Load a command from PLIST into STAGE, and return that + identification number. If ID is not INVALID_CMD_ID, that means we + are loading a top level command or a macro. In that case, use ID + as the identification number of the command. Otherwise, generate a + new id number for the command. MACROS is a list of raw macros. */ + +static int +load_command (FontLayoutStage *stage, MPlist *plist, + MPlist *macros, int id) +{ + int i; + int result; + + if (MPLIST_INTEGER_P (plist)) + { + int code = MPLIST_INTEGER (plist); + + if (code < 0) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + return code; + } + else if (MPLIST_PLIST_P (plist)) + { + /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... ) + | ( ( INTEGER INTEGER ) ... ) + | ( ( range INTEGER INTEGER ) ... ) */ + MPlist *elt = MPLIST_PLIST (plist); + int len = MPLIST_LENGTH (elt) - 1; + FontLayoutCmd *cmd; + + if (id == INVALID_CMD_ID) + { + FontLayoutCmd dummy; + id = INDEX_TO_CMD_ID (stage->used); + MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW); + } + cmd = stage->cmds + CMD_ID_TO_INDEX (id); + + if (MPLIST_SYMBOL_P (elt)) + { + FontLayoutCmdCond *cond; + + if (MPLIST_SYMBOL (elt) != Mcond) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + elt = MPLIST_NEXT (elt); + cmd->type = FontLayoutCmdTypeCond; + cond = &cmd->body.cond; + cond->seq_beg = cond->seq_end = -1; + cond->seq_from = cond->seq_to = 0; + cond->n_cmds = len; + MTABLE_CALLOC (cond->cmd_ids, len, MERROR_DRAW); + for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt)) + { + int this_id = load_command (stage, elt, macros, INVALID_CMD_ID); + + if (this_id == INVALID_CMD_ID || this_id == -2) + MERROR (MERROR_DRAW, this_id); + /* The above load_command may relocate stage->cmds. */ + cmd = stage->cmds + CMD_ID_TO_INDEX (id); + cond = &cmd->body.cond; + cond->cmd_ids[i] = this_id; + if (this_id <= CMD_ID_OFFSET_INDEX) + { + FontLayoutCmd *this_cmd + = stage->cmds + CMD_ID_TO_INDEX (this_id); + + if (this_cmd->type == FontLayoutCmdTypeRule + && this_cmd->body.rule.src_type == SRC_SEQ) + { + int first_char = this_cmd->body.rule.src.seq.codes[0]; + + if (cond->seq_beg < 0) + { + /* The first SEQ command. */ + cond->seq_beg = i; + cond->seq_from = cond->seq_to = first_char; + } + else if (cond->seq_end < 0) + { + /* The following SEQ command. */ + if (cond->seq_from > first_char) + cond->seq_from = first_char; + else if (cond->seq_to < first_char) + cond->seq_to = first_char; + } + } + else + { + if (cond->seq_beg >= 0 && cond->seq_end < 0) + /* The previous one is the last SEQ command. */ + cond->seq_end = i; + } + } + else + { + if (cond->seq_beg >= 0 && cond->seq_end < 0) + /* The previous one is the last SEQ command. */ + cond->seq_end = i; + } + } + if (cond->seq_beg >= 0 && cond->seq_end < 0) + /* The previous one is the last SEQ command. */ + cond->seq_end = i; + } + else + { + cmd->type = FontLayoutCmdTypeRule; + if (MPLIST_MTEXT_P (elt)) + { + MText *mt = MPLIST_MTEXT (elt); + char *str = (char *) MTEXT_DATA (mt); + + if (str[0] != '^') + { + mtext_ins_char (mt, 0, '^', 1); + str = (char *) MTEXT_DATA (mt); + } + if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED)) + MERROR (MERROR_FONT, INVALID_CMD_ID); + cmd->body.rule.src_type = SRC_REGEX; + cmd->body.rule.src.re.pattern = strdup (str); + } + else if (MPLIST_INTEGER_P (elt)) + { + cmd->body.rule.src_type = SRC_INDEX; + cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt); + } + else if (MPLIST_PLIST_P (elt)) + { + MPlist *pl = MPLIST_PLIST (elt); + int size = MPLIST_LENGTH (pl); + + if (MPLIST_INTEGER_P (pl)) + { + int i; + + cmd->body.rule.src_type = SRC_SEQ; + cmd->body.rule.src.seq.n_codes = size; + MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size, + MERROR_FONT); + for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl)) + { + if (! MPLIST_INTEGER_P (pl)) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + cmd->body.rule.src.seq.codes[i] + = (unsigned) MPLIST_INTEGER (pl); + } + } + else if (MPLIST_SYMBOL_P (pl) && size == 3) + { + cmd->body.rule.src_type = SRC_RANGE; + pl = MPLIST_NEXT (pl); + if (! MPLIST_INTEGER_P (pl)) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + cmd->body.rule.src.range.from + = (unsigned) MPLIST_INTEGER (pl); + pl = MPLIST_NEXT (pl); + if (! MPLIST_INTEGER_P (pl)) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + cmd->body.rule.src.range.to + = (unsigned) MPLIST_INTEGER (pl); + } + else + MERROR (MERROR_DRAW, INVALID_CMD_ID); + } + else + MERROR (MERROR_DRAW, INVALID_CMD_ID); + + elt = MPLIST_NEXT (elt); + cmd->body.rule.n_cmds = len; + MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW); + for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt)) + { + int this_id = load_command (stage, elt, macros, INVALID_CMD_ID); + + if (this_id == INVALID_CMD_ID || this_id == -2) + MERROR (MERROR_DRAW, this_id); + /* The above load_command may relocate stage->cmds. */ + cmd = stage->cmds + CMD_ID_TO_INDEX (id); + cmd->body.rule.cmd_ids[i] = this_id; + } + } + } + else if (MPLIST_SYMBOL_P (plist)) + { + MPlist *elt; + MSymbol sym = MPLIST_SYMBOL (plist); + char *name = msymbol_name (sym); + int len = strlen (name); + FontLayoutCmd cmd; + + if (len > 4 + && ((name[0] == 'o' && name[1] == 't' + && name[2] == 'f' && name[3] == ':') + || (name[0] == ':' && name[1] == 'o' && name[2] == 't' + && name[3] == 'f' && name[4] == '='))) + { + result = load_otf_command (&cmd, sym); + if (result < 0) + return result; + if (id == INVALID_CMD_ID) + { + id = INDEX_TO_CMD_ID (stage->used); + MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW); + } + else + stage->cmds[CMD_ID_TO_INDEX (id)] = cmd; + return id; + } + + if (len == 1) + { + if (*name == '=') + return CMD_ID_COPY; + else if (*name == '*') + return CMD_ID_REPEAT; + else if (*name == '<') + return CMD_ID_CLUSTER_BEGIN; + else if (*name == '>') + return CMD_ID_CLUSTER_END; + else if (*name == '|') + return CMD_ID_SEPARATOR; + else if (*name == '[') + return CMD_ID_LEFT_PADDING; + else if (*name == ']') + return CMD_ID_RIGHT_PADDING; + else + id = 0; + } + else + { + id = get_combining_command (sym); + if (id) + return id; + } + + i = 1; + MPLIST_DO (elt, macros) + { + if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt))) + { + id = INDEX_TO_CMD_ID (i); + if (stage->cmds[i].type == FontLayoutCmdTypeMAX) + id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)), + macros, id); + return id; + } + i++; + } + MERROR (MERROR_DRAW, INVALID_CMD_ID); + } + else + MERROR (MERROR_DRAW, INVALID_CMD_ID); + + return id; +} + +static void +free_flt_command (FontLayoutCmd *cmd) +{ + if (cmd->type == FontLayoutCmdTypeRule) + { + FontLayoutCmdRule *rule = &cmd->body.rule; + + if (rule->src_type == SRC_REGEX) + { + free (rule->src.re.pattern); + regfree (&rule->src.re.preg); + } + else if (rule->src_type == SRC_SEQ) + free (rule->src.seq.codes); + free (rule->cmd_ids); + } + else if (cmd->type == FontLayoutCmdTypeCond) + free (cmd->body.cond.cmd_ids); + else if (cmd->type == FontLayoutCmdTypeOTF) + { + if (cmd->body.otf.gsub_count + cmd->body.otf.gpos_count > 0) + free (cmd->body.otf.gsub); + } +} + +/* Load a generator from PLIST into a newly allocated FontLayoutStage, + and return it. PLIST has this form: + PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * ) +*/ + +static FontLayoutStage * +load_generator (MPlist *plist) +{ + FontLayoutStage *stage; + MPlist *elt, *pl; + FontLayoutCmd dummy; + int result; + + MSTRUCT_CALLOC (stage, MERROR_DRAW); + MLIST_INIT1 (stage, cmds, 32); + dummy.type = FontLayoutCmdTypeMAX; + MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT); + MPLIST_DO (elt, MPLIST_NEXT (plist)) + { + if (! MPLIST_PLIST_P (elt)) + MERROR (MERROR_FONT, NULL); + pl = MPLIST_PLIST (elt); + if (! MPLIST_SYMBOL_P (pl)) + MERROR (MERROR_FONT, NULL); + MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT); + } + + /* Load the first command from PLIST into STAGE->cmds[0]. Macros + called in the first command are also loaded from MPLIST_NEXT + (PLIST) into STAGE->cmds[n]. */ + result = load_command (stage, plist, MPLIST_NEXT (plist), + INDEX_TO_CMD_ID (0)); + if (result == INVALID_CMD_ID || result == -2) + { + MLIST_FREE1 (stage, cmds); + free (stage); + return NULL; + } + + return stage; +} + + +/* Load FLT of name LAYOUTER_NAME from the m17n database into a newly + allocated memory, and return it. */ + +static MFontLayoutTable * +load_flt (MSymbol layouter_name) +{ + MDatabase *mdb; + MPlist *top = NULL, *plist; + MSymbol Mcategory = msymbol ("category"); + MSymbol Mgenerator = msymbol ("generator"); + MSymbol Mend = msymbol ("end"); + MFontLayoutTable *layouter = NULL; + MCharTable *category = NULL; + + if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil))) + MERROR_GOTO (MERROR_FONT, finish); + if (! (top = (MPlist *) mdatabase_load (mdb))) + MERROR_GOTO (0, finish); + if (! MPLIST_PLIST_P (top)) + MERROR_GOTO (MERROR_FONT, finish); + + MPLIST_DO (plist, top) + { + MSymbol sym; + MPlist *elt; + + if (MPLIST_SYMBOL_P (plist) + && MPLIST_SYMBOL (plist) == Mend) + break; + if (! MPLIST_PLIST (plist)) + MERROR_GOTO (MERROR_FONT, finish); + elt = MPLIST_PLIST (plist); + if (! MPLIST_SYMBOL_P (elt)) + MERROR_GOTO (MERROR_FONT, finish); + sym = MPLIST_SYMBOL (elt); + elt = MPLIST_NEXT (elt); + if (! elt) + MERROR_GOTO (MERROR_FONT, finish); + if (sym == Mcategory) + { + if (category) + M17N_OBJECT_UNREF (category); + category = load_category_table (elt); + } + else if (sym == Mgenerator) + { + FontLayoutStage *stage; + + if (! category) + MERROR_GOTO (MERROR_FONT, finish); + stage = load_generator (elt); + if (! stage) + MERROR_GOTO (MERROR_FONT, finish); + stage->category = category; + M17N_OBJECT_REF (category); + if (! layouter) + { + layouter = mplist (); + /* Here don't do M17N_OBJECT_REF (category) because we + don't unref the value of the element added below. */ + mplist_add (layouter, Mcategory, category); + } + mplist_add (layouter, Mt, stage); + } + } + + if (category) + M17N_OBJECT_UNREF (category); + + finish: + M17N_OBJECT_UNREF (top); + mplist_add (flt_list, layouter_name, layouter); + return layouter; +} + + +static void +free_flt_stage (FontLayoutStage *stage) +{ + int i; + + M17N_OBJECT_UNREF (stage->category); + for (i = 0; i < stage->used; i++) + free_flt_command (stage->cmds + i); + MLIST_FREE1 (stage, cmds); + free (stage); +} + + +static MFontLayoutTable * +get_font_layout_table (MSymbol layouter_name) +{ + MPlist *plist = mplist_find_by_key (flt_list, layouter_name); + + return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name)); +} + + +/* FLS (Font Layout Service) */ + +/* Structure to hold information about a context of FLS. */ + +typedef struct +{ + /* Pointer to the current stage. */ + FontLayoutStage *stage; + + /* Pointer to the font. */ + MFLTFont *font; + + /* Input and output glyph string. */ + MFLTGlyphString *in, *out; + + /* Encode each character or code of a glyph by the current category + table into this array. An element is a category letter used for + a regular expression matching. */ + char *encoded; + /* [GIDX - ] gives a category for the glyph + index GIDX. */ + int encoded_offset; + int *match_indices; + int gstring_size; + int code_offset; + int cluster_begin_idx; + int cluster_begin_pos; + int cluster_end_pos; + int combining_code; + int left_padding; + int check_mask; +} FontLayoutContext; + +static int run_command (int, int, int, int, FontLayoutContext *); + +#define NMATCH 20 + +static int +run_rule (int depth, + FontLayoutCmdRule *rule, int from, int to, FontLayoutContext *ctx) +{ + int *saved_match_indices = ctx->match_indices; + int match_indices[NMATCH * 2]; + int consumed; + int i; + int orig_from = from; + + if (rule->src_type == SRC_SEQ) + { + int len; + + len = rule->src.seq.n_codes; + if (len > (to - from)) + return 0; + for (i = 0; i < len; i++) + if (rule->src.seq.codes[i] != GREF (ctx->in, from + i)->code) + break; + if (i < len) + return 0; + to = from + len; + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "", + rule->src.seq.codes[0]); + } + else if (rule->src_type == SRC_RANGE) + { + int head; + + if (from >= to) + return 0; + head = GREF (ctx->in, from)->code; + if (head < rule->src.range.from || head > rule->src.range.to) + return 0; + ctx->code_offset = head - rule->src.range.from; + to = from + 1; + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "", + rule->src.range.from, rule->src.range.to); + } + else if (rule->src_type == SRC_REGEX) + { + regmatch_t pmatch[NMATCH]; + char saved_code; + int result; + + if (from > to) + return 0; + saved_code = ctx->encoded[to - ctx->encoded_offset]; + ctx->encoded[to - ctx->encoded_offset] = '\0'; + result = regexec (&(rule->src.re.preg), + ctx->encoded + from - ctx->encoded_offset, + NMATCH, pmatch, 0); + if (result == 0 && pmatch[0].rm_so == 0) + { + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "", + rule->src.re.pattern, + ctx->encoded + from - ctx->encoded_offset, + pmatch[0].rm_eo); + ctx->encoded[to - ctx->encoded_offset] = saved_code; + for (i = 0; i < NMATCH; i++) + { + if (pmatch[i].rm_so < 0) + match_indices[i * 2] = match_indices[i * 2 + 1] = -1; + else + { + match_indices[i * 2] = from + pmatch[i].rm_so; + match_indices[i * 2 + 1] = from + pmatch[i].rm_eo; + } + } + ctx->match_indices = match_indices; + to = match_indices[1]; + } + else + { + ctx->encoded[to - ctx->encoded_offset] = saved_code; + return 0; + } + } + else if (rule->src_type == SRC_INDEX) + { + if (rule->src.match_idx >= NMATCH) + return 0; + from = ctx->match_indices[rule->src.match_idx * 2]; + if (from < 0) + return 0; + to = ctx->match_indices[rule->src.match_idx * 2 + 1]; + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT3 ("\n [FLT] %*s(INDEX %d", depth, "", rule->src.match_idx); + } + + consumed = 0; + depth++; + for (i = 0; i < rule->n_cmds; i++) + { + int pos; + + if (rule->cmd_ids[i] == CMD_ID_REPEAT) + { + if (! consumed) + continue; + i--; + } + pos = run_command (depth, rule->cmd_ids[i], from, to, ctx); + if (pos < 0) + MERROR (MERROR_DRAW, -1); + consumed = pos > from; + if (consumed) + from = pos; + } + + ctx->match_indices = saved_match_indices; + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT (")"); + return (rule->src_type == SRC_INDEX ? orig_from : to); +} + +static int +run_cond (int depth, + FontLayoutCmdCond *cond, int from, int to, FontLayoutContext *ctx) +{ + int i, pos = 0; + + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, ""); + depth++; + for (i = 0; i < cond->n_cmds; i++) + { + /* TODO: Write a code for optimization utilizaing the info + cond->seq_XXX. */ + if ((pos = run_command (depth, cond->cmd_ids[i], from, to, ctx)) + != 0) + break; + } + if (pos < 0) + MERROR (MERROR_DRAW, -1); + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT (")"); + return (pos); +} + +static int +run_otf (int depth, + MFLT_OTF_Spec *otf_spec, int from, int to, FontLayoutContext *ctx) +{ +#ifdef HAVE_OTF + MFLTFont *font = ctx->font; + int from_idx = ctx->out->used; + MFLTGlyphAdjustment *adjustment; + int out_len; + int i; + + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT3 ("\n [FLT] %*s%s", depth, "", MSYMBOL_NAME (otf_spec->sym)); + for (i = from; i < to; i++) + { + MFLTGlyph *g = GREF (ctx->in, i); + + if (! (GET_ENCODED (g))) + { + font->get_glyph_id (font, g); + SET_ENCODED (g, ctx, EncodedMask); + } + } + adjustment = alloca ((sizeof *adjustment) + * (ctx->out->allocated - ctx->out->used)); + if (! adjustment) + MERROR (MERROR_FLT, -1); + memset (adjustment, 0, + (sizeof *adjustment) * (ctx->out->allocated - ctx->out->used)); + to = font->drive_otf (font, otf_spec, ctx->in, from, to, ctx->out, adjustment); + if (to < 0) + return to; + out_len = ctx->out->used - from_idx; + if (otf_spec->gpos_count > 0) + { + MFLTGlyphAdjustment *a; + + for (i = 0, a = adjustment; i < out_len; i++, a++) + if (a->set) + break; + if (i < out_len) + { + font->get_metric (font, ctx->out, from_idx, ctx->out->used); + for (i = 0, a = adjustment; i < out_len; i++, a++) + { + MFLTGlyph *g = GREF (ctx->out, from_idx + i); + + SET_POSITIONED (g, ctx, PositionedMask); + if (a->xadv || a->yadv) + { + if (a->advance_is_absolute) + { + g->xadv = a->xadv; + g->yadv = a->yadv; + } + else + { + g->xadv += a->xadv; + g->yadv += a->yadv; + } + } + if (a->xoff || a->yoff) + { + int j; + MFLTGlyph *gg = g; + MFLTGlyphAdjustment *aa = a; + + g->xoff = a->xoff; + g->yoff = a->yoff; + while (aa->back > 0) + { + for (j = 0, gg--; j < aa->back; j++, gg--) + g->xoff -= gg->xadv; + aa = aa - aa->back; + g->xoff += aa->xoff; + g->yoff += aa->yoff; + } + } + } + } + } + + if (ctx->cluster_begin_idx) + for (; from_idx < ctx->out->used; from_idx++) + { + MFLTGlyph *g = GREF (ctx->out, from_idx); + UPDATE_CLUSTER_RANGE (ctx, g); + } +#endif + return to; +} + +static char work[16]; + +static char * +dump_combining_code (int code) +{ + char *vallign = "tcbB"; + char *hallign = "lcr"; + char *p; + int off_x, off_y; + + if (! code) + return "none"; + work[0] = vallign[COMBINING_CODE_BASE_Y (code)]; + work[1] = hallign[COMBINING_CODE_BASE_X (code)]; + off_y = COMBINING_CODE_OFF_Y (code); + off_x = COMBINING_CODE_OFF_X (code); + if (off_y > 0) + sprintf (work + 2, "+%d", off_y); + else if (off_y < 0) + sprintf (work + 2, "%d", off_y); + else if (off_x == 0) + sprintf (work + 2, "."); + p = work + strlen (work); + if (off_x > 0) + sprintf (p, ">%d", off_x); + else if (off_x < 0) + sprintf (p, "<%d", -off_x); + p += strlen (p); + p[0] = vallign[COMBINING_CODE_ADD_Y (code)]; + p[1] = hallign[COMBINING_CODE_ADD_X (code)]; + p[2] = '\0'; + return work; +} + +static int +run_command (int depth, int id, int from, int to, FontLayoutContext *ctx) +{ + MFLTGlyph *g; + + if (id >= 0) + { + int i; + + /* Direct code (== ctx->code_offset + id) output. + The source is not consumed. */ + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT3 ("\n [FLT] %*s(DIRECT 0x%X", depth, "", + ctx->code_offset + id); + i = from < to ? from : from - 1; + GDUP (ctx, i); + g = GREF (ctx->out, ctx->out->used - 1); + g->code = ctx->code_offset + id; + if (ctx->combining_code) + SET_COMBINING_CODE (g, ctx, ctx->combining_code); + if (ctx->left_padding) + SET_LEFT_PADDING (g, ctx, LeftPaddingMask); + for (i = from; i < to; i++) + { + MFLTGlyph *tmp = GREF (ctx->in, i); + + if (g->from > tmp->from) + g->from = tmp->from; + else if (g->to < tmp->to) + g->to = tmp->to; + } + UPDATE_CLUSTER_RANGE (ctx, g); + ctx->code_offset = ctx->combining_code = ctx->left_padding = 0; + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT (")"); + return (from); + } + + if (id <= CMD_ID_OFFSET_INDEX) + { + int idx = CMD_ID_TO_INDEX (id); + FontLayoutCmd *cmd; + + if (idx >= ctx->stage->used) + MERROR (MERROR_DRAW, -1); + cmd = ctx->stage->cmds + idx; + if (cmd->type == FontLayoutCmdTypeRule) + to = run_rule (depth, &cmd->body.rule, from, to, ctx); + else if (cmd->type == FontLayoutCmdTypeCond) + to = run_cond (depth, &cmd->body.cond, from, to, ctx); + else if (cmd->type == FontLayoutCmdTypeOTF) + to = run_otf (depth, &cmd->body.otf, from, to, ctx); + if (to < 0) + MERROR (MERROR_FLT, -1); + return to; + } + + if (id <= CMD_ID_OFFSET_COMBINING) + { + ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id); + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT3 ("\n [FLT] %*s(CMB %s)", depth, "", + dump_combining_code (ctx->combining_code)); + return from; + } + + switch (id) + { + case CMD_ID_COPY: + { + if (from >= to) + return from; + GDUP (ctx, from); + g = GREF (ctx->out, ctx->out->used - 1); + if (ctx->combining_code) + SET_COMBINING_CODE (g, ctx, ctx->combining_code); + if (ctx->left_padding) + SET_LEFT_PADDING (g, ctx, LeftPaddingMask); + UPDATE_CLUSTER_RANGE (ctx, g); + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g->code); + ctx->code_offset = ctx->combining_code = ctx->left_padding = 0; + return (from + 1); + } + + case CMD_ID_CLUSTER_BEGIN: + if (! ctx->cluster_begin_idx) + { + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT3 ("\n [FLT] %*s<%d", depth, "", + GREF (ctx->in, from)->from); + ctx->cluster_begin_idx = ctx->out->used; + ctx->cluster_begin_pos = GREF (ctx->in, from)->from; + ctx->cluster_end_pos = GREF (ctx->in, from)->to; + } + return from; + + case CMD_ID_CLUSTER_END: + if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < ctx->out->used) + { + int i; + + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos); + for (i = ctx->cluster_begin_idx; i < ctx->out->used; i++) + { + GREF (ctx->out, i)->from = ctx->cluster_begin_pos; + GREF (ctx->out, i)->to = ctx->cluster_end_pos; + } + ctx->cluster_begin_idx = 0; + } + return from; + + case CMD_ID_SEPARATOR: + { + int i; + + i = from < to ? from : from - 1; + GDUP (ctx, i); + g = GREF (ctx->out, ctx->out->used - 1); + g->c = -1, g->code = 0; + g->xadv = g->yadv = 0; + SET_ENCODED (g, ctx, 0); + return from; + } + + case CMD_ID_LEFT_PADDING: + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT2 ("\n [FLT] %*s[", depth, ""); + ctx->left_padding = 1; + return from; + + case CMD_ID_RIGHT_PADDING: + if (ctx->out->used > 0) + { + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT2 ("\n [FLT] %*s]", depth, ""); + g = GREF (ctx->out, ctx->out->used - 1); + SET_RIGHT_PADDING (g, ctx, RightPaddingMask); + } + return from; + } + + MERROR (MERROR_DRAW, -1); +} + +static void +positioning (MFLTFont *font, MFLTGlyphString *gstring, int from, int to) +{ + int i, j; + + for (i = j = from; i < to; i++) + { + MFLTGlyph *g = GREF (gstring, i); + + if (GET_POSITIONED (g)) + { + if (j < i) + font->get_metric (font, gstring, j, i); + j = i + 1; + } + } + if (j < i) + font->get_metric (font, gstring, j, i); +} + +static int +run_stages (MFLTGlyphString *gstring, int from, int to, + MFontLayoutTable *layouter, FontLayoutContext *ctx) +{ + MFLTGlyphString buf, *temp; + int stage_idx = 0; + int orig_from = from, orig_to = to; + int from_pos, to_pos, len; + int i, j; + + from_pos = GREF (ctx->in, from)->from; + to_pos = GREF (ctx->in, to - 1)->to; + len = to_pos - from_pos; + + buf = *(ctx->in); + buf.glyphs = NULL; + GINIT (ctx->out, ctx->out->allocated); + ctx->encoded = alloca (ctx->out->allocated); + if (! ctx->out->glyphs || ! ctx->encoded) + return -1; + + for (stage_idx = 0; 1; stage_idx++) + { + MCharTable *table; + int result; + + ctx->stage = (FontLayoutStage *) MPLIST_VAL (layouter); + table = ctx->stage->category; + ctx->code_offset = ctx->combining_code = ctx->left_padding = 0; + if (ctx->encoded_offset < from) + { + for (i = ctx->encoded_offset; i < from; i++) + ctx->encoded[i] + = (int) mchartable_lookup (table, GREF (ctx->in, i)->c); + ctx->encoded[i++] = ' '; + } + for (i = from; i < to; i++) + { + MFLTGlyph *g = GREF (ctx->in, i); + char enc = (GET_ENCODED (g) + ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1) + : g->code + ? (int) mchartable_lookup (table, g->code) + : ' '); + + ctx->encoded[i - ctx->encoded_offset] = enc; + if (! enc && stage_idx == 0) + { + to = i; + break; + } + } + ctx->encoded[i - ctx->encoded_offset] = '\0'; + ctx->match_indices[0] = from; + ctx->match_indices[1] = to; + for (i = 2; i < NMATCH; i++) + ctx->match_indices[i] = -1; + + if (MDEBUG_FLAG () > 2) + { + MDEBUG_PRINT2 ("\n [FLT] (STAGE %d \"%s\"", stage_idx, ctx->encoded); + MDEBUG_PRINT (" ("); + for (i = from; i < to; i++) + MDEBUG_PRINT3 ("%*s%04X", (i > 0), "", GREF (ctx->in, i)->code); + MDEBUG_PRINT (")"); + } + result = run_command (4, INDEX_TO_CMD_ID (0), from, to, ctx); + if (MDEBUG_FLAG () > 2) + MDEBUG_PRINT (")"); + if (result < 0) + return result; + + layouter = MPLIST_NEXT (layouter); + /* If this is the last stage, break the loop. */ + if (MPLIST_TAIL_P (layouter)) + break; + + /* Otherwise, prepare for the next stage. */ + temp = ctx->in; + ctx->in = ctx->out; + if (buf.glyphs) + ctx->out = temp; + else + { + GINIT (&buf, ctx->out->allocated); + ctx->out = &buf; + } + ctx->out->used = 0; + + ctx->encoded_offset = 0; + from = 0; + to = ctx->in->used; + } + + if (ctx->out->used > 0) + { + MFLTGlyph *g; + int *g_indices; + int x_ppem = ctx->font->x_ppem << 6, y_ppem = ctx->font->y_ppem << 6; + + /* Remove separator glyphs. */ + for (i = 0; i < ctx->out->used;) + { + g = GREF (ctx->out, i); + if (g->c < 0) + GREPLACE (NULL, 0, 0, ctx->out, i, i + 1); + else + i++; + } + + /* Get actual glyph IDs of glyphs. */ + for (i = 0; i < ctx->out->used; i++) + { + g = GREF (ctx->out, i); + if (! GET_ENCODED (g)) + ctx->font->get_glyph_id (ctx->font, g); + } + + /* Check if all characters in the range are covered by some + glyph(s). If not, change and of glyphs to cover + uncovered characters. */ + g_indices = alloca (sizeof (int) * len); + if (! g_indices) + return -1; + for (i = 0; i < len; i++) g_indices[i] = -1; + for (i = 0; i < ctx->out->used; i++) + { + int pos; + + g = GREF (ctx->out, i); + for (pos = g->from; pos <= g->to; pos++) + if (g_indices[pos - orig_from] < 0) + g_indices[pos - orig_from] = i; + } + for (i = 0; i < len; i++) + if (! g_indices[i]) + { + if (i == 0) + { + int this_from; + + for (i++; i < len && g_indices[i] < 0; i++); + j = g_indices[i]; + g = GREF (ctx->out, j); + this_from = g->from; + do { + g->from = orig_from + i; + } while (++j < ctx->out->used + && (g = GREF (ctx->out, j)) + && g->from == this_from); + } + else + { + int this_to; + + j = g_indices[i - 1]; + g = GREF (ctx->out, j); + this_to = g->to; + do { + g->to = orig_from + i + 1; + } while (--j >= 0 + && (g = GREF (ctx->out, j)) + && g->to == this_to); + } + } + + positioning (ctx->font, ctx->out, 0, ctx->out->used); + + /* Handle combining. */ + if (ctx->check_mask & CombiningCodeMask) + { + MFLTGlyph *base = GREF (ctx->out, 0); + int base_height = base->ascent + base->descent; + int combining_code; + + for (i = 1; i < ctx->out->used; i++) + { + if ((g = GREF (ctx->out, i)) + && (combining_code = GET_COMBINING_CODE (g))) + { + int height = g->ascent + g->descent; + int base_x, base_y, add_x, add_y, off_x, off_y; + + if (base->from > g->from) + base->from = g->from; + else if (base->to < g->to) + base->to = g->to; + + base_x = COMBINING_CODE_BASE_X (combining_code); + base_y = COMBINING_CODE_BASE_Y (combining_code); + add_x = COMBINING_CODE_ADD_X (combining_code); + add_y = COMBINING_CODE_ADD_Y (combining_code); + off_x = COMBINING_CODE_OFF_X (combining_code); + off_y = COMBINING_CODE_OFF_Y (combining_code); + + g->xoff = ((base->xadv * base_x - g->xadv * add_x) / 2 + + x_ppem * off_x / 100 - base->xadv); + if (base_y < 3) + g->yoff = base_height * base_y / 2 - base->ascent; + else + g->yoff = 0; + if (add_y < 3) + g->yoff -= height * add_y / 2 - g->ascent; + g->yoff -= y_ppem * off_y / 100; + if (base->lbearing > base->xadv + g->lbearing + g->xoff) + base->lbearing = base->xadv + g->lbearing + g->xoff; + if (base->rbearing < base->xadv + g->xadv + g->xoff) + base->rbearing = base->xadv + g->xadv + g->xoff; + if (base->ascent < g->ascent - g->yoff) + base->ascent = g->ascent - g->yoff; + if (base->descent < g->descent - g->yoff) + base->descent = g->descent - g->yoff; + g->xadv = g->yadv = 0; + if (GET_RIGHT_PADDING (g)) + SET_RIGHT_PADDING (base, ctx, RightPaddingMask); + } + else + { + base = g; + base_height = g->ascent + g->descent; + } + } + } + + /* Handle padding */ + if (ctx->check_mask & (LeftPaddingMask | RightPaddingMask)) + for (i = 0; i < ctx->out->used; i++) + { + g = GREF (ctx->out, i); + if (! GET_COMBINING_CODE (g)) + { + if (GET_LEFT_PADDING (g) && g->lbearing < 0) + { + g->xoff -= g->lbearing; + if (g->rbearing < 0) + g->xadv = g->rbearing - g->lbearing; + else + g->xadv += g->xoff; + g->rbearing += g->xoff; + g->lbearing = 0; + } + if (i > 0 && GET_RIGHT_PADDING (g) && g->rbearing > g->xadv) + { + g->xadv = g->rbearing; + } + } + } + } + + GREPLACE (ctx->out, 0, ctx->out->used, gstring, orig_from, orig_to); + to = orig_from + ctx->out->used; + return to; +} + + +/* Internal API */ + +int m17n__flt_initialized; + +unsigned +mfont__flt_encode_char (MSymbol layouter_name, int c) +{ + MFontLayoutTable *layouter = get_font_layout_table (layouter_name); + MCharTable *table; + unsigned code; + + if (! layouter) + return MCHAR_INVALID_CODE; + table = MPLIST_VAL (layouter); + code = (unsigned) mchartable_lookup (table, c); + return (code ? code : MCHAR_INVALID_CODE); +} + + +/* External API */ + +/* The following two are actually not exposed to a user but concealed + by the macro M17N_INIT (). */ + +void +m17n_init_flt (void) +{ + int mdebug_flag = MDEBUG_INIT; + + merror_code = MERROR_NONE; + if (m17n__flt_initialized++) + return; + m17n_init (); + if (merror_code != MERROR_NONE) + { + m17n__flt_initialized--; + return; + } + + MDEBUG_PUSH_TIME (); + + Mcond = msymbol ("cond"); + Mrange = msymbol ("range"); + Mfont = msymbol ("font"); + Mlayouter = msymbol ("layouter"); + flt_list = mplist (); + + MDEBUG_POP_TIME (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the flt modules.")); + MDEBUG_POP_TIME (); + + return; +} + +void +m17n_fini_flt (void) +{ + int mdebug_flag = MDEBUG_FINI; + MPlist *plist, *pl; + + if (m17n__flt_initialized == 0 + || --m17n__flt_initialized > 0) + return; + + MDEBUG_PUSH_TIME (); + MDEBUG_PUSH_TIME (); + + MPLIST_DO (plist, flt_list) + { + pl = MPLIST_PLIST (plist); + if (pl) + { + MPLIST_DO (pl, MPLIST_NEXT (pl)) + free_flt_stage (MPLIST_VAL (pl)); + pl = MPLIST_PLIST (plist); + M17N_OBJECT_UNREF (pl); + } + } + M17N_OBJECT_UNREF (flt_list); + + MDEBUG_POP_TIME (); + MDEBUG_PRINT_TIME ("FINI", (stderr, " to finalize the flt modules.")); + MDEBUG_POP_TIME (); + m17n_fini (); +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + +int +mflt_run (MFLTGlyphString *gstring, int from, int to, + MFLTFont *font, MSymbol layouter_name) +{ + int i, j; + FontLayoutContext ctx; + MCharTable *table; + int match_indices[NMATCH]; + MFontLayoutTable *layouter = get_font_layout_table (layouter_name); + FontLayoutStage *stage; + MFLTGlyph *g; + MFLTGlyphString out; + + if (! layouter) + { + GREPLACE (NULL, 0, 0, gstring, from, to); + return from; + } + + MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name)); + + /* Setup CTX. */ + memset (&ctx, 0, sizeof ctx); + table = MPLIST_VAL (layouter); + layouter = MPLIST_NEXT (layouter); + stage = (FontLayoutStage *) MPLIST_VAL (layouter); + /* Find previous glyphs that are also supported by the layouter. */ + for (i = from; + i > 0 && (g = GREF (gstring, i - 1)) + && g->c && mchartable_lookup (table, g->c); + i--) + g->code = g->c; + ctx.encoded_offset = i; + for (i = from; i < to; i++) + { + int c; + + g = GREF (gstring, i); + c = g->c; + memset (g, 0, sizeof (MFLTGlyph)); + g->code = g->c = c; + g->from = g->to = i; + } + + ctx.match_indices = match_indices; + + ctx.font = font; + ctx.in = gstring; + out = *gstring; + out.glyphs = NULL; + /* This is usually sufficient, but if not, we retry with the larger + values at most 3 times. This value is also used for the + allocating size of ctx.encoded. */ + out.allocated = (to - ctx.encoded_offset + 2) * 4; + ctx.out = &out; + + if (MDEBUG_FLAG ()) + { + MDEBUG_PRINT ("\n [FLT] (SOURCE"); + for (i = from, j = 0; i < to; i++, j++) + { + if (j > 0 && j % 8 == 0) + MDEBUG_PRINT ("\n [FLT} "); + MDEBUG_PRINT1 (" %04X", GREF (gstring, i)->c); + } + MDEBUG_PRINT (")"); + } + + for (i = 0; (i < 3 && + (to = run_stages (gstring, from, to, layouter, &ctx)) == -2); + i++) + { + ctx.out = &out; + ctx.out->allocated *= 2; + } + + for (i = from; i < to; i++) + { + MFLTGlyph *g = GREF (gstring, i); + + g->ascent >>= 6; + g->descent >>= 6; + g->lbearing >>= 6; + g->rbearing >>= 6; + g->xadv >>= 6; + g->yadv >>= 6; + g->xoff >>= 6; + g->yoff >>= 6; + } + + if (MDEBUG_FLAG ()) + { + MDEBUG_PRINT ("\n [FLT] (RESULT"); + if (MDEBUG_FLAG () > 1) + for (i = 0; from < to; from++, i++) + { + if (i > 0 && i % 4 == 0) + MDEBUG_PRINT ("\n [FLT] "); + g = GREF (gstring, from); + MDEBUG_PRINT4 (" (%04X %d %d %d)", + g->code, g->xadv, g->xoff, g->yoff); + } + else + for (; from < to; from++) + MDEBUG_PRINT1 (" %04X", GREF (gstring, from)->code); + MDEBUG_PRINT ("))\n"); + } + + return (to < 0 ? -1 : to); +} + + +/* for debugging... */ + +static void +dump_flt_cmd (FontLayoutStage *stage, int id, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + + memset (prefix, 32, indent); + prefix[indent] = 0; + + if (id >= 0) + fprintf (stderr, "0x%02X", id); + else if (id <= CMD_ID_OFFSET_INDEX) + { + int idx = CMD_ID_TO_INDEX (id); + FontLayoutCmd *cmd = stage->cmds + idx; + + if (cmd->type == FontLayoutCmdTypeRule) + { + FontLayoutCmdRule *rule = &cmd->body.rule; + int i; + + fprintf (stderr, "(rule "); + if (rule->src_type == SRC_REGEX) + fprintf (stderr, "\"%s\"", rule->src.re.pattern); + else if (rule->src_type == SRC_INDEX) + fprintf (stderr, "%d", rule->src.match_idx); + else if (rule->src_type == SRC_SEQ) + fprintf (stderr, "(seq)"); + else if (rule->src_type == SRC_RANGE) + fprintf (stderr, "(range)"); + else + fprintf (stderr, "(invalid src)"); + + for (i = 0; i < rule->n_cmds; i++) + { + fprintf (stderr, "\n%s ", prefix); + dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2); + } + fprintf (stderr, ")"); + } + else if (cmd->type == FontLayoutCmdTypeCond) + { + FontLayoutCmdCond *cond = &cmd->body.cond; + int i; + + fprintf (stderr, "(cond"); + for (i = 0; i < cond->n_cmds; i++) + { + fprintf (stderr, "\n%s ", prefix); + dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2); + } + fprintf (stderr, ")"); + } + else if (cmd->type == FontLayoutCmdTypeOTF) + { + fprintf (stderr, "(otf)"); + } + else + fprintf (stderr, "(error-command)"); + } + else if (id <= CMD_ID_OFFSET_COMBINING) + fprintf (stderr, "cominging-code"); + else + fprintf (stderr, "(predefiend %d)", id); +} + +void +mdebug_dump_flt (MFontLayoutTable *flt, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + MPlist *plist; + int stage_idx = 0; + + memset (prefix, 32, indent); + prefix[indent] = 0; + fprintf (stderr, "(flt"); + MPLIST_DO (plist, flt) + { + FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist); + int i; + + fprintf (stderr, "\n%s (stage %d", prefix, stage_idx); + for (i = 0; i < stage->used; i++) + { + fprintf (stderr, "\n%s ", prefix); + dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4); + } + fprintf (stderr, ")"); + stage_idx++; + } + fprintf (stderr, ")"); +} -- 1.7.10.4