Skip to content

Commit 40a1fdc

Browse files
kdickersonruevs
authored andcommitted
Implement basic kerning when rendering text.
Only old-style `kern` tables are supported--modern GPOS-based kerning is not supported.
1 parent cc64fed commit 40a1fdc

File tree

9 files changed

+384
-13
lines changed

9 files changed

+384
-13
lines changed

src/describescreen.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ void TextWindow::ScreenEditTtfText(int link, uint32_t v) {
1919
SS.TW.edit.request = hr;
2020
}
2121

22+
void TextWindow::ScreenToggleTtfKerning(int link, uint32_t v) {
23+
hRequest hr = { v };
24+
Request *r = SK.GetRequest(hr);
25+
26+
SS.UndoRemember();
27+
r->extraPoints = !r->extraPoints;
28+
29+
SS.MarkGroupDirty(r->group);
30+
SS.ScheduleShowTW();
31+
}
32+
2233
void TextWindow::ScreenSetTtfFont(int link, uint32_t v) {
2334
int i = (int)v;
2435
if(i < 0) return;
@@ -205,8 +216,11 @@ void TextWindow::DescribeSelection() {
205216
Printf(false, "%FtTRUETYPE FONT TEXT%E");
206217
Printf(true, " font = '%Fi%s%E'", e->font.c_str());
207218
if(e->h.isFromRequest()) {
208-
Printf(false, " text = '%Fi%s%E' %Fl%Ll%f%D[change]%E",
219+
Printf(true, " text = '%Fi%s%E' %Fl%Ll%f%D[change]%E",
209220
e->str.c_str(), &ScreenEditTtfText, e->h.request().v);
221+
Printf(true, " %Fd%f%D%Ll%s apply kerning",
222+
&ScreenToggleTtfKerning, e->h.request().v,
223+
e->extraPoints ? CHECK_TRUE : CHECK_FALSE);
210224
Printf(true, " select new font");
211225
SS.fonts.LoadAll();
212226
// Not using range-for here because we use i inside the output.

src/drawentity.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,8 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) const {
475475
Vector v = topLeft.Minus(botLeft);
476476
Vector u = (v.Cross(n)).WithMagnitude(v.Magnitude());
477477

478-
SS.fonts.PlotString(font, str, sbl, botLeft, u, v);
478+
// `extraPoints` is storing kerning boolean
479+
SS.fonts.PlotString(font, str, sbl, extraPoints, botLeft, u, v);
479480
break;
480481
}
481482

src/request.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
9090
// Request-specific generation.
9191
switch(type) {
9292
case Type::TTF_TEXT: {
93-
double actualAspectRatio = SS.fonts.AspectRatio(font, str);
93+
// `extraPoints` is storing kerning boolean
94+
double actualAspectRatio = SS.fonts.AspectRatio(font, str, extraPoints);
9495
if(EXACT(actualAspectRatio != 0.0)) {
9596
// We could load the font, so use the actual value.
9697
aspectRatio = actualAspectRatio;

src/ttf.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,11 @@ TtfFont *TtfFontList::LoadFont(const std::string &font)
108108
}
109109

110110
void TtfFontList::PlotString(const std::string &font, const std::string &str,
111-
SBezierList *sbl, Vector origin, Vector u, Vector v)
111+
SBezierList *sbl, bool kerning, Vector origin, Vector u, Vector v)
112112
{
113113
TtfFont *tf = LoadFont(font);
114114
if(!str.empty() && tf != NULL) {
115-
tf->PlotString(str, sbl, origin, u, v);
115+
tf->PlotString(str, sbl, kerning, origin, u, v);
116116
} else {
117117
// No text or no font; so draw a big X for an error marker.
118118
SBezier sb;
@@ -123,11 +123,11 @@ void TtfFontList::PlotString(const std::string &font, const std::string &str,
123123
}
124124
}
125125

126-
double TtfFontList::AspectRatio(const std::string &font, const std::string &str)
126+
double TtfFontList::AspectRatio(const std::string &font, const std::string &str, bool kerning)
127127
{
128128
TtfFont *tf = LoadFont(font);
129129
if(tf != NULL) {
130-
return tf->AspectRatio(str);
130+
return tf->AspectRatio(str, kerning);
131131
}
132132

133133
return 0.0;
@@ -331,7 +331,7 @@ static int CubicTo(const FT_Vector *c1, const FT_Vector *c2, const FT_Vector *p,
331331
}
332332

333333
void TtfFont::PlotString(const std::string &str,
334-
SBezierList *sbl, Vector origin, Vector u, Vector v)
334+
SBezierList *sbl, bool kerning, Vector origin, Vector u, Vector v)
335335
{
336336
ssassert(fontFace != NULL, "Expected font face to be loaded");
337337

@@ -344,6 +344,7 @@ void TtfFont::PlotString(const std::string &str,
344344
outlineFuncs.delta = 0;
345345

346346
FT_Pos dx = 0;
347+
uint32_t prevGid = 0;
347348
for(char32_t cid : ReadUTF8(str)) {
348349
uint32_t gid = FT_Get_Char_Index(fontFace, cid);
349350
if (gid == 0) {
@@ -382,6 +383,13 @@ void TtfFont::PlotString(const std::string &str,
382383
*/
383384
FT_BBox cbox;
384385
FT_Outline_Get_CBox(&fontFace->glyph->outline, &cbox);
386+
387+
// Apply Kerning, if any:
388+
FT_Vector kernVector;
389+
if(kerning && FT_Get_Kerning(fontFace, prevGid, gid, FT_KERNING_DEFAULT, &kernVector) == 0) {
390+
dx += kernVector.x;
391+
}
392+
385393
FT_Pos bx = dx - cbox.xMin;
386394
// Yes, this is what FreeType calls left-side bearing.
387395
// Then interchangeably uses that with "left-side bearing". Sigh.
@@ -402,14 +410,16 @@ void TtfFont::PlotString(const std::string &str,
402410
// And we're done, so advance our position by the requested advance
403411
// width, plus the user-requested extra advance.
404412
dx += fontFace->glyph->advance.x;
413+
prevGid = gid;
405414
}
406415
}
407416

408-
double TtfFont::AspectRatio(const std::string &str) {
417+
double TtfFont::AspectRatio(const std::string &str, bool kerning) {
409418
ssassert(fontFace != NULL, "Expected font face to be loaded");
410419

411420
// We always request a unit size character, so the aspect ratio is the same as advance length.
412421
double dx = 0;
422+
uint32_t prevGid = 0;
413423
for(char32_t chr : ReadUTF8(str)) {
414424
uint32_t gid = FT_Get_Char_Index(fontFace, chr);
415425
if (gid == 0) {
@@ -424,7 +434,14 @@ double TtfFont::AspectRatio(const std::string &str) {
424434
break;
425435
}
426436

437+
// Apply Kerning, if any:
438+
FT_Vector kernVector;
439+
if(kerning && FT_Get_Kerning(fontFace, prevGid, gid, FT_KERNING_DEFAULT, &kernVector) == 0) {
440+
dx += (double)kernVector.x / capHeight;
441+
}
442+
427443
dx += (double)fontFace->glyph->advance.x / capHeight;
444+
prevGid = gid;
428445
}
429446

430447
return dx;

src/ttf.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ class TtfFont {
2424
bool LoadFromResource(FT_LibraryRec_ *fontLibrary, bool keepOpen = false);
2525

2626
void PlotString(const std::string &str,
27-
SBezierList *sbl, Vector origin, Vector u, Vector v);
28-
double AspectRatio(const std::string &str);
27+
SBezierList *sbl, bool kerning, Vector origin, Vector u, Vector v);
28+
double AspectRatio(const std::string &str, bool kerning);
2929

3030
bool ExtractTTFData(bool keepOpen);
3131
};
@@ -43,8 +43,8 @@ class TtfFontList {
4343
TtfFont *LoadFont(const std::string &font);
4444

4545
void PlotString(const std::string &font, const std::string &str,
46-
SBezierList *sbl, Vector origin, Vector u, Vector v);
47-
double AspectRatio(const std::string &font, const std::string &str);
46+
SBezierList *sbl, bool kerning, Vector origin, Vector u, Vector v);
47+
double AspectRatio(const std::string &font, const std::string &str, bool kerning);
4848
};
4949

5050
#endif

src/ui.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ class TextWindow {
402402
// All of these are callbacks from the GUI code; first from when
403403
// we're describing an entity
404404
static void ScreenEditTtfText(int link, uint32_t v);
405+
static void ScreenToggleTtfKerning(int link, uint32_t v);
405406
static void ScreenSetTtfFont(int link, uint32_t v);
406407
static void ScreenUnselectAll(int link, uint32_t v);
407408

test/request/ttf_text/kerning.png

5.98 KB
Loading

0 commit comments

Comments
 (0)