Spaces:
Running
Running
//======================================================================== | |
// | |
// SplashFTFont.cc | |
// | |
//======================================================================== | |
//======================================================================== | |
// | |
// Modified under the Poppler project - http://poppler.freedesktop.org | |
// | |
// All changes made under the Poppler project to this file are licensed | |
// under GPL version 2 or later | |
// | |
// Copyright (C) 2005, 2007-2011, 2014, 2018, 2020 Albert Astals Cid <[email protected]> | |
// Copyright (C) 2006 Kristian Høgsberg <[email protected]> | |
// Copyright (C) 2009 Petr Gajdos <[email protected]> | |
// Copyright (C) 2010 Suzuki Toshiya <[email protected]> | |
// Copyright (C) 2011 Andreas Hartmetz <[email protected]> | |
// Copyright (C) 2012 Thomas Freitag <[email protected]> | |
// Copyright (C) 2017 Adrian Johnson <[email protected]> | |
// Copyright (C) 2018 Oliver Sander <[email protected]> | |
// | |
// To see a description of the changes please see the Changelog file that | |
// came with your tarball or type make ChangeLog if you are building from git | |
// | |
//======================================================================== | |
//------------------------------------------------------------------------ | |
static int glyphPathMoveTo(const FT_Vector *pt, void *path); | |
static int glyphPathLineTo(const FT_Vector *pt, void *path); | |
static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path); | |
static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path); | |
//------------------------------------------------------------------------ | |
// SplashFTFont | |
//------------------------------------------------------------------------ | |
SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, const SplashCoord *textMatA) | |
: SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa), textScale(0), enableFreeTypeHinting(fontFileA->engine->enableFreeTypeHinting), enableSlightHinting(fontFileA->engine->enableSlightHinting), isOk(false) | |
{ | |
FT_Face face; | |
int div; | |
int x, y; | |
face = fontFileA->face; | |
if (FT_New_Size(face, &sizeObj)) { | |
return; | |
} | |
face->size = sizeObj; | |
size = splashRound(splashDist(0, 0, mat[2], mat[3])); | |
if (size < 1) { | |
size = 1; | |
} | |
if (FT_Set_Pixel_Sizes(face, 0, size)) { | |
return; | |
} | |
// if the textMat values are too small, FreeType's fixed point | |
// arithmetic doesn't work so well | |
textScale = splashDist(0, 0, textMat[2], textMat[3]) / size; | |
if (unlikely(textScale == 0 || face->units_per_EM == 0)) { | |
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 = size; | |
} | |
if (yMax == yMin) { | |
yMin = 0; | |
yMax = (int)((SplashCoord)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); | |
textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)) * 65536); | |
textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)) * 65536); | |
textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)) * 65536); | |
textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)) * 65536); | |
isOk = true; | |
} | |
SplashFTFont::~SplashFTFont() { } | |
bool SplashFTFont::getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) | |
{ | |
return SplashFont::getGlyph(c, xFrac, 0, bitmap, x0, y0, clip, clipRes); | |
} | |
static FT_Int32 getFTLoadFlags(bool type1, bool trueType, bool aa, bool enableFreeTypeHinting, bool enableSlightHinting) | |
{ | |
int ret = FT_LOAD_DEFAULT; | |
if (aa) { | |
ret |= FT_LOAD_NO_BITMAP; | |
} | |
if (enableFreeTypeHinting) { | |
if (enableSlightHinting) { | |
ret |= FT_LOAD_TARGET_LIGHT; | |
} else { | |
if (trueType) { | |
// 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 (aa) { | |
ret |= FT_LOAD_NO_AUTOHINT; | |
} | |
} else if (type1) { | |
// Type 1 fonts seem to look better with 'light' hinting mode | |
ret |= FT_LOAD_TARGET_LIGHT; | |
} | |
} | |
} else { | |
ret |= FT_LOAD_NO_HINTING; | |
} | |
return ret; | |
} | |
bool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) | |
{ | |
SplashFTFontFile *ff; | |
FT_Vector offset; | |
FT_GlyphSlot slot; | |
FT_UInt gid; | |
int rowSize; | |
unsigned char *p, *q; | |
int i; | |
if (unlikely(!isOk)) { | |
return false; | |
} | |
ff = (SplashFTFontFile *)fontFile; | |
ff->face->size = sizeObj; | |
offset.x = (FT_Pos)(int)((SplashCoord)xFrac * splashFontFractionMul * 64); | |
offset.y = 0; | |
FT_Set_Transform(ff->face, &matrix, &offset); | |
slot = ff->face->glyph; | |
if (ff->codeToGID && c < ff->codeToGIDLen && c >= 0) { | |
gid = (FT_UInt)ff->codeToGID[c]; | |
} else { | |
gid = (FT_UInt)c; | |
} | |
if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableFreeTypeHinting, enableSlightHinting))) { | |
return false; | |
} | |
// prelimirary values based on FT_Outline_Get_CBox | |
// we add two pixels to each side to be in the safe side | |
FT_BBox cbox; | |
FT_Outline_Get_CBox(&ff->face->glyph->outline, &cbox); | |
bitmap->x = -(cbox.xMin / 64) + 2; | |
bitmap->y = (cbox.yMax / 64) + 2; | |
bitmap->w = ((cbox.xMax - cbox.xMin) / 64) + 4; | |
bitmap->h = ((cbox.yMax - cbox.yMin) / 64) + 4; | |
*clipRes = clip->testRect(x0 - bitmap->x, y0 - bitmap->y, x0 - bitmap->x + bitmap->w, y0 - bitmap->y + bitmap->h); | |
if (*clipRes == splashClipAllOutside) { | |
bitmap->freeData = false; | |
return true; | |
} | |
if (FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono)) { | |
return false; | |
} | |
if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) { | |
// this can happen if (a) the glyph is really tiny or (b) the | |
// metrics in the TrueType file are broken | |
return false; | |
} | |
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 = (unsigned char *)gmallocn_checkoverflow(rowSize, bitmap->h); | |
if (!bitmap->data) { | |
return false; | |
} | |
bitmap->freeData = true; | |
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 true; | |
} | |
double SplashFTFont::getGlyphAdvance(int c) | |
{ | |
SplashFTFontFile *ff; | |
FT_Vector offset; | |
FT_UInt gid; | |
FT_Matrix identityMatrix; | |
ff = (SplashFTFontFile *)fontFile; | |
// init the matrix | |
identityMatrix.xx = 65536; // 1 in 16.16 format | |
identityMatrix.xy = 0; | |
identityMatrix.yx = 0; | |
identityMatrix.yy = 65536; // 1 in 16.16 format | |
// init the offset | |
offset.x = 0; | |
offset.y = 0; | |
ff->face->size = sizeObj; | |
FT_Set_Transform(ff->face, &identityMatrix, &offset); | |
if (ff->codeToGID && c < ff->codeToGIDLen) { | |
gid = (FT_UInt)ff->codeToGID[c]; | |
} else { | |
gid = (FT_UInt)c; | |
} | |
if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableFreeTypeHinting, enableSlightHinting))) { | |
return -1; | |
} | |
// 64.0 is 1 in 26.6 format | |
return ff->face->glyph->metrics.horiAdvance / 64.0 / size; | |
} | |
struct SplashFTFontPath | |
{ | |
SplashPath *path; | |
SplashCoord textScale; | |
bool needClose; | |
}; | |
SplashPath *SplashFTFont::getGlyphPath(int c) | |
{ | |
static const FT_Outline_Funcs outlineFuncs = { | |
(int (*)(FT_Vector *, void *)) & glyphPathMoveTo, | |
(int (*)(FT_Vector *, void *)) & glyphPathLineTo, | |
(int (*)(FT_Vector *, FT_Vector *, void *)) & glyphPathConicTo, | |
(int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *)) & glyphPathCubicTo, | |
&glyphPathMoveTo, | |
&glyphPathLineTo, | |
&glyphPathConicTo, | |
&glyphPathCubicTo, | |
0, | |
0 | |
}; | |
SplashFTFontFile *ff; | |
SplashFTFontPath path; | |
FT_GlyphSlot slot; | |
FT_UInt gid; | |
FT_Glyph glyph; | |
if (unlikely(textScale == 0)) { | |
return nullptr; | |
} | |
ff = (SplashFTFontFile *)fontFile; | |
ff->face->size = sizeObj; | |
FT_Set_Transform(ff->face, &textMatrix, nullptr); | |
slot = ff->face->glyph; | |
if (ff->codeToGID && c < ff->codeToGIDLen && c >= 0) { | |
gid = ff->codeToGID[c]; | |
} else { | |
gid = (FT_UInt)c; | |
} | |
if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableFreeTypeHinting, enableSlightHinting))) { | |
return nullptr; | |
} | |
if (FT_Get_Glyph(slot, &glyph)) { | |
return nullptr; | |
} | |
if (FT_Outline_Check(&((FT_OutlineGlyph)glyph)->outline)) { | |
return nullptr; | |
} | |
path.path = new SplashPath(); | |
path.textScale = textScale; | |
path.needClose = false; | |
FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline, &outlineFuncs, &path); | |
if (path.needClose) { | |
path.path->close(); | |
} | |
FT_Done_Glyph(glyph); | |
return path.path; | |
} | |
static int glyphPathMoveTo(const FT_Vector *pt, void *path) | |
{ | |
SplashFTFontPath *p = (SplashFTFontPath *)path; | |
if (p->needClose) { | |
p->path->close(); | |
p->needClose = false; | |
} | |
p->path->moveTo((SplashCoord)pt->x * p->textScale / 64.0, (SplashCoord)pt->y * p->textScale / 64.0); | |
return 0; | |
} | |
static int glyphPathLineTo(const FT_Vector *pt, void *path) | |
{ | |
SplashFTFontPath *p = (SplashFTFontPath *)path; | |
p->path->lineTo((SplashCoord)pt->x * p->textScale / 64.0, (SplashCoord)pt->y * p->textScale / 64.0); | |
p->needClose = true; | |
return 0; | |
} | |
static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path) | |
{ | |
SplashFTFontPath *p = (SplashFTFontPath *)path; | |
SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc; | |
if (!p->path->getCurPt(&x0, &y0)) { | |
return 0; | |
} | |
xc = (SplashCoord)ctrl->x * p->textScale / 64.0; | |
yc = (SplashCoord)ctrl->y * p->textScale / 64.0; | |
x3 = (SplashCoord)pt->x * p->textScale / 64.0; | |
y3 = (SplashCoord)pt->y * p->textScale / 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 = (SplashCoord)(1.0 / 3.0) * (x0 + (SplashCoord)2 * xc); | |
y1 = (SplashCoord)(1.0 / 3.0) * (y0 + (SplashCoord)2 * yc); | |
x2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * xc + x3); | |
y2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * yc + y3); | |
p->path->curveTo(x1, y1, x2, y2, x3, y3); | |
p->needClose = true; | |
return 0; | |
} | |
static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path) | |
{ | |
SplashFTFontPath *p = (SplashFTFontPath *)path; | |
p->path->curveTo((SplashCoord)ctrl1->x * p->textScale / 64.0, (SplashCoord)ctrl1->y * p->textScale / 64.0, (SplashCoord)ctrl2->x * p->textScale / 64.0, (SplashCoord)ctrl2->y * p->textScale / 64.0, | |
(SplashCoord)pt->x * p->textScale / 64.0, (SplashCoord)pt->y * p->textScale / 64.0); | |
p->needClose = true; | |
return 0; | |
} | |