From 25febd82ed40259ad13c048f23ebfeac1bd56c47 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 2 Nov 2016 08:59:33 +0000 Subject: [PATCH] TTF: use metrics of 'A' to determine cap height. SolveSpace 2.0 used the height of 'A' (i.e. cap height) to determine the reference height. SolveSpace 2.1 completely broke that during transition to Freetype, and used something more or less random, by using FT_Set_Char_Size with units_per_EM. SolveSpace 2.2 attempted to fix that, but also used something more or less random, by using FT_Request_Size with "unit" values. Turns out that Freetype actually doesn't have a concept of cap height at all. It is possible to extract it from the TT_OS2 table that is present in some TrueType fonts, but it is not present in Microsoft fonts (the msttcorefonts ones), and for those Linux fonts in which it is present it doesn't appear very reliable. So instead, use the height of 'A' instead, like version 2.0 did. This has the advantage that it is quite bulletproof, and also matches exactly what the old files are measured against. One downside is that fonts without an 'A' glyph would not render. We can deal with that when it becomes a problem. --- src/ttf.cpp | 26 ++++++++++++++++++++++++-- src/ttf.h | 1 + 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/ttf.cpp b/src/ttf.cpp index 13c7a8e..79cbffd 100644 --- a/src/ttf.cpp +++ b/src/ttf.cpp @@ -168,6 +168,28 @@ bool TtfFont::LoadFromFile(FT_Library fontLibrary, bool nameOnly) { return false; } + char chr = 'A'; + uint32_t gid = FT_Get_Char_Index(fontFace, 'A'); + if (gid == 0) { + dbp("freetype: CID-to-GID mapping for CID 0x%04x failed: %s; using CID as GID", + chr, ft_error_string(gid)); + gid = chr; + } + + if(gid) { + if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) { + dbp("freetype: cannot load glyph for GID 0x%04x: %s", + gid, ft_error_string(fterr)); + FT_Done_Face(fontFace); + fontFace = NULL; + return false; + } + + FT_BBox bbox; + FT_Outline_Get_CBox(&fontFace->glyph->outline, &bbox); + capHeight = (double)bbox.yMax; + } + return true; } @@ -264,7 +286,7 @@ void TtfFont::PlotString(const std::string &str, * ones, antialiasing mitigates this considerably though. */ if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) { - dbp("freetype: cannot load glyph (gid %d): %s", + dbp("freetype: cannot load glyph for GID 0x%04x: %s", gid, ft_error_string(fterr)); return; } @@ -294,7 +316,7 @@ void TtfFont::PlotString(const std::string &str, data.u = u; data.v = v; data.beziers = sbl; - data.factor = 1.0f/(float)(1 << 16); + data.factor = 1.0 / capHeight; data.bx = bx; if(int fterr = FT_Outline_Decompose(&fontFace->glyph->outline, &outlineFuncs, &data)) { dbp("freetype: bezier decomposition failed (gid %d): %s", diff --git a/src/ttf.h b/src/ttf.h index fb26ab9..ec91127 100644 --- a/src/ttf.h +++ b/src/ttf.h @@ -14,6 +14,7 @@ public: std::string fontFile; std::string name; FT_FaceRec_ *fontFace; + double capHeight; std::string FontFileBaseName() const; bool LoadFromFile(FT_LibraryRec_ *fontLibrary, bool nameOnly = true);