--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#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:
+;; <http://www.microsoft.om/typograph/otspec/scripttags.htm>
+LANGSYS ::= OTF-TAG
+;; OTF's Language System name (four letters) listed at:
+;; <http://www.microsoft.om/typograph/otspec/languagetags.htm>
+
+GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
+GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
+FEATURE ::= OTF-TAG
+;; OTF's Feature name (four letters) listed at:
+;; <http://www.microsoft.om/typograph/otspec/???.htm>
+
+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;
+ /* <encoded>[GIDX - <encoded_offset>] 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 <from> and <to> 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;
+}
+
+\f
+/* 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);
+}
+
+\f
+/* 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);
+}
+
+\f
+/* 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, ")");
+}