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:
parent
11f29b1231
commit
b8e8da0c0b
@ -466,17 +466,16 @@ public:
|
|||||||
int x, y;
|
int x, y;
|
||||||
} IntPoint;
|
} IntPoint;
|
||||||
|
|
||||||
std::string fontFile;
|
std::string fontFile;
|
||||||
std::string name;
|
std::string name;
|
||||||
bool loaded;
|
bool loaded;
|
||||||
|
|
||||||
// The font itself, plus the mapping from ASCII codes to glyphs
|
// The font itself, plus the mapping from ASCII codes to glyphs
|
||||||
int useGlyph[256];
|
std::vector<uint16_t> charMap;
|
||||||
Glyph *glyph;
|
std::vector<Glyph> glyph;
|
||||||
int glyphs;
|
|
||||||
|
|
||||||
int maxPoints;
|
int maxPoints;
|
||||||
int scale;
|
int scale;
|
||||||
|
|
||||||
// The filehandle, while loading
|
// The filehandle, while loading
|
||||||
FILE *fh;
|
FILE *fh;
|
||||||
@ -486,9 +485,9 @@ public:
|
|||||||
ON_CURVE = 1,
|
ON_CURVE = 1,
|
||||||
OFF_CURVE = 2
|
OFF_CURVE = 2
|
||||||
};
|
};
|
||||||
int lastWas;
|
int lastWas;
|
||||||
IntPoint lastOnCurve;
|
IntPoint lastOnCurve;
|
||||||
IntPoint lastOffCurve;
|
IntPoint lastOffCurve;
|
||||||
|
|
||||||
// And the state that the caller must specify, determines where we
|
// And the state that the caller must specify, determines where we
|
||||||
// render to and how
|
// render to and how
|
||||||
@ -506,7 +505,7 @@ public:
|
|||||||
|
|
||||||
void Flush(void);
|
void Flush(void);
|
||||||
void Handle(int *dx, int x, int y, bool onCurve);
|
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,
|
void PlotString(const char *str, double spacing,
|
||||||
SBezierList *sbl, Vector origin, Vector u, Vector v);
|
SBezierList *sbl, Vector origin, Vector u, Vector v);
|
||||||
|
|
||||||
|
79
src/ttf.cpp
79
src/ttf.cpp
@ -99,7 +99,7 @@ uint32_t TtfFont::GetULONG(void) {
|
|||||||
// glyphs[index]
|
// glyphs[index]
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void TtfFont::LoadGlyph(int index) {
|
void TtfFont::LoadGlyph(int index) {
|
||||||
if(index < 0 || index >= glyphs) return;
|
if(index < 0 || index >= glyph.size()) return;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -109,8 +109,12 @@ void TtfFont::LoadGlyph(int index) {
|
|||||||
int16_t xMax = (int16_t)GetUSHORT();
|
int16_t xMax = (int16_t)GetUSHORT();
|
||||||
int16_t yMax = (int16_t)GetUSHORT();
|
int16_t yMax = (int16_t)GetUSHORT();
|
||||||
|
|
||||||
if(useGlyph[(int)'A'] == index) {
|
if(charMap.size() > 'A' && charMap[(int)'A'] == index) {
|
||||||
scale = (1024*1024) / yMax;
|
if(yMax > 0) {
|
||||||
|
scale = (1024*1024) / yMax;
|
||||||
|
} else {
|
||||||
|
scale = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(contours > 0) {
|
if(contours > 0) {
|
||||||
@ -430,8 +434,7 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
|
|||||||
uint16_t maxpMaxComponentElements = GetUSHORT();
|
uint16_t maxpMaxComponentElements = GetUSHORT();
|
||||||
uint16_t maxpMaxComponentDepth = GetUSHORT();
|
uint16_t maxpMaxComponentDepth = GetUSHORT();
|
||||||
|
|
||||||
glyphs = maxpNumGlyphs;
|
glyph.resize(maxpNumGlyphs);
|
||||||
glyph = (Glyph *)MemAlloc(glyphs*sizeof(glyph[0]));
|
|
||||||
|
|
||||||
// Load the hmtx table, which gives the horizontal metrics (spacing
|
// Load the hmtx table, which gives the horizontal metrics (spacing
|
||||||
// and advance width) of the font.
|
// and advance width) of the font.
|
||||||
@ -439,7 +442,7 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
|
|||||||
|
|
||||||
uint16_t hmtxAdvanceWidth = 0;
|
uint16_t hmtxAdvanceWidth = 0;
|
||||||
int16_t hmtxLsb = 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();
|
hmtxAdvanceWidth = GetUSHORT();
|
||||||
hmtxLsb = (int16_t)GetUSHORT();
|
hmtxLsb = (int16_t)GetUSHORT();
|
||||||
|
|
||||||
@ -447,7 +450,7 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
|
|||||||
glyph[i].advanceWidth = hmtxAdvanceWidth;
|
glyph[i].advanceWidth = hmtxAdvanceWidth;
|
||||||
}
|
}
|
||||||
// The last entry in the table applies to all subsequent glyphs also.
|
// 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].leftSideBearing = hmtxLsb;
|
||||||
glyph[i].advanceWidth = hmtxAdvanceWidth;
|
glyph[i].advanceWidth = hmtxAdvanceWidth;
|
||||||
}
|
}
|
||||||
@ -515,38 +518,24 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
|
|||||||
idRangeOffset[i] = GetUSHORT();
|
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++) {
|
for(i = 0; i < segCount; i++) {
|
||||||
|
if(charMap.size() < endChar[i] + 1)
|
||||||
|
charMap.resize(endChar[i] + 1);
|
||||||
|
|
||||||
uint16_t v = idDelta[i];
|
uint16_t v = idDelta[i];
|
||||||
if(idRangeOffset[i] == 0) {
|
if(idRangeOffset[i] == 0) {
|
||||||
int j;
|
for(int j = startChar[i]; j <= endChar[i]; j++) {
|
||||||
for(j = startChar[i]; j <= endChar[i]; j++) {
|
// Arithmetic is modulo 2^16
|
||||||
if(j > 0 && j < (int)arraylen(useGlyph)) {
|
charMap[j] = (uint16_t)(j + v);
|
||||||
// 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 {
|
} else {
|
||||||
int j;
|
for(int j = startChar[i]; j <= endChar[i]; j++) {
|
||||||
for(j = startChar[i]; j <= endChar[i]; j++) {
|
int fp = filePos[i];
|
||||||
if(j > 0 && j < (int)arraylen(useGlyph)) {
|
fp += (j - startChar[i])*sizeof(uint16_t);
|
||||||
int fp = filePos[i];
|
fp += idRangeOffset[i];
|
||||||
fp += (j - startChar[i])*sizeof(uint16_t);
|
fseek(fh, fp, SEEK_SET);
|
||||||
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.
|
// relative to the beginning of the glyf table.
|
||||||
fseek(fh, locaAddr, SEEK_SET);
|
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) {
|
if(headIndexToLocFormat == 1) {
|
||||||
// long offsets, 32 bits
|
// long offsets, 32 bits
|
||||||
glyphOffsets[i] = GetULONG();
|
glyphOffsets[i] = GetULONG();
|
||||||
@ -572,7 +561,7 @@ bool TtfFont::LoadFontFromFile(bool nameOnly) {
|
|||||||
scale = 1024;
|
scale = 1024;
|
||||||
// Load the glyf table. This contains the actual representations of the
|
// Load the glyf table. This contains the actual representations of the
|
||||||
// letter forms, as piecewise linear or quadratic outlines.
|
// 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);
|
fseek(fh, glyfAddr + glyphOffsets[i], SEEK_SET);
|
||||||
LoadGlyph(i);
|
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) {
|
void TtfFont::PlotCharacter(int *dx, char32_t c, double spacing) {
|
||||||
int gli = useGlyph[c];
|
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]);
|
Glyph *g = &(glyph[gli]);
|
||||||
if(!g->pt) return;
|
if(!g->pt) return;
|
||||||
|
|
||||||
@ -688,10 +683,10 @@ void TtfFont::PlotString(const char *str, double spacing,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int dx = 0;
|
int dx = 0;
|
||||||
|
|
||||||
while(*str) {
|
while(*str) {
|
||||||
PlotCharacter(&dx, *str, spacing);
|
char32_t chr;
|
||||||
str++;
|
str = ReadUTF8(str, &chr);
|
||||||
|
PlotCharacter(&dx, chr, spacing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user