From 23feb4cf8f22341c1f47810c9955a9b8475a14e6 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 + test/request/ttf_text/normal.png | Bin 6155 -> 6138 bytes 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/ttf.cpp b/src/ttf.cpp index d79879b..e83ff57 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); diff --git a/test/request/ttf_text/normal.png b/test/request/ttf_text/normal.png index ee03529865ba9ad46a416ea679dc7584c03522bf..3397538fac79242aa90b9c892759f58d4d98e4b4 100644 GIT binary patch literal 6138 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 b`fc^<0`YxWOD7Q$;=#T>hkh>Dg*x{?)DrzL literal 6155 zcmeHLeLRzU|G$itLa9zV$wP4*DGx;oGn}ZMR?(4|hdiVR6_Te-ZB*1zM4n^Q=@2o- zVxBjNg)xy~7!t#<7-od~>OP(OcXyxHeV^Yy_aDFC>%3mOw!OaBb$uS*-_Pgs{e0f{ z&sZX4Hp*`V06^x%j~3Pd06~4eB-eo}H6AH_0I;L|gvAdwA?Z{7KJCwrM?}8;weI9> z{JxKAr@G?NrH9g#OVK3c4l|waQq#9RCFLQNf7OOssY%LYB{+%!yGLD0)&oG>u5c(I z`#lK&UP;ISW+^lXAh``xRQbIxY%0x+u{h6^ZpS9OR!XwYIuStGoedKIc z@F)PtURd|B$Nm9E-VRjuk{Hf1we~k;%&7Ym5B@I^GJ|F3E=b~7Hh-|g zx{0{38vxLM8S*9gBL706+vP^7p2wz#@wh&fsb?*hV8>N4*!2$PV(KF4V z>pBF>r`Z%-7q@)Xp$u40pnJx77Ed%aV(7|G;4Bw!FS z?XOlGGbFobZld0e2CgHUd%Lh*j`Ihk=WRE+lp#6~NRI?nt_*ign!DECJw*L!9+M%0$u z0n1xZfL_ElqqF1pQfU!V+qN|u*vX(yy4FHBL^6#Lv?HL1H^%Y|XR%3o@RnCl26bZt zgK<6Ln{FhC%)L98jIC%C1r-TEQtvjjExaV^R!e(rjf9-pGyJj6O24#$0nsIrPB^sf z5k1FY8-h8#yjU9rzrfQ}&PNHly<Nt=s*QlxEMs0QonqB8T*cDg zg+-y?wR+VQ%rVS;^mZa*gkxnFu;v=*1gQjX^hr4>~q4C3^r9!0v{Q{;pOMy!0IHv08os(g~E3)={>rU8+{ zItNd*PSYF(%4BEunUh2MCCi>-t!-J&}RTm^t94!G$UN z)HwOq>p`RpC~L+dKGuV3QbfS636<FmXlty(1tf%>9g#-U=1e!j}5QO2rQ zlg@I7uGYY;7Itl9t#~pw!D|5{Si_d_V)bK(b~8N+ZzWjYGBIxLC16+QO$7r3pVGDF zE{LdMn+Kyt2Ch+bKh4nJkE~r7S&TAW-AgfDb6UN5cX*LW2tCZA_Pa#c2}9-WhPZ1x ze3GycLMy%Ifu1wSTmcj3??m}weFbUBWla!3%q~jsFYRB1A1SD2s17RGo0|3&W|tIN zwe+NGF?%X!N>_7h^_lckWrR}}CkF=RVUfr!UwI@HHIq>ir!QUCTNh1i*v=hR(9BeH ze?sOE-oF^xmhIDUe70StfW;|7BAUU>;Vvv+N&2PstkmQqVIU#SLGSVWqMmjmO$oer zXJzkICbH!FnR7>(HF24~1%ZJ{ilKRW`k1gYw5LCmz7T309gHuXB(=_NsI(g8 zI+W;+-XR;aZb+BN*_j**(FV87h8eEk^*)7S&>Lf9h$q8Ktt43G2J5A8r_r1f!cW}> zku-=Tthw9EYzV&DUCVy_(6Q|(>L#zlZk%Qfks6dJkkvu-8bzOL3N(Q>-Aca=HS1zUTEAD& z&e?$)MqTX$=5+kyE8kq$uZIF|sLz~0L`jN3QS7%v3`#}_$Jr&-{Mo*%U>OAZPt+%x z-@O8s__2oPvS}`R12y96!Ju|s?-Se*#2Hw=aoKNG0aMfSga*kB^=YkS>#AOoi!K4f z`;z62fdO~iea!{1WM5{to8P|wXuG{8D9ZAcC8j!SW^R&MzZC=KzS|~|BClVtqJdY~ zcTwlF0~S5#liH1Eno4_!Ru;DA*||;LP&t}q>15axDY-`#U+SzZg$Jv+x?5B=BfiXN z_QEf}q1SkOs8&!wNN$78y1t&gXgbK zIt_(s{!nHb4keLogDMCN@zF8KQ|IHsBz2$B0#~0odlccJjGx^51ZZ0?PfULf!c|Pc zwRw|J8padtsg)Na)75q+8Py4>6uh$IRnXhl-qRnryf8Tf&&BW99CyDQzsk^bmpD*= z4+YjI&xOHZ0~0eQf}$z?7&Tru?4!fa`Fav9oplqIpy=JA#!`5&Gr@y6D$G^1^BlNO z7)mDvw{KZoMkjE5Ddk!^*`{d{t=zJH+2Yp0=h8OOeX zBe8CQ*qTvN$*7GKV`aw%q4o=tyP`x% zFt-#LVd|>9o(HV7-r-`7;l2X2!J);m$*3k)P1L9<Ck50xSPHOOt&-`iV}t|KxnWkIXuf zFE(Ums~#Qf6(U-=Q@#7=l8c$@M}5|xp5JNq!haml&`&jtLjng zP;gA_m5jGx3v6Zm<^>`>pm-rf8bKv699EF7KNPMCMwz#m#B7hgsx+w4@}87(dL5yJ zUPTX}&@mVJZ6@!PrTI#AM|eGGe7D@I3H@+r*p&1x7FqfF@~;abLkxefFwB@=aERbM z?Yt1OHobH5)r20{1*BULf4sptgEKnxL@O2J0w^SL+qf~PSR z66Plz6a-Ea?XpDtFbYh(ig!zvgPr%=#sxn+VDTZoyr?ab|9|8{{sQ6)bM$`&h@fg(MW+%-d{y$Gwi+r@KzFCUxD?Fkas>vuzES_#WMgf^7ywb(mxYv^AI0}Jfb|{YUS};u(xf}8=yHP zmhSq4#{c2rQ2n&oI3FO>JW$*x%|Z@n%UFt%JSGQ_lEFcOy%c^9S+agJK#B_Z9NUP` zU)^Vr<(*2epH9aH3r*ohxv}> z?5ugQOw*}#wi^A%Z8eTc;V0(!Fw!$*@555@yo6~LpS^3J(k$Yjz?7FM2|2D^j1=h? z-;o1eK8vO88!3D<@t{W&xO}#nHgToF%gTccWNJ}6m1rg$wKpGUl(fncv5`9fKcy7~ zm8wjZ>J|XqIWEn5Ql2q147cPR%y9^l2K=BI>uj;3{%ZNaV4no5F|`DO)3K>&!qJQl zl5K&u@52L+dJuuks}SBrhJ%>da^(At)#ul-2VZXoUh*osYK_JdKPXd`)~6Vb4>;lw zT{8KbsJrC>gD4*8*5_@nFA>lC2K3?ehJ0%fVOcfO^yeQH(&)bPmde0bb2)_Pzizzq zL3?Ftc2+ZLiHkW|faMiiyXT0UShp0#Tt}V4OWx(xg)(pP`=3(%5BWf~W`x@>TRaJ$ zZzaooB)`!ej`QTM2eJ~9#CZqPk{HL~#}jwsN*ANHe6ExYhBcF-g9-SRp1f}z&?&x3 z7ZFPGeOm*W4MHzN0exZIgs^&L+&~!~v)s9-SF5?dB0Y=Q`}|UL0AO2@F?u+Lk6oGY zOzQJ((G{YN3DbhSh;$r!heM%Kz!cZ5aGozJOA14|=CYZlz-cd` zLcS-rUIE@r3c4B84)|@DTF2UtIL>;W)2o}LZQ)7xl&+E|l zdFuVif|g<>dOQ0H`#F7eGB9MmikKL(Dwgx?ed&^QSJuZ6`Kr)s{nJPIP>>fser-ij zB|ocSawp)24BWis*2Z|>?D?eEtxC;SMBx(@Ijor38zw59h>xjR60eZ=YNPhG2NpOP;VjAjuD$IA&>4aMU&8Z@sJx Avj6}9