From: handa Date: Wed, 15 Aug 2007 04:13:13 +0000 (+0000) Subject: *** empty log message *** X-Git-Url: http://git.chise.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a2353ac47a05b556a97dfee5ef0c90b4bd465a52;p=m17n%2Fm17n-test.git *** empty log message *** --- diff --git a/Makefile.am b/Makefile.am index 5fe85e1..9fd4cc1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,13 +1,32 @@ noinst_PROGRAMS = \ linebreak replace mimdemo mimdaemon fifotest1 fifotest2 \ - imtest findfont gdkdraw + imtest findfont gdkdraw finddb imsim langname viewfile pangotest \ + flt-gui flt-otf flt-hb mimdemo_LDADD = -lgd mimdaemon_LDADD = -lgd gdkdraw_LDADD = -lgd gdkdraw_LDFLAGS = `pkg-config --libs gdk-pixbuf-2.0` `pkg-config --libs gtk+-2.0` -AM_CPPFLAGS = `pkg-config --cflags gdk-pixbuf-2.0` `pkg-config --cflags gtk+-2.0` +AM_CPPFLAGS = `pkg-config --cflags gdk-pixbuf-2.0` `pkg-config --cflags gtk+-2.0` `pkg-config --cflags pangocairo` + +viewfile_LDFLAGS = `pkg-config --libs gtk+-2.0` +pangotest_LDFLAGS = `pkg-config --libs pangocairo` + +baseCFLAGS = @FT2_CFLAGS@ @FC_CFLAGS@ +baseLIBS = @FT2_LIBS@ @FC_LIBS@ + +flt_gui_SOURCES = flt.c +flt_gui_CFLAGS = ${baseCFLAGS} @M17NGUI_CFLAGS@ -DFLT_GUI +flt_gui_LDFLAGS = ${baseLIBS} @M17NGUI_LIBS@ + +flt_otf_SOURCES = flt.c +flt_otf_CFLAGS = ${baseCFLAGS} @M17NFLT_CFLAGS@ @OTF_CFLAGS@ -DFLT_OTF +flt_otf_LDFLAGS = ${baseLIBS} @M17NFLT_LIBS@ @OTF_LIBS@ + +flt_hb_SOURCES = flt.c +flt_hb_CFLAGS = ${baseCFLAGS} @M17NFLT_CFLAGS@ @HB_CFLAGS@ -DFLT_HB +flt_hb_LDFLAGS = ${baseLIBS} @M17NFLT_LIBS@ @HB_LIBS@ verify: linebreak @echo "checking line breaking..." diff --git a/configure.ac b/configure.ac index 20209cb..c698a44 100644 --- a/configure.ac +++ b/configure.ac @@ -1,13 +1,25 @@ AC_INIT(m17n-test, 1.0.0, m17n-lib-bug@m17n.org) AM_INIT_AUTOMAKE(m17n-test, 1.0.0) - AC_PROG_CC +AM_PROG_CC_C_O AC_PATH_XTRA -PKG_CHECK_MODULES(M17N, m17n-gui) +PKG_CHECK_MODULES(FT2, freetype2) +PKG_CHECK_MODULES(FC, fontconfig) +PKG_CHECK_MODULES(M17NGUI, m17n-gui) +PKG_CHECK_MODULES(M17NFLT, m17n-flt) +PKG_CHECK_MODULES(OTF, libotf) + +AC_ARG_WITH(harfbuzz, [Harfbuzz source directory], HBDIR="$withval", HBDIR="") +if test -d "$HBDIR/src/.libs"; then + HB_CFLAGS="-I$HBDIR/src" + for o in global open stream buffer gdef gsub gpos; do + HB_LIBS="$HB_LIBS $HBDIR/src/.libs/harfbuzz-$o.o" + done +fi -LDFLAGS="$LDFLAGS $M17N_LIBS" -CPPFLAGS="$CPPFLAGS $M17N_CFLAGS" +AC_SUBST(HB_CFLAGS) +AC_SUBST(HB_LIBS) AC_CONFIG_FILES(Makefile) diff --git a/flt.c b/flt.c new file mode 100644 index 0000000..d7cf04a --- /dev/null +++ b/flt.c @@ -0,0 +1,1030 @@ +#include +#include +#include +#include FT_FREETYPE_H +#include FT_TRUETYPE_TABLES_H +#include +#include + +#if defined (FLT_GUI) + +#include +#define PROGNAME "flt-gui" + +#elif defined (FLT_OTF) + +#include +#include +#define PROGNAME "flt-otf" + +#else /* defined (FLT_HB) */ + +#include +#include +#define PROGNAME "flt-hb" + +#endif + +static FT_Library ft_library; + +FT_Face +new_face (char *fontname, char **err) +{ + FcPattern *pat; + FcChar8 *fam, *file; + double pixelsize; + FcFontSet *fs; + FcObjectSet *os; + FT_Face face; + + pat = FcNameParse ((FcChar8 *) fontname); + if (! pat) + { + *err = "invalid name format"; + return NULL; + } + if (FcPatternGetString (pat, FC_FAMILY, 0, &fam) != FcResultMatch) + { + *err = "no family name"; + return NULL; + } + if (FcPatternGetDouble (pat, FC_PIXEL_SIZE, 0, &pixelsize) != FcResultMatch) + pixelsize = 20; + FcPatternAddBool (pat, FC_SCALABLE, FcTrue); + FcPatternDel (pat, FC_PIXEL_SIZE); + os = FcObjectSetBuild (FC_FILE, NULL); + fs = FcFontList (NULL, pat, os); + if (fs->nfont == 0) + { + *err = "no matching font"; + return NULL; + } + FcPatternGetString (fs->fonts[0], FC_FILE, 0, &file); + + if (FT_New_Face (ft_library, (char *) file, 0, &face)) + { + FcFontSetDestroy (fs); + FcObjectSetDestroy (os); + FcPatternDestroy (pat); + *err = "font open fail"; + return NULL; + } + FcFontSetDestroy (fs); + FcObjectSetDestroy (os); + FcPatternDestroy (pat); + if (FT_Set_Pixel_Sizes (face, pixelsize, pixelsize)) + { + *err = "set pixelsize fail"; + FT_Done_Face (face); + return NULL; + } + return face; +} + +#ifdef FLT_GUI + +typedef struct +{ + MFont *font; + FT_Face face; +} MFLTFont; + +MFrame *frame; + +MFLTFont * +open_font (char *fontname, char **err) +{ + FT_Face ft_face = new_face (fontname, err); + MFLTFont *font; + MFace *face; + MPlist *plist; + MFontset *fontset; + + if (! ft_face) + return NULL; + face = mface (); + plist = mplist (); + fontset = mfontset ("generic"); + mface_put_prop (face, Mfontset, fontset); + mplist_add (plist, Mface, face); + mplist_add (plist, Mdevice, Mnil); + frame = mframe (plist); + m17n_object_unref (plist); + m17n_object_unref (face); + m17n_object_unref (fontset); + + font = calloc (1, sizeof (MFLTFont)); + font->font = mfont_encapsulate (frame, Mfreetype, ft_face); + font->face = ft_face; + return font; +} + +void +close_font (MFLTFont *font) +{ + FT_Done_Face (font->face); + free (font); + m17n_object_unref (frame); +} + +int +flt (MText *mt, int from, int to, MFLTFont *font, char *flt_name) +{ + mtext_put_prop (mt, from, to, Mfont, font->font); + mdraw_text_extents (frame, mt, from, to, NULL, NULL, NULL, NULL); + return 0; +} + +#else /* FLT_OTF or FLT_HB */ + +typedef struct { + MFLTFont font; + FT_Face face; +} FontInfo; + +int +get_glyph_id (MFLTFont *font, MFLTGlyph *g) +{ + FT_Face face = ((FontInfo *) font)->face; + + g->code = FT_Get_Char_Index (face, g->code); + + return (g->code ? 0 : -1); +} + +int +get_metric (MFLTFont *font, MFLTGlyphString *gstring, int from, int to) +{ + FT_Face face = ((FontInfo *) font)->face; + + for (; from < to; from++) + { + MFLTGlyph *g = gstring->glyphs + from; + FT_Glyph_Metrics *metrics; + + if (FT_Load_Glyph (face, g->code, FT_LOAD_DEFAULT)) + return -1; + metrics = &face->glyph->metrics; + g->lbearing = metrics->horiBearingX; + g->rbearing = metrics->horiBearingX + metrics->width; + g->xadv = metrics->horiAdvance; + g->yadv = metrics->vertAdvance; + g->ascent = metrics->horiBearingY; + g->descent = metrics->height - metrics->horiBearingY; + } + return 0; +} + +int +flt (MText *mt, int from, int to, MFLTFont *font, char *flt_name) +{ + MFLTGlyphString gstring; + int len = to - from; + int i; + + memset (&gstring, 0, sizeof (MFLTGlyphString)); + gstring.glyph_size = sizeof (MFLTGlyph); + gstring.allocated = len * 2; + gstring.glyphs = alloca (sizeof (MFLTGlyph) * gstring.allocated); + for (i = 0; i < len; i++) + gstring.glyphs[i].c = mtext_ref_char (mt, from + i); + gstring.used = len; + return mflt_run (&gstring, 0, len, font, msymbol (flt_name)); +} + +#ifdef FLT_OTF + +typedef struct { + MFLTFont font; + FT_Face face; + OTF *otf; +} FontInfoOTF; + +#define DEVICE_DELTA(table, size) \ + (((size) >= (table).StartSize && (size) <= (table).EndSize) \ + ? ((table).DeltaValue[(size) - (table).StartSize] << 6) \ + : 0) + +static void +adjust_anchor (OTF_Anchor *anchor, FT_Face face, + unsigned code, unsigned x_ppem, unsigned y_ppem, int *x, int *y) +{ + if (anchor->AnchorFormat == 2) + { + FT_Outline *outline; + int ap = anchor->f.f1.AnchorPoint; + + FT_Load_Glyph (face, (FT_UInt) code, FT_LOAD_MONOCHROME); + outline = &face->glyph->outline; + if (ap < outline->n_points) + { + *x = outline->points[ap].x; + *y = outline->points[ap].y; + } + } + else if (anchor->AnchorFormat == 3) + { + if (anchor->f.f2.XDeviceTable.offset) + *x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, x_ppem); + if (anchor->f.f2.YDeviceTable.offset) + *y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, y_ppem); + } +} + +char * +tag_name (char *str, unsigned tag) +{ + *str++ = tag >> 24; + *str++ = (tag >> 16) & 0xFF; + *str++ = (tag >> 8) & 0xFF; + *str++ = tag & 0xFF; + return str; +} + +void +encode_features (char *str, int count, unsigned *features) +{ + int all = 0; + int i; + + for (i = 0; i < count; i++) + { + unsigned tag = features[i]; + + if (tag == 0) + all = 1; + else + { + if (i > 0) + *str++ = ','; + if (all) + *str++ = '~'; + str = tag_name (str, tag); + } + } + if (all) + { + if (i > 1) + *str++ = ','; + *str++ = '*'; + } + *str = '\0'; +} + +int +drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec, + MFLTGlyphString *in, int from, int to, + MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment) +{ + int len = to - from; + int i, gidx; + OTF *otf = ((FontInfoOTF *) font)->otf; + OTF_GlyphString otf_gstring; + OTF_Glyph *otfg; + char script[4], langsys[4]; + char *gsub_features = NULL, *gpos_features = NULL; + + if (len == 0) + return from; + + if (! otf) + goto simple_copy; + otf_gstring.glyphs = NULL; + if (OTF_get_table (otf, "head") < 0) + { + OTF_close (otf); + ((FontInfoOTF *) font)->otf = NULL; + goto simple_copy; + } + + tag_name (script, spec->script); + tag_name (langsys, spec->langsys); + + if (spec->gsub_count > 0) + { + gsub_features = alloca (6 * spec->gsub_count); + if (gsub_features) + { + if (OTF_check_table (otf, "GSUB") < 0) + gsub_features = NULL; + else + encode_features (gsub_features, spec->gsub_count, spec->gsub); + } + } + if (spec->gpos_count) + { + gpos_features = alloca (6 * spec->gpos_count); + if (gpos_features) + { + if (OTF_check_table (otf, "GPOS") < 0) + gpos_features = NULL; + else + encode_features (gpos_features, spec->gpos_count, spec->gpos); + } + } + + otf_gstring.size = otf_gstring.used = len; + otf_gstring.glyphs = (OTF_Glyph *) malloc (sizeof (OTF_Glyph) * len); + memset (otf_gstring.glyphs, 0, sizeof (OTF_Glyph) * len); + for (i = 0; i < len; i++) + { + otf_gstring.glyphs[i].c = in->glyphs[from + i].c; + otf_gstring.glyphs[i].glyph_id = in->glyphs[from + i].code; + } + + OTF_drive_gdef (otf, &otf_gstring); + gidx = out->used; + + if (gsub_features) + { + if (OTF_drive_gsub (otf, &otf_gstring, script, langsys, gsub_features) + < 0) + goto simple_copy; + if (out->allocated < out->used + otf_gstring.used) + return -2; + for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; i++, otfg++) + { + MFLTGlyph *g = out->glyphs + out->used; + + *g = in->glyphs[from + otfg->f.index.from]; + g->c = otfg->c; + g->code = otfg->glyph_id; + out->used++; + } + } + else + { + if (out->allocated < out->used + len) + return -2; + for (i = 0; i < len; i++) + out->glyphs[out->used++] = in->glyphs[from + i]; + } + + font->get_metric (font, out, gidx, out->used); + + if (gpos_features) + { + FT_Face face; + MFLTGlyph *base = NULL, *mark = NULL, *g; + int x_ppem, y_ppem, x_scale, y_scale; + + if (OTF_check_features (otf, 1, + spec->script, spec->langsys, + spec->gpos, spec->gpos_count) != 1 + || (OTF_drive_gpos (otf, &otf_gstring, script, langsys, + gpos_features) < 0)) + return to; + + face = ((FontInfo *) font)->face; + x_ppem = face->size->metrics.x_ppem; + y_ppem = face->size->metrics.y_ppem; + x_scale = face->size->metrics.x_scale; + y_scale = face->size->metrics.y_scale; + + for (i = 0, otfg = otf_gstring.glyphs, g = out->glyphs + gidx; + i < otf_gstring.used; i++, otfg++, g++) + { + MFLTGlyph *prev; + + if (! otfg->glyph_id) + continue; + switch (otfg->positioning_type) + { + case 0: + break; + case 1: case 2: + { + int format = otfg->f.f1.format; + + if (format & OTF_XPlacement) + adjustment[i].xoff + = otfg->f.f1.value->XPlacement * x_scale / 0x10000; + if (format & OTF_XPlaDevice) + adjustment[i].xoff + += DEVICE_DELTA (otfg->f.f1.value->XPlaDevice, x_ppem); + if (format & OTF_YPlacement) + adjustment[i].yoff + = - (otfg->f.f1.value->YPlacement * y_scale / 0x10000); + if (format & OTF_YPlaDevice) + adjustment[i].yoff + -= DEVICE_DELTA (otfg->f.f1.value->YPlaDevice, y_ppem); + if (format & OTF_XAdvance) + adjustment[i].xadv + += otfg->f.f1.value->XAdvance * x_scale / 0x10000; + if (format & OTF_XAdvDevice) + adjustment[i].xadv + += DEVICE_DELTA (otfg->f.f1.value->XAdvDevice, x_ppem); + if (format & OTF_YAdvance) + adjustment[i].yadv + += otfg->f.f1.value->YAdvance * y_scale / 0x10000; + if (format & OTF_YAdvDevice) + adjustment[i].xadv + += DEVICE_DELTA (otfg->f.f1.value->YAdvDevice, y_ppem); + adjustment[i].set = 1; + } + break; + case 3: + /* Not yet supported. */ + break; + case 4: case 5: + if (! base) + break; + prev = base; + goto label_adjust_anchor; + default: /* i.e. case 6 */ + if (! mark) + break; + prev = mark; + + label_adjust_anchor: + { + int base_x, base_y, mark_x, mark_y; + + base_x = otfg->f.f4.base_anchor->XCoordinate * x_scale / 0x10000; + base_y = otfg->f.f4.base_anchor->YCoordinate * y_scale / 0x10000; + mark_x = otfg->f.f4.mark_anchor->XCoordinate * x_scale / 0x10000; + mark_y = otfg->f.f4.mark_anchor->YCoordinate * y_scale / 0x10000;; + + if (otfg->f.f4.base_anchor->AnchorFormat != 1) + adjust_anchor (otfg->f.f4.base_anchor, face, prev->code, + x_ppem, y_ppem, &base_x, &base_y); + if (otfg->f.f4.mark_anchor->AnchorFormat != 1) + adjust_anchor (otfg->f.f4.mark_anchor, face, g->code, + x_ppem, y_ppem, &mark_x, &mark_y); + adjustment[i].xoff = (base_x - mark_x); + adjustment[i].yoff = - (base_y - mark_y); + adjustment[i].back = (g - prev); + adjustment[i].set = 1; + } + } + if (otfg->GlyphClass == OTF_GlyphClass0) + base = mark = g; + else if (otfg->GlyphClass == OTF_GlyphClassMark) + mark = g; + else + base = g; + } + } + free (otf_gstring.glyphs); + return to; + + simple_copy: + font->get_metric (font, in, from, to); + for (i = 0; i < len; i++) + { + MFLTGlyph *g = in->glyphs + (from + i); + + out->glyphs[out->used++] = *g; + } + if (otf_gstring.glyphs) + free (otf_gstring.glyphs); + return to; +} + +MFLTFont * +open_font (char *fontname, char **err) +{ + FT_Face face = new_face (fontname, err); + FontInfoOTF *font_info; + + if (! face) + return NULL; + font_info = malloc (sizeof (FontInfoOTF)); + font_info->font.get_glyph_id = get_glyph_id; + font_info->font.get_metric = get_metric; + font_info->font.drive_otf = drive_otf; + font_info->face = face; + font_info->otf = OTF_open_ft_face (face); + return ((MFLTFont *) font_info); +} + +void +close_font (MFLTFont *font) +{ + FontInfoOTF *font_info = (FontInfoOTF *) font; + + OTF_close (font_info->otf); + FT_Done_Face (font_info->face); + free (font_info); +} + +#else /* FLT_HB */ + +typedef struct { + HB_UShort count; + HB_UShort *indices; +} FeatureInfo; + +typedef struct { + FeatureInfo gsub; + FeatureInfo gpos; +} GsubGposInfo; + +typedef struct { + MFLTFont font; + FT_Face face; + HB_FontRec hb_font; + HB_StreamRec gdef_stream; + HB_GDEF gdef; + HB_GSUB gsub; + HB_GPOS gpos; + MPlist *otf_spec_cache; +} FontInfoHB; + +HB_Bool +convertStringToGlyphIndices (HB_Font font, const HB_UChar16 *string, + hb_uint32 length, HB_Glyph *glyphs, + hb_uint32 *numGlyphs, HB_Bool rightToLeft) +{ + return TRUE; +} + +void +getGlyphAdvances (HB_Font font, const HB_Glyph *glyphs, hb_uint32 numGlyphs, + HB_Fixed *advances, int flags /*HB_ShaperFlag*/) +{ + hb_uint32 i; + + for (i = 0; i < numGlyphs; i++) + advances[i] =0; +} + +HB_Bool +canRender (HB_Font font, const HB_UChar16 *string, hb_uint32 length) +{ + return TRUE; +} + +HB_Error +getPointInOutline (HB_Font font, HB_Glyph glyph, int flags /*HB_ShaperFlag*/, + hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, + hb_uint32 *nPoints) +{ + FT_Face face = font->faceData; + HB_Error error; + + if ((error = (HB_Error) FT_Load_Glyph (face, glyph, flags))) + return error; + if (face->glyph->format != ft_glyph_format_outline) + return (HB_Error) HB_Err_Invalid_GPOS_SubTable; + *nPoints = face->glyph->outline.n_points; + if (! *nPoints) + return HB_Err_Ok; + if (point > *nPoints) + return (HB_Error) HB_Err_Invalid_GPOS_SubTable; + + *xpos = face->glyph->outline.points[point].x; + *ypos = face->glyph->outline.points[point].y; + + return HB_Err_Ok; +} + +void +getGlyphMetrics (HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics) +{ +} + +HB_Fixed +getFontMetric (HB_Font font, HB_FontMetric metric) +{ + return 0; +} + +#define MAKE_TAG(name) ((((FT_ULong) (name)[0]) << 24) \ + | (((FT_ULong) (name)[1]) << 16) \ + | (((FT_ULong) (name)[2]) << 8) \ + | (name)[3]) + +HB_Error +new_stream (FT_Face face, char *tagname, HB_Stream stream) +{ + FT_ULong tag = MAKE_TAG (tagname); + FT_ULong len; + FT_Byte *buf; + + if (! FT_IS_SFNT (face)) + return HB_Err_Invalid_Argument; + len = 0; + if (FT_Load_Sfnt_Table (face, tag, 0, NULL, &len)) + return HB_Err_Table_Missing; + buf = malloc (len); + if (! buf) + return HB_Err_Out_Of_Memory; + if (FT_Load_Sfnt_Table (face, tag, 0, buf, &len)) + { + free (buf); + return HB_Err_Table_Missing; + } + stream->base = (HB_Byte *) buf; + stream->size = len; + stream->pos = 0; + stream->cursor = NULL; + + return HB_Err_Ok; +} + +HB_Error +load_gdef (FontInfoHB *font_info) +{ + HB_Error err; + + if (! font_info->gdef_stream.base) + { + err = new_stream ((FT_Face) font_info->hb_font.faceData, "GDEF", + &font_info->gdef_stream); + if (err != HB_Err_Ok) + return err; + } + return HB_Load_GDEF_Table (&font_info->gdef_stream, &font_info->gdef); +} + +HB_Error +load_gsub (FontInfoHB *font_info) +{ + HB_Error err; + HB_StreamRec stream; + HB_LookupList *lookup_list; + int i; + + if (! font_info->gdef) + { + if ((err = load_gdef (font_info)) != HB_Err_Ok) + return err; + } + err = new_stream ((FT_Face) font_info->hb_font.faceData, "GSUB", &stream); + if (err != HB_Err_Ok) + return err; + err = HB_Load_GSUB_Table (&stream, &font_info->gsub, + font_info->gdef, &font_info->gdef_stream); + free (stream.base); + lookup_list = &font_info->gsub->LookupList; + for (i = 0; i < lookup_list->LookupCount; i++) + lookup_list->Properties[i] = HB_GLYPH_PROPERTIES_UNKNOWN; + return err; +} + +HB_Error +load_gpos (FontInfoHB *font_info) +{ + HB_Error err; + HB_StreamRec stream; + HB_LookupList *lookup_list; + int i; + + if (! font_info->gdef) + { + if ((err = load_gdef (font_info)) != HB_Err_Ok) + return err; + } + err = new_stream ((FT_Face) font_info->hb_font.faceData, "GPOS", &stream); + if (err != HB_Err_Ok) + return err; + err = HB_Load_GPOS_Table (&stream, &font_info->gpos, + font_info->gdef, &font_info->gdef_stream); + free (stream.base); + lookup_list = &font_info->gpos->LookupList; + for (i = 0; i < lookup_list->LookupCount; i++) + lookup_list->Properties[i] = HB_GLYPH_PROPERTIES_UNKNOWN; + return err; +} + +const HB_FontClass hb_fontClass = { + convertStringToGlyphIndices, getGlyphAdvances, canRender, + getPointInOutline, getGlyphMetrics, getFontMetric +}; + +HB_Error +setup_features (int gsubp, FontInfoHB *font_info, MFLT_OTF_Spec *spec, + FeatureInfo *info) +{ + int count, preordered; + unsigned int *features; + FT_UShort script, langsys; + FT_UShort req_feature, index; + FT_UInt *feature_list; + int i, j, k; + HB_Error err; + + if (gsubp) + { + if (! font_info->gsub) + { + if ((err = load_gsub (font_info)) != HB_Err_Ok) + return err; + } + if ((err = HB_GSUB_Select_Script (font_info->gsub, spec->script, &script)) + != HB_Err_Ok) + return err; + if (spec->langsys) + { + if ((err = HB_GSUB_Select_Language (font_info->gsub, spec->langsys, + script, &langsys, &req_feature)) + != HB_Err_Ok) + return err; + } + else + langsys = req_feature = 0xFFFF; + count = spec->gsub_count; + features = spec->gsub; + } + else + { + if (! font_info->gpos) + { + if ((err = load_gpos (font_info)) != HB_Err_Ok) + return err; + } + if ((err = HB_GPOS_Select_Script (font_info->gpos, spec->script, &script)) + != HB_Err_Ok) + return err; + if (spec->langsys) + { + if ((err = HB_GPOS_Select_Language (font_info->gpos, spec->langsys, + script, &langsys, &req_feature)) + != HB_Err_Ok) + return err; + } + else + langsys = req_feature = 0xFFFF; + count = spec->gpos_count; + features = spec->gpos; + } + feature_list = NULL; + for (preordered = 0; preordered < count; preordered++) + if (features[preordered] == 0) + { + if ((err = (gsubp + ? HB_GSUB_Query_Features (font_info->gsub, script, langsys, + &feature_list) + : HB_GPOS_Query_Features (font_info->gpos, script, langsys, + &feature_list))) + != HB_Err_Ok) + return err; + break; + } + if (feature_list) + for (i = 0; feature_list[i]; i++); + else + i = preordered; + info->indices = malloc (sizeof (FT_UShort) * ((req_feature != 0xFFFF) + i)); + i = 0; + if (req_feature != 0xFFFF) + info->indices[i++] = req_feature; + for (j = 0; j < preordered; j++) + { + if ((err = (gsubp + ? HB_GSUB_Select_Feature (font_info->gsub, features[j], + script, langsys, &index) + : HB_GPOS_Select_Feature (font_info->gpos, features[j], + script, langsys, &index))) + != HB_Err_Ok) + return err; + info->indices[i++] = index; + } + if (feature_list) + for (j = 0; feature_list[j]; j++) + { + for (k = preordered + 1; k < count; k++) + if (feature_list[j] == features[k]) + break; + if (k == count) + { + if ((gsubp + ? HB_GSUB_Select_Feature (font_info->gsub, feature_list[j], + script, langsys, &index) + : HB_GPOS_Select_Feature (font_info->gpos, feature_list[j], + script, langsys, &index)) + != FT_Err_Ok) + return -1; + info->indices[i++] = index; + } + } + info->count = i; + return HB_Err_Ok; +} + +GsubGposInfo * +setup_otf_spec (MFLTFont *font, MFLT_OTF_Spec *spec) +{ + FontInfoHB *font_info = (FontInfoHB *) font; + GsubGposInfo *ginfo; + + if (spec->gsub_count + spec->gpos_count == 0) + return NULL; + ginfo = mplist_get (font_info->otf_spec_cache, spec->sym); + if (ginfo) + return (ginfo->gsub.count + ginfo->gpos.count > 0 ? ginfo : NULL); + ginfo = calloc (1, sizeof (GsubGposInfo)); + mplist_push (font_info->otf_spec_cache, spec->sym, ginfo); + if (setup_features (1, font_info, spec, &(ginfo->gsub)) != HB_Err_Ok) + return NULL; + if (setup_features (0, font_info, spec, &(ginfo->gpos)) != HB_Err_Ok) + return NULL; + return ginfo; +} + +int +drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec, + MFLTGlyphString *in, int from, int to, + MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment) +{ + FontInfoHB *font_info = (FontInfoHB *) font; + int len = to - from; + int i, gidx; + HB_Buffer buf; + GsubGposInfo *ginfo; + HB_UShort *apply_order_save; + HB_UShort apply_count_save; + int apply_saved = 0; + int gpos_applied = 0; + HB_Error err; + + if (len == 0) + return from; + + buf = NULL; + if (hb_buffer_new (&buf) != FT_Err_Ok) + goto simple_copy; + for (i = from; i < to; i++) + { + if (hb_buffer_add_glyph (buf, in->glyphs[i].code, 0, i) != FT_Err_Ok) + goto simple_copy; + } + ginfo = setup_otf_spec (font, spec); + if (! ginfo) + goto simple_copy; + if (ginfo->gsub.count > 0) + { + if (! font_info->gdef + && load_gdef (font_info) != HB_Err_Ok) + goto simple_copy; + apply_order_save = font_info->gsub->FeatureList.ApplyOrder; + apply_count_save = font_info->gsub->FeatureList.ApplyCount; + apply_saved = 1; + font_info->gsub->FeatureList.ApplyOrder = ginfo->gsub.indices; + font_info->gsub->FeatureList.ApplyCount = ginfo->gsub.count; + err = HB_GSUB_Apply_String (font_info->gsub, buf); + if (err != HB_Err_Ok && err != HB_Err_Not_Covered) + goto simple_copy; + if (out->used + buf->in_length > out->allocated) + return -2; + } + + if (ginfo->gpos.count > 0 + && (font_info->gpos || load_gpos (font_info) == HB_Err_Ok)) + { + if (! apply_saved) + { + apply_order_save = font_info->gsub->FeatureList.ApplyOrder; + apply_count_save = font_info->gsub->FeatureList.ApplyCount; + apply_saved = 1; + } + font_info->gpos->FeatureList.ApplyOrder = ginfo->gpos.indices; + font_info->gpos->FeatureList.ApplyCount = ginfo->gpos.count; + err = HB_GPOS_Apply_String (&font_info->hb_font, font_info->gpos, + FT_LOAD_DEFAULT, buf, FALSE, FALSE); + if (err == HB_Err_Ok) + gpos_applied = 1; + } + + if (apply_saved) + { + font_info->gsub->FeatureList.ApplyOrder = apply_order_save; + font_info->gsub->FeatureList.ApplyCount = apply_count_save; + } + + gidx = out->used; + + for (i = 0; i < buf->in_length; i++) + { + HB_GlyphItem hg = buf->in_string + i; + HB_Position pos = buf->positions + i; + MFLTGlyph *g; + + out->glyphs[out->used] = in->glyphs[hg->cluster]; + g = out->glyphs + out->used++; + if (g->code != hg->gindex) + g->c = 0, g->code = hg->gindex; + adjustment[i].set = gpos_applied; + if (gpos_applied) + { + adjustment[i].xadv = pos->x_advance; + adjustment[i].yadv = pos->y_advance; + adjustment[i].xoff = pos->x_pos; + adjustment[i].yoff = - pos->y_pos; + adjustment[i].back = pos->back; + adjustment[i].advance_is_absolute = pos->new_advance; + } + } + return to; + + simple_copy: + if (buf) + hb_buffer_free (buf); + for (i = 0; i < len; i++) + out->glyphs[out->used++] = in->glyphs[from + i]; + return to; +} + +MFLTFont * +open_font (char *fontname, char **err) +{ + FT_Face face = new_face (fontname, err); + FontInfoHB *font_info; + + if (! face) + return NULL; + font_info = calloc (1, sizeof (FontInfoHB)); + font_info->font.get_glyph_id = get_glyph_id; + font_info->font.get_metric = get_metric; + font_info->font.drive_otf = drive_otf; + font_info->face = face; + font_info->hb_font.klass = &hb_fontClass; + font_info->hb_font.faceData = face; + font_info->hb_font.userData = NULL; + font_info->hb_font.x_ppem = face->size->metrics.x_ppem; + font_info->hb_font.x_scale = face->size->metrics.x_scale; + font_info->hb_font.y_scale = face->size->metrics.y_scale; + font_info->hb_font.y_ppem = face->size->metrics.y_ppem; + font_info->otf_spec_cache = mplist (); + return ((MFLTFont *) font_info); +} + +void +close_font (MFLTFont *font) +{ + FontInfoHB *font_info = (FontInfoHB *) font; + MPlist *p; + + if (font_info->gdef) + { + HB_Done_GDEF_Table (font_info->gdef); + free (font_info->gdef_stream.base); + } + if (font_info->gsub) + HB_Done_GSUB_Table (font_info->gsub); + if (font_info->gpos) + HB_Done_GPOS_Table (font_info->gpos); + FT_Done_Face ((FT_Face) (font_info->hb_font.faceData)); + for (p = font_info->otf_spec_cache; mplist_key (p) != Mnil; + p = mplist_next (p)) + { + GsubGposInfo *ginfo = mplist_value (p); + + if (ginfo->gsub.count > 0) + free (ginfo->gsub.indices); + if (ginfo->gpos.count > 0) + free (ginfo->gpos.indices); + } + m17n_object_unref (font_info->otf_spec_cache); + free (font_info); +} + +#endif +#endif + +int +main (int argc, char **argv) +{ + char *font_name, *flt_name; + char *err; + MText *mt; + MFLTFont *font; + int len, i, j; + + if (argc < 3) + { + fprintf (stderr, "Usage: flt FONT-NAME FLT-NAME < TEXT-FILE\n"); + exit (1); + } + + font_name = argv[1]; + flt_name = argv[2]; + + FT_Init_FreeType (&ft_library); + M17N_INIT (); + font = open_font (font_name, &err); + if (! font) + { + fprintf (stderr, "Font error: %s: %s\n", argv[1], err); + exit (1); + } + + mt = mconv_decode_stream (msymbol ("utf-8"), stdin); + len = mtext_len (mt); + for (i = 0, j = mtext_character (mt, i, len, '\n'); i < len; + i = j + 1, j = mtext_character (mt, i, len, '\n')) + { + if (j < 0) + j = len; + if (flt (mt, i, j, font, flt_name) < 0) + { + fprintf (stderr, "Error in FLT processing.\n"); + exit (1); + } + } + m17n_object_unref (mt); + close_font (font); + M17N_FINI (); + return 0; +}