/* ftfont.c -- FreeType font driver.
- Copyright (C) 2006 Free Software Foundation, Inc.
- Copyright (C) 2006
+ Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
+ Copyright (C) 2006, 2007, 2008
National Institute of Advanced Industrial Science and Technology (AIST)
Registration Number H13PRO009
GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Emacs is distributed in the hope that it will be useful,
#include <config.h>
#include <stdio.h>
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#include FT_SIZES_H
#include <fontconfig/fontconfig.h>
#include <fontconfig/fcfreetype.h>
#include "coding.h"
#include "fontset.h"
#include "font.h"
+#include "ftfont.h"
/* Symbolic type of this font-driver. */
Lisp_Object Qfreetype;
{
struct font font;
FT_Size ft_size;
+#ifdef HAVE_LIBOTF
+ int maybe_otf; /* Flag to tell if this may be OTF or not. */
+ OTF *otf;
+#endif /* HAVE_LIBOTF */
};
static int ftfont_build_basic_charsets P_ ((void));
Lisp_Object, Lisp_Object));
static Lisp_Object ftfont_list_generic_family P_ ((Lisp_Object, Lisp_Object,
Lisp_Object));
+Lisp_Object ftfont_font_format P_ ((FcPattern *));
#define SYMBOL_FcChar8(SYM) (FcChar8 *) SDATA (SYMBOL_NAME (SYM))
Lisp_Object frame, registry;
{
Lisp_Object entity;
- FcChar8 *file;
+ FcChar8 *file, *fontformat;
FcCharSet *charset;
char *str;
int numeric;
return Qnil;
if (FcPatternGetCharSet (p, FC_CHARSET, 0, &charset) != FcResultMatch)
charset = NULL;
+#ifdef FC_FONTFORMAT
+ if (FcPatternGetString (p, FC_FONTFORMAT, 0, &fontformat) != FcResultMatch)
+#endif /* FC_FONTFORMAT */
+ fontformat = NULL;
entity = Fmake_vector (make_number (FONT_ENTITY_MAX), null_string);
return Qnil;
if (FcPatternAddString (p, FC_FILE, file) == FcFalse
- || (charset && FcPatternAddCharSet (p, FC_CHARSET, charset) == FcFalse)
+ || (charset
+ && FcPatternAddCharSet (p, FC_CHARSET, charset) == FcFalse)
+#ifdef FC_FONTFORMAT
+ || (fontformat
+ && FcPatternAddString (p, FC_FONTFORMAT, fontformat) == FcFalse)
+#endif /* FC_FONTFORMAT */
|| (numeric >= 0
&& FcPatternAddInteger (p, FC_SPACING, numeric) == FcFalse))
{
objset = FcObjectSetBuild (FC_FOUNDRY, FC_FAMILY, FC_WEIGHT, FC_SLANT,
FC_WIDTH, FC_PIXEL_SIZE, FC_SPACING,
- FC_CHARSET, FC_FILE, NULL);
+ FC_CHARSET, FC_FILE,
+#ifdef FC_FONTFORMAT
+ FC_FONTFORMAT,
+#endif /* FC_FONTFORMAT */
+ NULL);
if (! objset)
goto err;
pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString,
}
-static Lisp_Object ftfont_get_cache P_ ((Lisp_Object));
+static Lisp_Object ftfont_get_cache P_ ((FRAME_PTR));
static Lisp_Object ftfont_list P_ ((Lisp_Object, Lisp_Object));
static Lisp_Object ftfont_match P_ ((Lisp_Object, Lisp_Object));
static Lisp_Object ftfont_list_family P_ ((Lisp_Object));
struct font_bitmap *, int));
static int ftfont_anchor_point P_ ((struct font *, unsigned, int,
int *, int *));
+static Lisp_Object ftfont_shape P_ ((Lisp_Object));
struct font_driver ftfont_driver =
{
- (Lisp_Object) NULL, /* Qfreetype */
+ 0, /* Qfreetype */
ftfont_get_cache,
ftfont_list,
ftfont_match,
NULL,
NULL,
ftfont_anchor_point,
-#ifdef HAVE_LIBOTF
- font_otf_capability,
- font_otf_gsub,
- font_otf_gpos
-#else
NULL,
NULL,
+ NULL,
+ NULL,
+#if defined (HAVE_M17N_FLT) && defined (HAVE_LIBOTF)
+ ftfont_shape
+#else /* not (HAVE_M17N_FLT && HAVE_LIBOTF) */
NULL
-#endif /* HAVE_LIBOTF */
+#endif /* not (HAVE_M17N_FLT && HAVE_LIBOTF) */
};
extern Lisp_Object QCname;
static Lisp_Object
-ftfont_get_cache (frame)
- Lisp_Object frame;
+ftfont_get_cache (f)
+ FRAME_PTR f;
{
return freetype_font_cache;
}
+struct OpenTypeSpec
+{
+ Lisp_Object script;
+ unsigned int script_tag, langsys_tag;
+ int nfeatures[2];
+ unsigned int *features[2];
+};
+
+#define OTF_SYM_TAG(SYM, TAG) \
+ do { \
+ unsigned char *p = SDATA (SYMBOL_NAME (SYM)); \
+ TAG = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; \
+ } while (0)
+
+#define OTF_TAG_STR(TAG, P) \
+ do { \
+ (P)[0] = (char) (TAG >> 24); \
+ (P)[1] = (char) ((TAG >> 16) & 0xFF); \
+ (P)[2] = (char) ((TAG >> 8) & 0xFF); \
+ (P)[3] = (char) (TAG & 0xFF); \
+ (P)[4] = '\0'; \
+ } while (0)
+
+static struct OpenTypeSpec *
+ftfont_get_open_type_spec (Lisp_Object otf_spec)
+{
+ struct OpenTypeSpec *spec = malloc (sizeof (struct OpenTypeSpec));
+ Lisp_Object val;
+ int i, j, negative;
+
+ if (! spec)
+ return NULL;
+ spec->script = XCAR (otf_spec);
+ if (! NILP (val))
+ {
+ OTF_SYM_TAG (spec->script, spec->script_tag);
+ val = assq_no_quit (spec->script, Votf_script_alist);
+ if (CONSP (val) && SYMBOLP (XCDR (val)))
+ spec->script = XCDR (val);
+ else
+ spec->script = Qnil;
+ }
+ else
+ spec->script_tag = 0x44464C54; /* "DFLT" */
+ otf_spec = XCDR (otf_spec);
+ val = XCAR (otf_spec);
+ if (! NILP (val))
+ OTF_SYM_TAG (val, spec->langsys_tag);
+ else
+ spec->langsys_tag = 0;
+ spec->nfeatures[0] = spec->nfeatures[1] = 0;
+ for (i = 0; i < 2; i++)
+ {
+ Lisp_Object len;
+
+ otf_spec = XCDR (otf_spec);
+ if (NILP (otf_spec))
+ break;
+ val = XCAR (otf_spec);
+ if (NILP (val))
+ continue;
+ len = Flength (val);
+ spec->features[i] = malloc (sizeof (int) * XINT (len));
+ if (! spec->features[i])
+ {
+ if (i > 0 && spec->features[0])
+ free (spec->features[0]);
+ free (spec);
+ return NULL;
+ }
+ for (j = 0, negative = 0; CONSP (val); val = XCDR (val))
+ {
+ if (NILP (XCAR (val)))
+ negative = 1;
+ else
+ {
+ unsigned int tag;
+
+ OTF_SYM_TAG (XCAR (val), tag);
+ spec->features[i][j++] = negative ? tag & 0x80000000 : tag;
+ }
+ }
+ spec->nfeatures[i] = j;
+ }
+ return spec;
+}
+
static Lisp_Object
ftfont_list (frame, spec)
Lisp_Object frame, spec;
FcObjectSet *objset = NULL;
Lisp_Object script;
Lisp_Object registry = Qunicode_bmp;
+ struct OpenTypeSpec *otspec= NULL;
int weight = 0;
double dpi = -1;
int spacing = -1;
int scalable = -1;
- char otf_script[15]; /* For "otlayout\:XXXX" */
+ char otlayout[15]; /* For "otlayout:XXXX" */
val = null_vector;
return Qnil;
charset = cs_iso8859_1;
}
- else if (! EQ (registry, Qiso10646_1) && ! EQ (registry, Qunicode_bmp))
+ else if (! EQ (registry, Qiso10646_1)
+ && ! EQ (registry, Qunicode_bmp)
+ && ! EQ (registry, Qunicode_sip))
return val;
}
- otf_script[0] = '\0';
+ otlayout[0] = '\0';
script = Qnil;
for (extra = AREF (spec, FONT_EXTRA_INDEX);
CONSP (extra); extra = XCDR (extra))
key = XCAR (tmp), val = XCDR (tmp);
if (EQ (key, QCotf))
{
- script = assq_no_quit (val, Votf_script_alist);
- if (CONSP (script) && SYMBOLP (XCDR (script)))
- script = XCDR (script);
- tmp = SYMBOL_NAME (val);
- sprintf (otf_script, "otlayout:%s", (char *) SDATA (tmp));
+ otspec = ftfont_get_open_type_spec (val);
+ if (! otspec)
+ return null_vector;
+ strcat (otlayout, "otlayout:");
+ OTF_TAG_STR (otspec->script_tag, otlayout + 9);
+ script = otspec->script;
}
else if (EQ (key, QClanguage))
{
objset = FcObjectSetBuild (FC_FOUNDRY, FC_FAMILY, FC_WEIGHT, FC_SLANT,
FC_WIDTH, FC_PIXEL_SIZE, FC_SPACING,
- FC_CHARSET, FC_FILE, NULL);
+ FC_CHARSET, FC_FILE,
+#ifdef FC_FONTFORMAT
+ FC_FONTFORMAT,
+#endif /* FC_FONTFORMAT */
+ NULL);
if (! objset)
goto err;
- if (otf_script[0])
+ if (otlayout[0])
{
-#ifndef FC_CAPABILITY
- goto finish;
-#else /* not FC_CAPABILITY */
+#ifdef FC_CAPABILITY
if (! FcObjectSetAdd (objset, FC_CAPABILITY))
goto err;
+#else /* not FC_CAPABILITY */
+ goto finish;
#endif /* not FC_CAPABILITY */
}
continue;
}
#ifdef FC_CAPABILITY
- if (otf_script[0])
+ if (otlayout[0])
{
FcChar8 *this;
if (FcPatternGetString (fontset->fonts[i], FC_CAPABILITY, 0,
&this) != FcResultMatch
- || ! strstr ((char *) this, otf_script))
+ || ! strstr ((char *) this, otlayout))
continue;
}
#endif /* FC_CAPABILITY */
+#ifdef HAVE_LIBOTF
+ if (otspec)
+ {
+ FcChar8 *file;
+ OTF *otf;
+
+ if (FcPatternGetString (fontset->fonts[i], FC_FILE, 0, &file)
+ != FcResultMatch)
+ continue;
+ otf = OTF_open ((char *) file);
+ if (! otf)
+ continue;
+ if (OTF_check_features (otf, 1,
+ otspec->script_tag, otspec->langsys_tag,
+ otspec->features[0],
+ otspec->nfeatures[0]) != 1
+ || OTF_check_features (otf, 0,
+ otspec->script_tag, otspec->langsys_tag,
+ otspec->features[1],
+ otspec->nfeatures[1]) != 1)
+ continue;
+ }
+#endif /* HAVE_LIBOTF */
entity = ftfont_pattern_entity (fontset->fonts[i], frame, registry);
if (! NILP (entity))
val = Fcons (entity, val);
if (fontset) FcFontSetDestroy (fontset);
if (langset) FcLangSetDestroy (langset);
if (pattern) FcPatternDestroy (pattern);
-
+ if (otspec)
+ {
+ if (otspec->nfeatures[0] > 0)
+ free (otspec->features[0]);
+ if (otspec->nfeatures[1] > 0)
+ free (otspec->features[1]);
+ free (otspec);
+ }
return val;
}
if (! ftfont_info)
return NULL;
ftfont_info->ft_size = ft_size;
+#ifdef HAVE_LIBOTF
+ ftfont_info->maybe_otf = ft_face->face_flags & FT_FACE_FLAG_SFNT;
+ ftfont_info->otf = NULL;
+#endif /* HAVE_LIBOTF */
font = (struct font *) ftfont_info;
+ font->format = ftfont_font_format (pattern);
font->entity = entity;
font->pixel_size = size;
font->driver = &ftfont_driver;
{
int i;
+ font->font.average_width = font->font.space_width = 0;
for (i = 32; i < 127; i++)
{
if (FT_Load_Char (ft_face, i, FT_LOAD_DEFAULT) != 0)
(XSAVE_VALUE (val)->integer)--;
if (XSAVE_VALUE (val)->integer == 0)
- FT_Done_Face (ftfont_info->ft_size->face);
+ {
+ FT_Done_Face (ftfont_info->ft_size->face);
+#ifdef HAVE_LIBOTF
+ if (ftfont_info->otf)
+ OTF_close (ftfont_info->otf);
+#endif
+ }
else
FT_Done_Size (ftfont_info->ft_size);
FT_ULong charcode = c;
FT_UInt code = FT_Get_Char_Index (ft_face, charcode);
- return (code > 0 ? code : 0xFFFFFFFF);
+ return (code > 0 ? code : FONT_INVALID_CODE);
}
static int
if (FT_Load_Glyph (ft_face, code, load_flags) != 0)
return -1;
+ bitmap->bits_per_pixel
+ = (ft_face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 1
+ : ft_face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY ? 8
+ : ft_face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD ? 8
+ : ft_face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V ? 8
+ : -1);
+ if (bitmap->bits_per_pixel < 0)
+ /* We don't suport that kind of pixel mode. */
+ return -1;
bitmap->rows = ft_face->glyph->bitmap.rows;
bitmap->width = ft_face->glyph->bitmap.width;
bitmap->pitch = ft_face->glyph->bitmap.pitch;
return 0;
}
+#ifdef HAVE_LIBOTF
+#ifdef HAVE_M17N_FLT
+
+struct MFLTFontFT
+{
+ MFLTFont flt_font;
+ struct font *font;
+ FT_Face ft_face;
+ OTF *otf;
+};
+
+static int
+ftfont_get_glyph_id (font, gstring, from, to)
+ MFLTFont *font;
+ MFLTGlyphString *gstring;
+ int from, to;
+{
+ struct MFLTFontFT *flt_font_ft = (struct MFLTFontFT *) font;
+ FT_Face ft_face = flt_font_ft->ft_face;
+ MFLTGlyph *g;
+
+ for (g = gstring->glyphs + from; from < to; g++, from++)
+ if (! g->encoded)
+ {
+ FT_UInt code = FT_Get_Char_Index (ft_face, g->code);
+
+ g->code = code > 0 ? code : FONT_INVALID_CODE;
+ g->encoded = 1;
+ }
+ return 0;
+}
+
+static int
+ftfont_get_metrics (font, gstring, from, to)
+ MFLTFont *font;
+ MFLTGlyphString *gstring;
+ int from, to;
+{
+ struct MFLTFontFT *flt_font_ft = (struct MFLTFontFT *) font;
+ FT_Face ft_face = flt_font_ft->ft_face;
+ MFLTGlyph *g;
+
+ for (g = gstring->glyphs + from; from < to; g++, from++)
+ if (! g->measured)
+ {
+ if (g->code != FONT_INVALID_CODE)
+ {
+ FT_Glyph_Metrics *m;
+
+ if (FT_Load_Glyph (ft_face, g->code, FT_LOAD_DEFAULT) != 0)
+ abort ();
+ m = &ft_face->glyph->metrics;
+
+ g->lbearing = m->horiBearingX;
+ g->rbearing = m->horiBearingX + m->width;
+ g->ascent = m->horiBearingY;
+ g->descent = m->height - m->horiBearingY;
+ g->xadv = m->horiAdvance;
+ }
+ else
+ {
+ g->lbearing = 0;
+ g->rbearing = g->xadv = flt_font_ft->font->font.space_width << 6;
+ g->ascent = flt_font_ft->font->ascent << 6;
+ g->descent = flt_font_ft->font->descent << 6;
+ }
+ g->yadv = 0;
+ g->measured = 1;
+ }
+ return 0;
+}
+
+static int
+ftfont_check_otf (MFLTFont *font, MFLTOtfSpec *spec)
+{
+ struct MFLTFontFT *flt_font_ft = (struct MFLTFontFT *) font;
+ OTF *otf = flt_font_ft->otf;
+ OTF_Tag *tags;
+ int i, n, negative;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (! spec->features[i])
+ continue;
+ for (n = 0; spec->features[i][n]; n++);
+ tags = alloca (sizeof (OTF_Tag) * n);
+ for (n = 0, negative = 0; spec->features[i][n]; n++)
+ {
+ if (spec->features[i][n] == 0xFFFFFFFF)
+ negative = 1;
+ else if (negative)
+ tags[n - 1] = spec->features[i][n] | 0x80000000;
+ else
+ tags[n] = spec->features[i][n];
+ }
+ if (n - negative > 0
+ && OTF_check_features (otf, i == 0, spec->script, spec->langsys,
+ tags, n - negative) != 1)
+ return 0;
+ }
+ return 1;
+}
+
+#define DEVICE_DELTA(table, size) \
+ (((size) >= (table).StartSize && (size) <= (table).EndSize) \
+ ? (table).DeltaValue[(size) - (table).StartSize] << 6 \
+ : 0)
+
+static void
+adjust_anchor (FT_Face ft_face, OTF_Anchor *anchor,
+ unsigned code, int x_ppem, int y_ppem, int *x, int *y)
+{
+ if (anchor->AnchorFormat == 2)
+ {
+ FT_Outline *outline;
+ int ap = anchor->f.f1.AnchorPoint;
+
+ FT_Load_Glyph (ft_face, (FT_UInt) code, FT_LOAD_MONOCHROME);
+ outline = &ft_face->glyph->outline;
+ if (ap < outline->n_points)
+ {
+ *x = outline->points[ap].x << 6;
+ *y = outline->points[ap].y << 6;
+ }
+ }
+ 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);
+ }
+}
+
+static OTF_GlyphString otf_gstring;
+
+static int
+ftfont_drive_otf (font, spec, in, from, to, out, adjustment)
+ MFLTFont *font;
+ MFLTOtfSpec *spec;
+ MFLTGlyphString *in;
+ int from, to;
+ MFLTGlyphString *out;
+ MFLTGlyphAdjustment *adjustment;
+{
+ struct MFLTFontFT *flt_font_ft = (struct MFLTFontFT *) font;
+ FT_Face ft_face = flt_font_ft->ft_face;
+ OTF *otf = flt_font_ft->otf;
+ int len = to - from;
+ int i, j, gidx;
+ OTF_Glyph *otfg;
+ char script[5], *langsys = NULL;
+ char *gsub_features = NULL, *gpos_features = NULL;
+
+ if (len == 0)
+ return from;
+ OTF_tag_name (spec->script, script);
+ if (spec->langsys)
+ {
+ langsys = alloca (5);
+ OTF_tag_name (spec->langsys, langsys);
+ }
+ for (i = 0; i < 2; i++)
+ {
+ char *p;
+
+ if (spec->features[i] && spec->features[i][1] != 0xFFFFFFFF)
+ {
+ for (j = 0; spec->features[i][j]; j++);
+ if (i == 0)
+ p = gsub_features = alloca (6 * j);
+ else
+ p = gpos_features = alloca (6 * j);
+ for (j = 0; spec->features[i][j]; j++)
+ {
+ if (spec->features[i][j] == 0xFFFFFFFF)
+ *p++ = '*', *p++ = ',';
+ else
+ {
+ OTF_tag_name (spec->features[i][j], p);
+ p[4] = ',';
+ p += 5;
+ }
+ }
+ *--p = '\0';
+ }
+ }
+
+ if (otf_gstring.size == 0)
+ {
+ otf_gstring.glyphs = (OTF_Glyph *) malloc (sizeof (OTF_Glyph) * len);
+ otf_gstring.size = len;
+ }
+ else if (otf_gstring.size < len)
+ {
+ otf_gstring.glyphs = (OTF_Glyph *) realloc (otf_gstring.glyphs,
+ sizeof (OTF_Glyph) * len);
+ otf_gstring.size = len;
+ }
+ otf_gstring.used = 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; )
+ {
+ OTF_Glyph *endg;
+ MFLTGlyph *g;
+ int min_from, max_to;
+ int j;
+
+ g = out->glyphs + out->used;
+ *g = in->glyphs[from + otfg->f.index.from];
+ if (g->code != otfg->glyph_id)
+ {
+ g->c = 0;
+ g->code = otfg->glyph_id;
+ g->measured = 0;
+ }
+ out->used++;
+ min_from = g->from;
+ max_to = g->to;
+ if (otfg->f.index.from < otfg->f.index.to)
+ {
+ /* OTFG substitutes multiple glyphs in IN. */
+ for (j = from + otfg->f.index.from + 1;
+ j <= from + otfg->f.index.to; j++)
+ {
+ if (min_from > in->glyphs[j].from)
+ min_from = in->glyphs[j].from;
+ if (max_to < in->glyphs[j].to)
+ max_to = in->glyphs[j].to;
+ }
+ g->from = min_from;
+ g->to = max_to;
+ }
+ for (i++, otfg++; (i < otf_gstring.used
+ && otfg->f.index.from == otfg[-1].f.index.from);
+ i++, otfg++)
+ {
+ g = out->glyphs + out->used;
+ *g = in->glyphs[from + otfg->f.index.to];
+ if (g->code != otfg->glyph_id)
+ {
+ g->c = 0;
+ g->code = otfg->glyph_id;
+ g->measured = 0;
+ }
+ 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];
+ }
+
+ if (gpos_features)
+ {
+ MFLTGlyph *base = NULL, *mark = NULL, *g;
+ int x_ppem, y_ppem, x_scale, y_scale;
+
+ if (OTF_drive_gpos (otf, &otf_gstring, script, langsys, gpos_features)
+ < 0)
+ return to;
+
+ x_ppem = ft_face->size->metrics.x_ppem;
+ y_ppem = ft_face->size->metrics.y_ppem;
+ x_scale = ft_face->size->metrics.x_scale;
+ y_scale = ft_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: /* Single */
+ case 2: /* Pair */
+ {
+ 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].yadv
+ += DEVICE_DELTA (otfg->f.f1.value->YAdvDevice, y_ppem);
+ adjustment[i].set = 1;
+ }
+ break;
+ case 3: /* Cursive */
+ /* Not yet supported. */
+ break;
+ case 4: /* Mark-to-Base */
+ case 5: /* Mark-to-Ligature */
+ if (! base)
+ break;
+ prev = base;
+ goto label_adjust_anchor;
+ default: /* i.e. case 6 Mark-to-Mark */
+ if (! mark)
+ break;
+ prev = mark;
+
+ label_adjust_anchor:
+ {
+ int base_x, base_y, mark_x, mark_y;
+ int this_from, this_to;
+
+ 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 (ft_face, otfg->f.f4.base_anchor,
+ prev->code, x_ppem, y_ppem, &base_x, &base_y);
+ if (otfg->f.f4.mark_anchor->AnchorFormat != 1)
+ adjust_anchor (ft_face, otfg->f.f4.mark_anchor, 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].xadv = 0;
+ adjustment[i].advance_is_absolute = 1;
+ adjustment[i].set = 1;
+ this_from = g->from;
+ this_to = g->to;
+ for (j = 0; prev + j < g; j++)
+ {
+ if (this_from > prev[j].from)
+ this_from = prev[j].from;
+ if (this_to < prev[j].to)
+ this_to = prev[j].to;
+ }
+ for (; prev <= g; prev++)
+ {
+ prev->from = this_from;
+ prev->to = this_to;
+ }
+ }
+ }
+ if (otfg->GlyphClass == OTF_GlyphClass0)
+ base = mark = g;
+ else if (otfg->GlyphClass == OTF_GlyphClassMark)
+ mark = g;
+ else
+ base = g;
+ }
+ }
+ return to;
+
+ simple_copy:
+ if (out->allocated < out->used + len)
+ return -2;
+ font->get_metrics (font, in, from, to);
+ memcpy (out->glyphs + out->used, in->glyphs + from,
+ sizeof (MFLTGlyph) * len);
+ out->used += len;
+ return to;
+}
+
+static MFLTGlyphString gstring;
+
+static int m17n_flt_initialized;
+
+extern Lisp_Object QCfamily;
+
+Lisp_Object
+ftfont_shape_by_flt (lgstring, font, ft_face, otf)
+ Lisp_Object lgstring;
+ struct font *font;
+ FT_Face ft_face;
+ OTF *otf;
+{
+ EMACS_UINT len = LGSTRING_LENGTH (lgstring);
+ EMACS_UINT i;
+ struct MFLTFontFT flt_font_ft;
+
+ if (! m17n_flt_initialized)
+ {
+ M17N_INIT ();
+ m17n_flt_initialized = 1;
+ }
+
+ for (i = 0; i < len; i++)
+ if (NILP (LGSTRING_GLYPH (lgstring, i)))
+ break;
+ len = i;
+
+ if (gstring.allocated == 0)
+ {
+ gstring.allocated = len * 2;
+ gstring.glyph_size = sizeof (MFLTGlyph);
+ gstring.glyphs = malloc (sizeof (MFLTGlyph) * gstring.allocated);
+ }
+ else if (gstring.allocated < len * 2)
+ {
+ gstring.allocated = len * 2;
+ gstring.glyphs = realloc (gstring.glyphs,
+ sizeof (MFLTGlyph) * gstring.allocated);
+ }
+ for (i = 0; i < len; i++)
+ gstring.glyphs[i].c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
+ gstring.used = len;
+ gstring.r2l = 0;
+
+ {
+ Lisp_Object family = Ffont_get (LGSTRING_FONT (lgstring), QCfamily);
+
+ if (NILP (family))
+ flt_font_ft.flt_font.family = Mnil;
+ else
+ flt_font_ft.flt_font.family = msymbol (SDATA (SYMBOL_NAME (family)));
+ }
+ flt_font_ft.flt_font.x_ppem = ft_face->size->metrics.x_ppem;
+ flt_font_ft.flt_font.y_ppem = ft_face->size->metrics.y_ppem;
+ flt_font_ft.flt_font.get_glyph_id = ftfont_get_glyph_id;
+ flt_font_ft.flt_font.get_metrics = ftfont_get_metrics;
+ flt_font_ft.flt_font.check_otf = ftfont_check_otf;
+ flt_font_ft.flt_font.drive_otf = ftfont_drive_otf;
+ flt_font_ft.flt_font.internal = NULL;
+ flt_font_ft.font = font;
+ flt_font_ft.ft_face = ft_face;
+ flt_font_ft.otf = otf;
+ for (i = 0; i < 3; i++)
+ {
+ int result = mflt_run (&gstring, 0, len, &flt_font_ft.flt_font, NULL);
+ if (result != -2)
+ break;
+ gstring.allocated += gstring.allocated;
+ gstring.glyphs = realloc (gstring.glyphs,
+ sizeof (MFLTGlyph) * gstring.allocated);
+ }
+ if (gstring.used > LGSTRING_LENGTH (lgstring))
+ return Qnil;
+ for (i = 0; i < gstring.used; i++)
+ {
+ MFLTGlyph *g = gstring.glyphs + i;
+
+ g->from = LGLYPH_FROM (LGSTRING_GLYPH (lgstring, g->from));
+ g->to = LGLYPH_TO (LGSTRING_GLYPH (lgstring, g->to));
+ }
+
+ for (i = 0; i < gstring.used; i++)
+ {
+ Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
+ MFLTGlyph *g = gstring.glyphs + i;
+
+ if (NILP (lglyph))
+ {
+ lglyph = Fmake_vector (make_number (LGLYPH_SIZE), Qnil);
+ LGSTRING_SET_GLYPH (lgstring, i, lglyph);
+ }
+ LGLYPH_SET_FROM (lglyph, g->from);
+ LGLYPH_SET_TO (lglyph, g->to);
+ LGLYPH_SET_CHAR (lglyph, g->c);
+ LGLYPH_SET_CODE (lglyph, g->code);
+ LGLYPH_SET_WIDTH (lglyph, g->xadv >> 6);
+ LGLYPH_SET_LBEARING (lglyph, g->lbearing >> 6);
+ LGLYPH_SET_RBEARING (lglyph, g->rbearing >> 6);
+ LGLYPH_SET_ASCENT (lglyph, g->ascent >> 6);
+ LGLYPH_SET_DESCENT (lglyph, g->descent >> 6);
+ if (g->adjusted)
+ {
+ Lisp_Object vec;
+
+ vec = Fmake_vector (make_number (3), Qnil);
+ ASET (vec, 0, make_number (g->xoff >> 6));
+ ASET (vec, 1, make_number (g->yoff >> 6));
+ ASET (vec, 2, make_number (g->xadv >> 6));
+ LGLYPH_SET_ADJUSTMENT (lglyph, vec);
+ }
+ }
+ return make_number (i);
+}
+
+Lisp_Object
+ftfont_shape (lgstring)
+ Lisp_Object lgstring;
+{
+ struct font *font;
+ struct ftfont_info *ftfont_info;
+
+ CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring), font);
+ ftfont_info = (struct ftfont_info *) font;
+ if (! ftfont_info->maybe_otf)
+ return 0;
+ if (! ftfont_info->otf)
+ {
+ OTF *otf = OTF_open_ft_face (ftfont_info->ft_size->face);
+
+ if (! otf || OTF_get_table (otf, "head") < 0)
+ {
+ if (otf)
+ OTF_close (otf);
+ ftfont_info->maybe_otf = 0;
+ return 0;
+ }
+
+ ftfont_info->otf = otf;
+ }
+
+ return ftfont_shape_by_flt (lgstring, font, ftfont_info->ft_size->face,
+ ftfont_info->otf);
+}
+
+#endif /* HAVE_M17N_FLT */
+#endif /* HAVE_LIBOTF */
+
+Lisp_Object
+ftfont_font_format (FcPattern *pattern)
+{
+ FcChar8 *str;
+
+#ifdef FC_FONTFORMAT
+ if (FcPatternGetString (pattern, FC_FONTFORMAT, 0, &str) != FcResultMatch)
+ return Qnil;
+ if (strcmp ((char *) str, "TrueType") == 0)
+ return intern ("truetype");
+ if (strcmp ((char *) str, "Type 1") == 0)
+ return intern ("type1");
+ if (strcmp ((char *) str, "PCF") == 0)
+ return intern ("pcf");
+ if (strcmp ((char *) str, "BDF") == 0)
+ return intern ("bdf");
+#else /* not FC_FONTFORMAT */
+ if (FcPatternGetString (pattern, FC_FILE, 0, &str) != FcResultMatch)
+ return Qnil;
+ if (strcasestr ((char *) str, ".ttf") == 0)
+ return intern ("truetype");
+ if (strcasestr ((char *) str, "pfb") == 0)
+ return intern ("type1");
+ if (strcasestr ((char *) str, "pcf") == 0)
+ return intern ("pcf");
+ if (strcasestr ((char *) str, "bdf") == 0)
+ return intern ("bdf");
+#endif /* not FC_FONTFORMAT */
+ return intern ("unknown");
+}
+
\f
void
syms_of_ftfont ()