diff --git a/src/describescreen.cpp b/src/describescreen.cpp index dd541ee..0659034 100644 --- a/src/describescreen.cpp +++ b/src/describescreen.cpp @@ -19,6 +19,17 @@ void TextWindow::ScreenEditTtfText(int link, uint32_t v) { SS.TW.edit.request = hr; } +void TextWindow::ScreenToggleTtfKerning(int link, uint32_t v) { + hRequest hr = { v }; + Request *r = SK.GetRequest(hr); + + SS.UndoRemember(); + r->extraPoints = !r->extraPoints; + + SS.MarkGroupDirty(r->group); + SS.ScheduleShowTW(); +} + void TextWindow::ScreenSetTtfFont(int link, uint32_t v) { int i = (int)v; if(i < 0) return; @@ -205,8 +216,11 @@ void TextWindow::DescribeSelection() { Printf(false, "%FtTRUETYPE FONT TEXT%E"); Printf(true, " font = '%Fi%s%E'", e->font.c_str()); if(e->h.isFromRequest()) { - Printf(false, " text = '%Fi%s%E' %Fl%Ll%f%D[change]%E", + Printf(true, " text = '%Fi%s%E' %Fl%Ll%f%D[change]%E", e->str.c_str(), &ScreenEditTtfText, e->h.request().v); + Printf(true, " %Fd%f%D%Ll%s apply kerning", + &ScreenToggleTtfKerning, e->h.request().v, + e->extraPoints ? CHECK_TRUE : CHECK_FALSE); Printf(true, " select new font"); SS.fonts.LoadAll(); // Not using range-for here because we use i inside the output. diff --git a/src/drawentity.cpp b/src/drawentity.cpp index 2dbbe54..7ac0338 100644 --- a/src/drawentity.cpp +++ b/src/drawentity.cpp @@ -475,7 +475,8 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) const { Vector v = topLeft.Minus(botLeft); Vector u = (v.Cross(n)).WithMagnitude(v.Magnitude()); - SS.fonts.PlotString(font, str, sbl, botLeft, u, v); + // `extraPoints` is storing kerning boolean + SS.fonts.PlotString(font, str, sbl, extraPoints, botLeft, u, v); break; } diff --git a/src/request.cpp b/src/request.cpp index 3614cec..7613a03 100644 --- a/src/request.cpp +++ b/src/request.cpp @@ -90,7 +90,8 @@ void Request::Generate(IdList *entity, // Request-specific generation. switch(type) { case Type::TTF_TEXT: { - double actualAspectRatio = SS.fonts.AspectRatio(font, str); + // `extraPoints` is storing kerning boolean + double actualAspectRatio = SS.fonts.AspectRatio(font, str, extraPoints); if(EXACT(actualAspectRatio != 0.0)) { // We could load the font, so use the actual value. aspectRatio = actualAspectRatio; diff --git a/src/ttf.cpp b/src/ttf.cpp index e39afc7..3d7cb63 100644 --- a/src/ttf.cpp +++ b/src/ttf.cpp @@ -108,11 +108,11 @@ TtfFont *TtfFontList::LoadFont(const std::string &font) } void TtfFontList::PlotString(const std::string &font, const std::string &str, - SBezierList *sbl, Vector origin, Vector u, Vector v) + SBezierList *sbl, bool kerning, Vector origin, Vector u, Vector v) { TtfFont *tf = LoadFont(font); if(!str.empty() && tf != NULL) { - tf->PlotString(str, sbl, origin, u, v); + tf->PlotString(str, sbl, kerning, origin, u, v); } else { // No text or no font; so draw a big X for an error marker. SBezier sb; @@ -123,11 +123,11 @@ void TtfFontList::PlotString(const std::string &font, const std::string &str, } } -double TtfFontList::AspectRatio(const std::string &font, const std::string &str) +double TtfFontList::AspectRatio(const std::string &font, const std::string &str, bool kerning) { TtfFont *tf = LoadFont(font); if(tf != NULL) { - return tf->AspectRatio(str); + return tf->AspectRatio(str, kerning); } return 0.0; @@ -331,7 +331,7 @@ static int CubicTo(const FT_Vector *c1, const FT_Vector *c2, const FT_Vector *p, } void TtfFont::PlotString(const std::string &str, - SBezierList *sbl, Vector origin, Vector u, Vector v) + SBezierList *sbl, bool kerning, Vector origin, Vector u, Vector v) { ssassert(fontFace != NULL, "Expected font face to be loaded"); @@ -344,6 +344,7 @@ void TtfFont::PlotString(const std::string &str, outlineFuncs.delta = 0; FT_Pos dx = 0; + uint32_t prevGid = 0; for(char32_t cid : ReadUTF8(str)) { uint32_t gid = FT_Get_Char_Index(fontFace, cid); if (gid == 0) { @@ -382,6 +383,13 @@ void TtfFont::PlotString(const std::string &str, */ FT_BBox cbox; FT_Outline_Get_CBox(&fontFace->glyph->outline, &cbox); + + // Apply Kerning, if any: + FT_Vector kernVector; + if(kerning && FT_Get_Kerning(fontFace, prevGid, gid, FT_KERNING_DEFAULT, &kernVector) == 0) { + dx += kernVector.x; + } + FT_Pos bx = dx - cbox.xMin; // Yes, this is what FreeType calls left-side bearing. // Then interchangeably uses that with "left-side bearing". Sigh. @@ -402,14 +410,16 @@ void TtfFont::PlotString(const std::string &str, // And we're done, so advance our position by the requested advance // width, plus the user-requested extra advance. dx += fontFace->glyph->advance.x; + prevGid = gid; } } -double TtfFont::AspectRatio(const std::string &str) { +double TtfFont::AspectRatio(const std::string &str, bool kerning) { ssassert(fontFace != NULL, "Expected font face to be loaded"); // We always request a unit size character, so the aspect ratio is the same as advance length. double dx = 0; + uint32_t prevGid = 0; for(char32_t chr : ReadUTF8(str)) { uint32_t gid = FT_Get_Char_Index(fontFace, chr); if (gid == 0) { @@ -424,7 +434,14 @@ double TtfFont::AspectRatio(const std::string &str) { break; } + // Apply Kerning, if any: + FT_Vector kernVector; + if(kerning && FT_Get_Kerning(fontFace, prevGid, gid, FT_KERNING_DEFAULT, &kernVector) == 0) { + dx += (double)kernVector.x / capHeight; + } + dx += (double)fontFace->glyph->advance.x / capHeight; + prevGid = gid; } return dx; diff --git a/src/ttf.h b/src/ttf.h index 565e6a9..1dc7205 100644 --- a/src/ttf.h +++ b/src/ttf.h @@ -24,8 +24,8 @@ public: bool LoadFromResource(FT_LibraryRec_ *fontLibrary, bool keepOpen = false); void PlotString(const std::string &str, - SBezierList *sbl, Vector origin, Vector u, Vector v); - double AspectRatio(const std::string &str); + SBezierList *sbl, bool kerning, Vector origin, Vector u, Vector v); + double AspectRatio(const std::string &str, bool kerning); bool ExtractTTFData(bool keepOpen); }; @@ -43,8 +43,8 @@ public: TtfFont *LoadFont(const std::string &font); void PlotString(const std::string &font, const std::string &str, - SBezierList *sbl, Vector origin, Vector u, Vector v); - double AspectRatio(const std::string &font, const std::string &str); + SBezierList *sbl, bool kerning, Vector origin, Vector u, Vector v); + double AspectRatio(const std::string &font, const std::string &str, bool kerning); }; #endif diff --git a/src/ui.h b/src/ui.h index 5e40194..49c1be6 100644 --- a/src/ui.h +++ b/src/ui.h @@ -402,6 +402,7 @@ public: // All of these are callbacks from the GUI code; first from when // we're describing an entity static void ScreenEditTtfText(int link, uint32_t v); + static void ScreenToggleTtfKerning(int link, uint32_t v); static void ScreenSetTtfFont(int link, uint32_t v); static void ScreenUnselectAll(int link, uint32_t v); diff --git a/test/request/ttf_text/kerning.png b/test/request/ttf_text/kerning.png new file mode 100644 index 0000000..4d6eab0 Binary files /dev/null and b/test/request/ttf_text/kerning.png differ diff --git a/test/request/ttf_text/kerning.slvs b/test/request/ttf_text/kerning.slvs new file mode 100644 index 0000000..c0f8a7f Binary files /dev/null and b/test/request/ttf_text/kerning.slvs differ diff --git a/test/request/ttf_text/test.cpp b/test/request/ttf_text/test.cpp index 5e9db1b..2937f79 100644 --- a/test/request/ttf_text/test.cpp +++ b/test/request/ttf_text/test.cpp @@ -6,6 +6,12 @@ TEST_CASE(normal_roundtrip) { CHECK_SAVE("normal.slvs"); } +TEST_CASE(kerning_roundtrip) { + CHECK_LOAD("kerning.slvs"); + CHECK_RENDER("kerning.png"); + CHECK_SAVE("kerning.slvs"); +} + TEST_CASE(normal_migrate_from_v20) { CHECK_LOAD("normal_v20.slvs"); CHECK_SAVE("normal.slvs");