717 lines
24 KiB
C++
717 lines
24 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Routines to read a TrueType font as vector outlines, and generate them
|
|
// as entities, since they're always representable as either lines or
|
|
// quadratic Bezier curves.
|
|
//
|
|
// Copyright 2008-2013 Jonathan Westhues.
|
|
//-----------------------------------------------------------------------------
|
|
#include "solvespace.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Get the list of available font filenames, and load the name for each of
|
|
// them. Only that, though, not the glyphs too.
|
|
//-----------------------------------------------------------------------------
|
|
void TtfFontList::LoadAll(void) {
|
|
if(loaded) return;
|
|
|
|
// Get the list of font files from the platform-specific code.
|
|
LoadAllFontFiles();
|
|
|
|
int i;
|
|
for(i = 0; i < l.n; i++) {
|
|
TtfFont *tf = &(l.elem[i]);
|
|
tf->LoadFontFromFile(true);
|
|
}
|
|
|
|
loaded = true;
|
|
}
|
|
|
|
void TtfFontList::PlotString(char *font, char *str, double spacing,
|
|
SBezierList *sbl,
|
|
Vector origin, Vector u, Vector v)
|
|
{
|
|
LoadAll();
|
|
|
|
int i;
|
|
for(i = 0; i < l.n; i++) {
|
|
TtfFont *tf = &(l.elem[i]);
|
|
if(strcmp(tf->FontFileBaseName(), font)==0) {
|
|
tf->LoadFontFromFile(false);
|
|
tf->PlotString(str, spacing, sbl, origin, u, v);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Couldn't find the font; so draw a big X for an error marker.
|
|
SBezier sb;
|
|
sb = SBezier::From(origin, origin.Plus(u).Plus(v));
|
|
sbl->l.Add(&sb);
|
|
sb = SBezier::From(origin.Plus(v), origin.Plus(u));
|
|
sbl->l.Add(&sb);
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Get a single character from the open .ttf file; EOF is an error, since
|
|
// we can always see that coming.
|
|
//-----------------------------------------------------------------------------
|
|
int TtfFont::Getc(void) {
|
|
int c = fgetc(fh);
|
|
if(c == EOF) {
|
|
throw "EOF";
|
|
}
|
|
return c;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Helpers to get 1, 2, or 4 bytes from the .ttf file. Big endian.
|
|
// The BYTE, USHORT and ULONG nomenclature comes from the OpenType spec.
|
|
//-----------------------------------------------------------------------------
|
|
uint8_t TtfFont::GetBYTE(void) {
|
|
return (uint8_t)Getc();
|
|
}
|
|
uint16_t TtfFont::GetUSHORT(void) {
|
|
uint8_t b0, b1;
|
|
b1 = (uint8_t)Getc();
|
|
b0 = (uint8_t)Getc();
|
|
|
|
return (b1 << 8) | b0;
|
|
}
|
|
uint32_t TtfFont::GetULONG(void) {
|
|
uint8_t b0, b1, b2, b3;
|
|
b3 = (uint8_t)Getc();
|
|
b2 = (uint8_t)Getc();
|
|
b1 = (uint8_t)Getc();
|
|
b0 = (uint8_t)Getc();
|
|
|
|
return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Load a glyph from the .ttf file into memory. Assumes that the .ttf file
|
|
// is already seeked to the correct location, and writes the result to
|
|
// glyphs[index]
|
|
//-----------------------------------------------------------------------------
|
|
void TtfFont::LoadGlyph(int index) {
|
|
if(index < 0 || index >= glyphs) return;
|
|
|
|
int i;
|
|
|
|
int16_t contours = (int16_t)GetUSHORT();
|
|
int16_t xMin = (int16_t)GetUSHORT();
|
|
int16_t yMin = (int16_t)GetUSHORT();
|
|
int16_t xMax = (int16_t)GetUSHORT();
|
|
int16_t yMax = (int16_t)GetUSHORT();
|
|
|
|
if(useGlyph['A'] == index) {
|
|
scale = (1024*1024) / yMax;
|
|
}
|
|
|
|
if(contours > 0) {
|
|
uint16_t *endPointsOfContours =
|
|
(uint16_t *)AllocTemporary(contours*sizeof(uint16_t));
|
|
|
|
for(i = 0; i < contours; i++) {
|
|
endPointsOfContours[i] = GetUSHORT();
|
|
}
|
|
uint16_t totalPts = endPointsOfContours[i-1] + 1;
|
|
|
|
uint16_t instructionLength = GetUSHORT();
|
|
for(i = 0; i < instructionLength; i++) {
|
|
// We can ignore the instructions, since we're doing vector
|
|
// output.
|
|
(void)GetBYTE();
|
|
}
|
|
|
|
uint8_t *flags = (uint8_t *)AllocTemporary(totalPts*sizeof(uint8_t));
|
|
int16_t *x = (int16_t *)AllocTemporary(totalPts*sizeof(int16_t));
|
|
int16_t *y = (int16_t *)AllocTemporary(totalPts*sizeof(int16_t));
|
|
|
|
// Flags, that indicate format of the coordinates
|
|
#define FLAG_ON_CURVE (1 << 0)
|
|
#define FLAG_DX_IS_BYTE (1 << 1)
|
|
#define FLAG_DY_IS_BYTE (1 << 2)
|
|
#define FLAG_REPEAT (1 << 3)
|
|
#define FLAG_X_IS_SAME (1 << 4)
|
|
#define FLAG_X_IS_POSITIVE (1 << 4)
|
|
#define FLAG_Y_IS_SAME (1 << 5)
|
|
#define FLAG_Y_IS_POSITIVE (1 << 5)
|
|
for(i = 0; i < totalPts; i++) {
|
|
flags[i] = GetBYTE();
|
|
if(flags[i] & FLAG_REPEAT) {
|
|
int n = GetBYTE();
|
|
uint8_t f = flags[i];
|
|
int j;
|
|
for(j = 0; j < n; j++) {
|
|
i++;
|
|
if(i >= totalPts) {
|
|
throw "too many points in glyph";
|
|
}
|
|
flags[i] = f;
|
|
}
|
|
}
|
|
}
|
|
|
|
// x coordinates
|
|
int16_t xa = 0;
|
|
for(i = 0; i < totalPts; i++) {
|
|
if(flags[i] & FLAG_DX_IS_BYTE) {
|
|
uint8_t v = GetBYTE();
|
|
if(flags[i] & FLAG_X_IS_POSITIVE) {
|
|
xa += v;
|
|
} else {
|
|
xa -= v;
|
|
}
|
|
} else {
|
|
if(flags[i] & FLAG_X_IS_SAME) {
|
|
// no change
|
|
} else {
|
|
int16_t d = (int16_t)GetUSHORT();
|
|
xa += d;
|
|
}
|
|
}
|
|
x[i] = xa;
|
|
}
|
|
|
|
// y coordinates
|
|
int16_t ya = 0;
|
|
for(i = 0; i < totalPts; i++) {
|
|
if(flags[i] & FLAG_DY_IS_BYTE) {
|
|
uint8_t v = GetBYTE();
|
|
if(flags[i] & FLAG_Y_IS_POSITIVE) {
|
|
ya += v;
|
|
} else {
|
|
ya -= v;
|
|
}
|
|
} else {
|
|
if(flags[i] & FLAG_Y_IS_SAME) {
|
|
// no change
|
|
} else {
|
|
int16_t d = (int16_t)GetUSHORT();
|
|
ya += d;
|
|
}
|
|
}
|
|
y[i] = ya;
|
|
}
|
|
|
|
Glyph *g = &(glyph[index]);
|
|
g->pt = (FontPoint *)MemAlloc(totalPts*sizeof(FontPoint));
|
|
int contour = 0;
|
|
for(i = 0; i < totalPts; i++) {
|
|
g->pt[i].x = x[i];
|
|
g->pt[i].y = y[i];
|
|
g->pt[i].onCurve = (uint8_t)(flags[i] & FLAG_ON_CURVE);
|
|
|
|
if(i == endPointsOfContours[contour]) {
|
|
g->pt[i].lastInContour = true;
|
|
contour++;
|
|
} else {
|
|
g->pt[i].lastInContour = false;
|
|
}
|
|
}
|
|
g->pts = totalPts;
|
|
g->xMax = xMax;
|
|
g->xMin = xMin;
|
|
|
|
} else {
|
|
// This is a composite glyph, TODO.
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Return the basename of our font filename; that's how the requests and
|
|
// entities that reference us will store it.
|
|
//-----------------------------------------------------------------------------
|
|
const char *TtfFont::FontFileBaseName(void) {
|
|
char *sb = strrchr(fontFile, '\\');
|
|
char *sf = strrchr(fontFile, '/');
|
|
char *s = sf ? sf : sb;
|
|
if(!s) return "";
|
|
return s + 1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Load a TrueType font into memory. We care about the curves that define
|
|
// the letter shapes, and about the mappings that determine which glyph goes
|
|
// with which character.
|
|
//-----------------------------------------------------------------------------
|
|
bool TtfFont::LoadFontFromFile(bool nameOnly) {
|
|
if(loaded) return true;
|
|
|
|
int i;
|
|
|
|
fh = fopen(fontFile, "rb");
|
|
if(!fh) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// First, load the Offset Table
|
|
uint32_t version = GetULONG();
|
|
uint16_t numTables = GetUSHORT();
|
|
uint16_t searchRange = GetUSHORT();
|
|
uint16_t entrySelector = GetUSHORT();
|
|
uint16_t rangeShift = GetUSHORT();
|
|
|
|
// Now load the Table Directory; our goal in doing this will be to
|
|
// find the addresses of the tables that we will need.
|
|
uint32_t glyfAddr = (uint32_t)-1, glyfLen;
|
|
uint32_t cmapAddr = (uint32_t)-1, cmapLen;
|
|
uint32_t headAddr = (uint32_t)-1, headLen;
|
|
uint32_t locaAddr = (uint32_t)-1, locaLen;
|
|
uint32_t maxpAddr = (uint32_t)-1, maxpLen;
|
|
uint32_t nameAddr = (uint32_t)-1, nameLen;
|
|
uint32_t hmtxAddr = (uint32_t)-1, hmtxLen;
|
|
uint32_t hheaAddr = (uint32_t)-1, hheaLen;
|
|
|
|
for(i = 0; i < numTables; i++) {
|
|
char tag[5] = "xxxx";
|
|
tag[0] = (char)GetBYTE();
|
|
tag[1] = (char)GetBYTE();
|
|
tag[2] = (char)GetBYTE();
|
|
tag[3] = (char)GetBYTE();
|
|
uint32_t checksum = GetULONG();
|
|
uint32_t offset = GetULONG();
|
|
uint32_t length = GetULONG();
|
|
|
|
if(strcmp(tag, "glyf")==0) {
|
|
glyfAddr = offset;
|
|
glyfLen = length;
|
|
} else if(strcmp(tag, "cmap")==0) {
|
|
cmapAddr = offset;
|
|
cmapLen = length;
|
|
} else if(strcmp(tag, "head")==0) {
|
|
headAddr = offset;
|
|
headLen = length;
|
|
} else if(strcmp(tag, "loca")==0) {
|
|
locaAddr = offset;
|
|
locaLen = length;
|
|
} else if(strcmp(tag, "maxp")==0) {
|
|
maxpAddr = offset;
|
|
maxpLen = length;
|
|
} else if(strcmp(tag, "name")==0) {
|
|
nameAddr = offset;
|
|
nameLen = length;
|
|
} else if(strcmp(tag, "hhea")==0) {
|
|
hheaAddr = offset;
|
|
hheaLen = length;
|
|
} else if(strcmp(tag, "hmtx")==0) {
|
|
hmtxAddr = offset;
|
|
hmtxLen = length;
|
|
}
|
|
}
|
|
|
|
if(glyfAddr == (uint32_t)-1 ||
|
|
cmapAddr == (uint32_t)-1 ||
|
|
headAddr == (uint32_t)-1 ||
|
|
locaAddr == (uint32_t)-1 ||
|
|
maxpAddr == (uint32_t)-1 ||
|
|
hmtxAddr == (uint32_t)-1 ||
|
|
nameAddr == (uint32_t)-1 ||
|
|
hheaAddr == (uint32_t)-1)
|
|
{
|
|
throw "missing table addr";
|
|
}
|
|
|
|
// Load the name table. This gives us display names for the font, which
|
|
// we need when we're giving the user a list to choose from.
|
|
fseek(fh, nameAddr, SEEK_SET);
|
|
|
|
uint16_t nameFormat = GetUSHORT();
|
|
uint16_t nameCount = GetUSHORT();
|
|
uint16_t nameStringOffset = GetUSHORT();
|
|
// And now we're at the name records. Go through those till we find
|
|
// one that we want.
|
|
int displayNameOffset = 0, displayNameLength = 0;
|
|
for(i = 0; i < nameCount; i++) {
|
|
uint16_t platformID = GetUSHORT();
|
|
uint16_t encodingID = GetUSHORT();
|
|
uint16_t languageID = GetUSHORT();
|
|
uint16_t nameId = GetUSHORT();
|
|
uint16_t length = GetUSHORT();
|
|
uint16_t offset = GetUSHORT();
|
|
|
|
if(nameId == 4) {
|
|
displayNameOffset = offset;
|
|
displayNameLength = length;
|
|
break;
|
|
}
|
|
}
|
|
if(nameOnly && i >= nameCount) {
|
|
throw "no name";
|
|
}
|
|
|
|
if(nameOnly) {
|
|
// Find the display name, and store it in the provided buffer.
|
|
fseek(fh, nameAddr+nameStringOffset+displayNameOffset, SEEK_SET);
|
|
int c = 0;
|
|
for(i = 0; i < displayNameLength; i++) {
|
|
uint8_t b = GetBYTE();
|
|
if(b && c < ((int)sizeof(name.str) - 2)) {
|
|
name.str[c++] = b;
|
|
}
|
|
}
|
|
name.str[c++] = '\0';
|
|
|
|
fclose(fh);
|
|
return true;
|
|
}
|
|
|
|
|
|
// Load the head table; we need this to determine the format of the
|
|
// loca table, 16- or 32-bit entries
|
|
fseek(fh, headAddr, SEEK_SET);
|
|
|
|
uint32_t headVersion = GetULONG();
|
|
uint32_t headFontRevision = GetULONG();
|
|
uint32_t headCheckSumAdj = GetULONG();
|
|
uint32_t headMagicNumber = GetULONG();
|
|
uint16_t headFlags = GetUSHORT();
|
|
uint16_t headUnitsPerEm = GetUSHORT();
|
|
(void)GetULONG(); // created time
|
|
(void)GetULONG();
|
|
(void)GetULONG(); // modified time
|
|
(void)GetULONG();
|
|
uint16_t headXmin = GetUSHORT();
|
|
uint16_t headYmin = GetUSHORT();
|
|
uint16_t headXmax = GetUSHORT();
|
|
uint16_t headYmax = GetUSHORT();
|
|
uint16_t headMacStyle = GetUSHORT();
|
|
uint16_t headLowestRecPPEM = GetUSHORT();
|
|
uint16_t headFontDirectionHint = GetUSHORT();
|
|
uint16_t headIndexToLocFormat = GetUSHORT();
|
|
uint16_t headGlyphDataFormat = GetUSHORT();
|
|
|
|
if(headMagicNumber != 0x5F0F3CF5) {
|
|
throw "bad magic number";
|
|
}
|
|
|
|
// Load the hhea table, which contains the number of entries in the
|
|
// horizontal metrics (hmtx) table.
|
|
fseek(fh, hheaAddr, SEEK_SET);
|
|
uint32_t hheaVersion = GetULONG();
|
|
uint16_t hheaAscender = GetUSHORT();
|
|
uint16_t hheaDescender = GetUSHORT();
|
|
uint16_t hheaLineGap = GetUSHORT();
|
|
uint16_t hheaAdvanceWidthMax = GetUSHORT();
|
|
uint16_t hheaMinLsb = GetUSHORT();
|
|
uint16_t hheaMinRsb = GetUSHORT();
|
|
uint16_t hheaXMaxExtent = GetUSHORT();
|
|
uint16_t hheaCaretSlopeRise = GetUSHORT();
|
|
uint16_t hheaCaretSlopeRun = GetUSHORT();
|
|
uint16_t hheaCaretOffset = GetUSHORT();
|
|
(void)GetUSHORT();
|
|
(void)GetUSHORT();
|
|
(void)GetUSHORT();
|
|
(void)GetUSHORT();
|
|
uint16_t hheaMetricDataFormat = GetUSHORT();
|
|
uint16_t hheaNumberOfMetrics = GetUSHORT();
|
|
|
|
// Load the maxp table, which determines (among other things) the number
|
|
// of glyphs in the font
|
|
fseek(fh, maxpAddr, SEEK_SET);
|
|
|
|
uint32_t maxpVersion = GetULONG();
|
|
uint16_t maxpNumGlyphs = GetUSHORT();
|
|
uint16_t maxpMaxPoints = GetUSHORT();
|
|
uint16_t maxpMaxContours = GetUSHORT();
|
|
uint16_t maxpMaxComponentPoints = GetUSHORT();
|
|
uint16_t maxpMaxComponentContours = GetUSHORT();
|
|
uint16_t maxpMaxZones = GetUSHORT();
|
|
uint16_t maxpMaxTwilightPoints = GetUSHORT();
|
|
uint16_t maxpMaxStorage = GetUSHORT();
|
|
uint16_t maxpMaxFunctionDefs = GetUSHORT();
|
|
uint16_t maxpMaxInstructionDefs = GetUSHORT();
|
|
uint16_t maxpMaxStackElements = GetUSHORT();
|
|
uint16_t maxpMaxSizeOfInstructions = GetUSHORT();
|
|
uint16_t maxpMaxComponentElements = GetUSHORT();
|
|
uint16_t maxpMaxComponentDepth = GetUSHORT();
|
|
|
|
glyphs = maxpNumGlyphs;
|
|
glyph = (Glyph *)MemAlloc(glyphs*sizeof(glyph[0]));
|
|
|
|
// Load the hmtx table, which gives the horizontal metrics (spacing
|
|
// and advance width) of the font.
|
|
fseek(fh, hmtxAddr, SEEK_SET);
|
|
|
|
uint16_t hmtxAdvanceWidth = 0;
|
|
int16_t hmtxLsb = 0;
|
|
for(i = 0; i < min(glyphs, hheaNumberOfMetrics); i++) {
|
|
hmtxAdvanceWidth = GetUSHORT();
|
|
hmtxLsb = (int16_t)GetUSHORT();
|
|
|
|
glyph[i].leftSideBearing = hmtxLsb;
|
|
glyph[i].advanceWidth = hmtxAdvanceWidth;
|
|
}
|
|
// The last entry in the table applies to all subsequent glyphs also.
|
|
for(; i < glyphs; i++) {
|
|
glyph[i].leftSideBearing = hmtxLsb;
|
|
glyph[i].advanceWidth = hmtxAdvanceWidth;
|
|
}
|
|
|
|
// Load the cmap table, which determines the mapping of characters to
|
|
// glyphs.
|
|
fseek(fh, cmapAddr, SEEK_SET);
|
|
|
|
uint32_t usedTableAddr = (uint32_t)-1;
|
|
|
|
uint16_t cmapVersion = GetUSHORT();
|
|
uint16_t cmapTableCount = GetUSHORT();
|
|
for(i = 0; i < cmapTableCount; i++) {
|
|
uint16_t platformId = GetUSHORT();
|
|
uint16_t encodingId = GetUSHORT();
|
|
uint32_t offset = GetULONG();
|
|
|
|
if(platformId == 3 && encodingId == 1) {
|
|
// The Windows Unicode mapping is our preference
|
|
usedTableAddr = cmapAddr + offset;
|
|
}
|
|
}
|
|
|
|
if(usedTableAddr == (uint32_t)-1) {
|
|
throw "no used table addr";
|
|
}
|
|
|
|
// So we can load the desired subtable; in this case, Windows Unicode,
|
|
// which is us.
|
|
fseek(fh, usedTableAddr, SEEK_SET);
|
|
|
|
uint16_t mapFormat = GetUSHORT();
|
|
uint16_t mapLength = GetUSHORT();
|
|
uint16_t mapVersion = GetUSHORT();
|
|
uint16_t mapSegCountX2 = GetUSHORT();
|
|
uint16_t mapSearchRange = GetUSHORT();
|
|
uint16_t mapEntrySelector = GetUSHORT();
|
|
uint16_t mapRangeShift = GetUSHORT();
|
|
|
|
if(mapFormat != 4) {
|
|
// Required to use format 4 per spec
|
|
throw "not format 4";
|
|
}
|
|
|
|
int segCount = mapSegCountX2 / 2;
|
|
uint16_t *endChar = (uint16_t *)AllocTemporary(segCount*sizeof(uint16_t));
|
|
uint16_t *startChar = (uint16_t *)AllocTemporary(segCount*sizeof(uint16_t));
|
|
uint16_t *idDelta = (uint16_t *)AllocTemporary(segCount*sizeof(uint16_t));
|
|
uint16_t *idRangeOffset = (uint16_t *)AllocTemporary(segCount*sizeof(uint16_t));
|
|
|
|
uint32_t *filePos = (uint32_t *)AllocTemporary(segCount*sizeof(uint32_t));
|
|
|
|
for(i = 0; i < segCount; i++) {
|
|
endChar[i] = GetUSHORT();
|
|
}
|
|
uint16_t mapReservedPad = GetUSHORT();
|
|
for(i = 0; i < segCount; i++) {
|
|
startChar[i] = GetUSHORT();
|
|
}
|
|
for(i = 0; i < segCount; i++) {
|
|
idDelta[i] = GetUSHORT();
|
|
}
|
|
for(i = 0; i < segCount; i++) {
|
|
filePos[i] = (uint32_t)ftell(fh);
|
|
idRangeOffset[i] = GetUSHORT();
|
|
}
|
|
|
|
// So first, null out the glyph table in our in-memory representation
|
|
// of the font; any character for which cmap does not provide a glyph
|
|
// corresponds to -1
|
|
for(i = 0; i < (int)arraylen(useGlyph); i++) {
|
|
useGlyph[i] = 0;
|
|
}
|
|
|
|
for(i = 0; i < segCount; i++) {
|
|
uint16_t v = idDelta[i];
|
|
if(idRangeOffset[i] == 0) {
|
|
int j;
|
|
for(j = startChar[i]; j <= endChar[i]; j++) {
|
|
if(j > 0 && j < (int)arraylen(useGlyph)) {
|
|
// Don't create a reference to a glyph that we won't
|
|
// store because it's bigger than the table.
|
|
if((uint16_t)(j + v) < glyphs) {
|
|
// Arithmetic is modulo 2^16
|
|
useGlyph[j] = (uint16_t)(j + v);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
int j;
|
|
for(j = startChar[i]; j <= endChar[i]; j++) {
|
|
if(j > 0 && j < (int)arraylen(useGlyph)) {
|
|
int fp = filePos[i];
|
|
fp += (j - startChar[i])*sizeof(uint16_t);
|
|
fp += idRangeOffset[i];
|
|
fseek(fh, fp, SEEK_SET);
|
|
|
|
useGlyph[j] = GetUSHORT();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load the loca table. This contains the offsets of each glyph,
|
|
// relative to the beginning of the glyf table.
|
|
fseek(fh, locaAddr, SEEK_SET);
|
|
|
|
uint32_t *glyphOffsets = (uint32_t *)AllocTemporary(glyphs*sizeof(uint32_t));
|
|
|
|
for(i = 0; i < glyphs; i++) {
|
|
if(headIndexToLocFormat == 1) {
|
|
// long offsets, 32 bits
|
|
glyphOffsets[i] = GetULONG();
|
|
} else if(headIndexToLocFormat == 0) {
|
|
// short offsets, 16 bits but divided by 2
|
|
glyphOffsets[i] = GetUSHORT()*2;
|
|
} else {
|
|
throw "bad headIndexToLocFormat";
|
|
}
|
|
}
|
|
|
|
scale = 1024;
|
|
// Load the glyf table. This contains the actual representations of the
|
|
// letter forms, as piecewise linear or quadratic outlines.
|
|
for(i = 0; i < glyphs; i++) {
|
|
fseek(fh, glyfAddr + glyphOffsets[i], SEEK_SET);
|
|
LoadGlyph(i);
|
|
}
|
|
} catch (char *s) {
|
|
dbp("failed: '%s'", s);
|
|
fclose(fh);
|
|
return false;
|
|
}
|
|
|
|
fclose(fh);
|
|
loaded = true;
|
|
return true;
|
|
}
|
|
|
|
void TtfFont::Flush(void) {
|
|
lastWas = NOTHING;
|
|
}
|
|
|
|
void TtfFont::Handle(int *dx, int x, int y, bool onCurve) {
|
|
x = ((x + *dx)*scale + 512) >> 10;
|
|
y = (y*scale + 512) >> 10;
|
|
|
|
if(lastWas == ON_CURVE && onCurve) {
|
|
// This is a line segment.
|
|
LineSegment(lastOnCurve.x, lastOnCurve.y, x, y);
|
|
} else if(lastWas == ON_CURVE && !onCurve) {
|
|
// We can't do the Bezier until we get the next on-curve point,
|
|
// but we must store the off-curve point.
|
|
} else if(lastWas == OFF_CURVE && onCurve) {
|
|
// We are ready to do a Bezier.
|
|
Bezier(lastOnCurve.x, lastOnCurve.y,
|
|
lastOffCurve.x, lastOffCurve.y,
|
|
x, y);
|
|
} else if(lastWas == OFF_CURVE && !onCurve) {
|
|
// Two consecutive off-curve points implicitly have an on-point
|
|
// curve between them, and that should trigger us to generate a
|
|
// Bezier.
|
|
IntPoint fake;
|
|
fake.x = (x + lastOffCurve.x) / 2;
|
|
fake.y = (y + lastOffCurve.y) / 2;
|
|
Bezier(lastOnCurve.x, lastOnCurve.y,
|
|
lastOffCurve.x, lastOffCurve.y,
|
|
fake.x, fake.y);
|
|
|
|
lastOnCurve.x = fake.x;
|
|
lastOnCurve.y = fake.y;
|
|
}
|
|
|
|
if(onCurve) {
|
|
lastOnCurve.x = x;
|
|
lastOnCurve.y = y;
|
|
lastWas = ON_CURVE;
|
|
} else {
|
|
lastOffCurve.x = x;
|
|
lastOffCurve.y = y;
|
|
lastWas = OFF_CURVE;
|
|
}
|
|
}
|
|
|
|
void TtfFont::PlotCharacter(int *dx, int c, double spacing) {
|
|
int gli = useGlyph[c];
|
|
|
|
if(gli < 0 || gli >= glyphs) return;
|
|
Glyph *g = &(glyph[gli]);
|
|
if(!g->pt) return;
|
|
|
|
if(c == ' ') {
|
|
*dx += g->advanceWidth;
|
|
return;
|
|
}
|
|
|
|
int dx0 = *dx;
|
|
|
|
// A point that has x = xMin should be plotted at (dx0 + lsb); fix up
|
|
// our x-position so that the curve-generating code will put stuff
|
|
// at the right place.
|
|
*dx = dx0 - g->xMin;
|
|
*dx += g->leftSideBearing;
|
|
|
|
int i;
|
|
int firstInContour = 0;
|
|
for(i = 0; i < g->pts; i++) {
|
|
Handle(dx, g->pt[i].x, g->pt[i].y, g->pt[i].onCurve);
|
|
|
|
if(g->pt[i].lastInContour) {
|
|
int f = firstInContour;
|
|
Handle(dx, g->pt[f].x, g->pt[f].y, g->pt[f].onCurve);
|
|
firstInContour = i + 1;
|
|
Flush();
|
|
}
|
|
}
|
|
|
|
// And we're done, so advance our position by the requested advance
|
|
// width, plus the user-requested extra advance.
|
|
*dx = dx0 + g->advanceWidth + (int)(spacing + 0.5);
|
|
}
|
|
|
|
void TtfFont::PlotString(char *str, double spacing,
|
|
SBezierList *sbl,
|
|
Vector porigin, Vector pu, Vector pv)
|
|
{
|
|
beziers = sbl;
|
|
u = pu;
|
|
v = pv;
|
|
origin = porigin;
|
|
|
|
if(!loaded || !str || *str == '\0') {
|
|
LineSegment(0, 0, 1024, 0);
|
|
LineSegment(1024, 0, 1024, 1024);
|
|
LineSegment(1024, 1024, 0, 1024);
|
|
LineSegment(0, 1024, 0, 0);
|
|
return;
|
|
}
|
|
|
|
int dx = 0;
|
|
|
|
while(*str) {
|
|
PlotCharacter(&dx, *str, spacing);
|
|
str++;
|
|
}
|
|
}
|
|
|
|
Vector TtfFont::TransformIntPoint(int x, int y) {
|
|
Vector r = origin;
|
|
r = r.Plus(u.ScaledBy(x / 1024.0));
|
|
r = r.Plus(v.ScaledBy(y / 1024.0));
|
|
return r;
|
|
}
|
|
|
|
void TtfFont::LineSegment(int x0, int y0, int x1, int y1) {
|
|
SBezier sb = SBezier::From(TransformIntPoint(x0, y0),
|
|
TransformIntPoint(x1, y1));
|
|
beziers->l.Add(&sb);
|
|
}
|
|
|
|
void TtfFont::Bezier(int x0, int y0, int x1, int y1, int x2, int y2) {
|
|
SBezier sb = SBezier::From(TransformIntPoint(x0, y0),
|
|
TransformIntPoint(x1, y1),
|
|
TransformIntPoint(x2, y2));
|
|
beziers->l.Add(&sb);
|
|
}
|
|
|