blob: 2b0415bb7ca7ce0dac8bb13e27726e76c243c7c7 [file] [log] [blame]
//========================================================================
//
// SplashFTFont.cc
//
//========================================================================
#include <config.h>
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif
#include "goo/gmem.h"
#include "SplashMath.h"
#include "SplashGlyphBitmap.h"
#include "SplashPath.h"
#include "SplashFTFontEngine.h"
#include "SplashFTFontFile.h"
#include "SplashFTFont.h"
#include FT_OUTLINE_H
#include FT_INTERNAL_OBJECTS_H // needed for FT_New_Size decl
//------------------------------------------------------------------------
static int glyphPathMoveTo(FT_Vector *pt, void *path);
static int glyphPathLineTo(FT_Vector *pt, void *path);
static int glyphPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *path);
static int glyphPathCubicTo(FT_Vector *ctrl1, FT_Vector *ctrl2,
FT_Vector *pt, void *path);
//------------------------------------------------------------------------
// SplashFTFont
//------------------------------------------------------------------------
SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA):
SplashFont(fontFileA, matA, fontFileA->engine->aa)
{
FT_Face face;
SplashCoord size, div;
int x, y;
face = fontFileA->face;
if (FT_New_Size(face, &sizeObj)) {
return;
}
face->size = sizeObj;
size = splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]);
if (FT_Set_Pixel_Sizes(face, 0, (int)size)) {
return;
}
div = face->bbox.xMax > 20000 ? 65536 : 1;
// transform the four corners of the font bounding box -- the min
// and max values form the bounding box of the transformed font
x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) /
(div * face->units_per_EM));
xMin = xMax = x;
y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) /
(div * face->units_per_EM));
yMin = yMax = y;
x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) /
(div * face->units_per_EM));
if (x < xMin) {
xMin = x;
} else if (x > xMax) {
xMax = x;
}
y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) /
(div * face->units_per_EM));
if (y < yMin) {
yMin = y;
} else if (y > yMax) {
yMax = y;
}
x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) /
(div * face->units_per_EM));
if (x < xMin) {
xMin = x;
} else if (x > xMax) {
xMax = x;
}
y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) /
(div * face->units_per_EM));
if (y < yMin) {
yMin = y;
} else if (y > yMax) {
yMax = y;
}
x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) /
(div * face->units_per_EM));
if (x < xMin) {
xMin = x;
} else if (x > xMax) {
xMax = x;
}
y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) /
(div * face->units_per_EM));
if (y < yMin) {
yMin = y;
} else if (y > yMax) {
yMax = y;
}
// This is a kludge: some buggy PDF generators embed fonts with
// zero bounding boxes.
if (xMax == xMin) {
xMin = 0;
xMax = (int)size;
}
if (yMax == yMin) {
yMin = 0;
yMax = (int)(1.2 * size);
}
// compute the transform matrix
matrix.xx = (FT_Fixed)((mat[0] / size) * 65536);
matrix.yx = (FT_Fixed)((mat[1] / size) * 65536);
matrix.xy = (FT_Fixed)((mat[2] / size) * 65536);
matrix.yy = (FT_Fixed)((mat[3] / size) * 65536);
}
SplashFTFont::~SplashFTFont() {
}
GBool SplashFTFont::getGlyph(int c, int xFrac, int yFrac,
SplashGlyphBitmap *bitmap) {
return SplashFont::getGlyph(c, xFrac, 0, bitmap);
}
GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac,
SplashGlyphBitmap *bitmap) {
SplashFTFontFile *ff;
FT_Vector offset;
FT_GlyphSlot slot;
FT_UInt gid;
int rowSize;
Guchar *p, *q;
int i;
ff = (SplashFTFontFile *)fontFile;
ff->face->size = sizeObj;
offset.x = (FT_Pos)(xFrac * splashFontFractionMul * 64);
offset.y = 0;
FT_Set_Transform(ff->face, &matrix, &offset);
slot = ff->face->glyph;
if (ff->codeToGID && c < ff->codeToGIDLen) {
gid = (FT_UInt)ff->codeToGID[c];
} else {
gid = (FT_UInt)c;
}
// if we have the FT2 bytecode interpreter, autohinting won't be used
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
if (FT_Load_Glyph(ff->face, gid,
aa ? FT_LOAD_NO_BITMAP : FT_LOAD_DEFAULT)) {
return gFalse;
}
#else
// FT2's autohinting doesn't always work very well (especially with
// font subsets), so turn it off if anti-aliasing is enabled; if
// anti-aliasing is disabled, this seems to be a tossup - some fonts
// look better with hinting, some without, so leave hinting on
if (FT_Load_Glyph(ff->face, gid,
aa ? FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP
: FT_LOAD_DEFAULT)) {
return gFalse;
}
#endif
if (FT_Render_Glyph(slot, aa ? ft_render_mode_normal
: ft_render_mode_mono)) {
return gFalse;
}
bitmap->x = -slot->bitmap_left;
bitmap->y = slot->bitmap_top;
bitmap->w = slot->bitmap.width;
bitmap->h = slot->bitmap.rows;
bitmap->aa = aa;
if (aa) {
rowSize = bitmap->w;
} else {
rowSize = (bitmap->w + 7) >> 3;
}
bitmap->data = (Guchar *)gmalloc(rowSize * bitmap->h);
bitmap->freeData = gTrue;
for (i = 0, p = bitmap->data, q = slot->bitmap.buffer;
i < bitmap->h;
++i, p += rowSize, q += slot->bitmap.pitch) {
memcpy(p, q, rowSize);
}
return gTrue;
}
SplashPath *SplashFTFont::getGlyphPath(int c) {
static FT_Outline_Funcs outlineFuncs = {
&glyphPathMoveTo,
&glyphPathLineTo,
&glyphPathConicTo,
&glyphPathCubicTo,
0, 0
};
SplashFTFontFile *ff;
SplashPath *path;
FT_GlyphSlot slot;
FT_UInt gid;
FT_Glyph glyph;
ff = (SplashFTFontFile *)fontFile;
ff->face->size = sizeObj;
FT_Set_Transform(ff->face, &matrix, NULL);
slot = ff->face->glyph;
if (ff->codeToGID && c < ff->codeToGIDLen) {
gid = ff->codeToGID[c];
} else {
gid = (FT_UInt)c;
}
if (FT_Load_Glyph(ff->face, gid, FT_LOAD_NO_BITMAP)) {
return NULL;
}
if (FT_Get_Glyph(slot, &glyph)) {
return NULL;
}
path = new SplashPath();
FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline,
&outlineFuncs, path);
return path;
}
static int glyphPathMoveTo(FT_Vector *pt, void *path) {
((SplashPath *)path)->moveTo(pt->x / 64.0, -pt->y / 64.0);
return 0;
}
static int glyphPathLineTo(FT_Vector *pt, void *path) {
((SplashPath *)path)->lineTo(pt->x / 64.0, -pt->y / 64.0);
return 0;
}
static int glyphPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *path) {
SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc;
if (!((SplashPath *)path)->getCurPt(&x0, &y0)) {
return 0;
}
xc = ctrl->x / 64.0;
yc = -ctrl->y / 64.0;
x3 = pt->x / 64.0;
y3 = -pt->y / 64.0;
// A second-order Bezier curve is defined by two endpoints, p0 and
// p3, and one control point, pc:
//
// p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3
//
// A third-order Bezier curve is defined by the same two endpoints,
// p0 and p3, and two control points, p1 and p2:
//
// p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3
//
// Applying some algebra, we can convert a second-order curve to a
// third-order curve:
//
// p1 = (1/3) * (p0 + 2pc)
// p2 = (1/3) * (2pc + p3)
x1 = (1.0 / 3.0) * (x0 + 2 * xc);
y1 = (1.0 / 3.0) * (y0 + 2 * yc);
x2 = (1.0 / 3.0) * (2 * xc + x3);
y2 = (1.0 / 3.0) * (2 * yc + y3);
((SplashPath *)path)->curveTo(x1, y1, x2, y2, x3, y3);
return 0;
}
static int glyphPathCubicTo(FT_Vector *ctrl1, FT_Vector *ctrl2,
FT_Vector *pt, void *path) {
((SplashPath *)path)->curveTo(ctrl1->x / 64.0, -ctrl1->y / 64.0,
ctrl2->x / 64.0, -ctrl2->y / 64.0,
pt->x / 64.0, -pt->y / 64.0);
return 0;
}
#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H