Add Unicode support to TTF renderer.

It works. Mostly. Sort of. Only on Windows fonts. Sometimes it
randomly refuses to render glyphs (try `х`, that's not a latin ex).
I'm not really sure why, the logic seems right.

Why do we have a homegrown TTF parser anyway? It's kind of awful.
It breaks on any slightly unusual input. It plows through UTF-16BE
font names like a nuclear-powered steamroller. It outright ignores
composite glyphs (is that why it's broken this time?). The kerning
is seizure-inducing. It ignores any characters outside BMP by design.

Maybe we should just replace it with freetype.
This commit is contained in:
whitequark 2015-12-29 00:18:58 +08:00
parent 11f29b1231
commit b8e8da0c0b
2 changed files with 48 additions and 54 deletions

View File

@ -466,17 +466,16 @@ public:
int x, y;
} IntPoint;
std::string fontFile;
std::string name;
bool loaded;
std::string fontFile;
std::string name;
bool loaded;
// The font itself, plus the mapping from ASCII codes to glyphs
int useGlyph[256];
Glyph *glyph;
int glyphs;
std::vector<uint16_t> charMap;
std::vector<Glyph> glyph;
int maxPoints;
int scale;
int maxPoints;
int scale;
// The filehandle, while loading
FILE *fh;
@ -486,9 +485,9 @@ public:
ON_CURVE = 1,
OFF_CURVE = 2
};
int lastWas;
IntPoint lastOnCurve;
IntPoint lastOffCurve;
int lastWas;
IntPoint lastOnCurve;
IntPoint lastOffCurve;
// And the state that the caller must specify, determines where we
// render to and how
@ -506,7 +505,7 @@ public:
void Flush(void);
void Handle(int *dx, int x, int y, bool onCurve);
void PlotCharacter(int *dx, int c, double spacing);
void PlotCharacter(int *dx, char32_t c, double spacing);
void PlotString(const char *str, double spacing,
SBezierList *sbl, Vector origin, Vector u, Vector v);

View File

@ -99,7 +99,7 @@ uint32_t TtfFont::GetULONG(void) {
// glyphs[index]
//-----------------------------------------------------------------------------
void TtfFont::LoadGlyph(int index) {
if(index < 0 || index >= glyphs) return;
if(index < 0 || index >= glyph.size()) return;
int i;
@ -109,8 +109,12 @@ void TtfFont::LoadGlyph(int index) {
int16_t xMax = (int16_t)GetUSHORT();
int16_t yMax = (int16_t)GetUSHORT();
if(useGlyph[(int)'A'] == index) {
scale = (1024*1024) / yMax;
if(charMap.size() > 'A' && charMap[(int)'A'] == index) {
if(yMax > 0) {
scale = (1024*1024) / yMax;
} else {
scale = 1;
}
}
if(contours > 0) {
@ -430,8 +434,7 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
uint16_t maxpMaxComponentElements = GetUSHORT();
uint16_t maxpMaxComponentDepth = GetUSHORT();
glyphs = maxpNumGlyphs;
glyph = (Glyph *)MemAlloc(glyphs*sizeof(glyph[0]));
glyph.resize(maxpNumGlyphs);
// Load the hmtx table, which gives the horizontal metrics (spacing
// and advance width) of the font.
@ -439,7 +442,7 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
uint16_t hmtxAdvanceWidth = 0;
int16_t hmtxLsb = 0;
for(i = 0; i < min(glyphs, (int)hheaNumberOfMetrics); i++) {
for(i = 0; i < min(glyph.size(), (size_t)hheaNumberOfMetrics); i++) {
hmtxAdvanceWidth = GetUSHORT();
hmtxLsb = (int16_t)GetUSHORT();
@ -447,7 +450,7 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
glyph[i].advanceWidth = hmtxAdvanceWidth;
}
// The last entry in the table applies to all subsequent glyphs also.
for(; i < glyphs; i++) {
for(; i < glyph.size(); i++) {
glyph[i].leftSideBearing = hmtxLsb;
glyph[i].advanceWidth = hmtxAdvanceWidth;
}
@ -515,38 +518,24 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
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++) {
if(charMap.size() < endChar[i] + 1)
charMap.resize(endChar[i] + 1);
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);
}
}
for(int j = startChar[i]; j <= endChar[i]; j++) {
// Arithmetic is modulo 2^16
charMap[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);
for(int j = startChar[i]; j <= endChar[i]; j++) {
int fp = filePos[i];
fp += (j - startChar[i])*sizeof(uint16_t);
fp += idRangeOffset[i];
fseek(fh, fp, SEEK_SET);
useGlyph[j] = GetUSHORT();
}
charMap[j] = GetUSHORT();
}
}
}
@ -555,9 +544,9 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
// relative to the beginning of the glyf table.
fseek(fh, locaAddr, SEEK_SET);
uint32_t *glyphOffsets = (uint32_t *)AllocTemporary(glyphs*sizeof(uint32_t));
uint32_t *glyphOffsets = (uint32_t *)AllocTemporary(glyph.size()*sizeof(uint32_t));
for(i = 0; i < glyphs; i++) {
for(i = 0; i < glyph.size(); i++) {
if(headIndexToLocFormat == 1) {
// long offsets, 32 bits
glyphOffsets[i] = GetULONG();
@ -572,7 +561,7 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
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++) {
for(i = 0; i < glyph.size(); i++) {
fseek(fh, glyfAddr + glyphOffsets[i], SEEK_SET);
LoadGlyph(i);
}
@ -632,10 +621,16 @@ void TtfFont::Handle(int *dx, int x, int y, bool onCurve) {
}
}
void TtfFont::PlotCharacter(int *dx, int c, double spacing) {
int gli = useGlyph[c];
void TtfFont::PlotCharacter(int *dx, char32_t c, double spacing) {
int gli;
if(c < charMap.size()) {
gli = charMap[c];
if(gli < 0 || gli >= glyph.size())
gli = 0; // 0, by convention, is the unknown glyph
} else {
gli = 0;
}
if(gli < 0 || gli >= glyphs) return;
Glyph *g = &(glyph[gli]);
if(!g->pt) return;
@ -688,10 +683,10 @@ void TtfFont::PlotString(const char *str, double spacing,
}
int dx = 0;
while(*str) {
PlotCharacter(&dx, *str, spacing);
str++;
char32_t chr;
str = ReadUTF8(str, &chr);
PlotCharacter(&dx, chr, spacing);
}
}