diff --git a/panda/src/text/config_text.cxx b/panda/src/text/config_text.cxx index 4790c5ecf7..36ef375afe 100644 --- a/panda/src/text/config_text.cxx +++ b/panda/src/text/config_text.cxx @@ -48,6 +48,12 @@ ConfigVariableBool text_dynamic_merge "operation. Usually it's a performance " "advantage to keep this true. See TextNode::set_flatten_flags().")); +ConfigVariableBool text_kerning +("text-kerning", false, + PRC_DESC("Set this true to enable kerning when the font provides kerning " + "tables. This can result in more aesthetically pleasing spacing " + "between individual glyphs.")); + ConfigVariableInt text_anisotropic_degree ("text-anisotropic-degree", 1, PRC_DESC("This is the default anisotropic-degree that is set on dynamic " diff --git a/panda/src/text/config_text.h b/panda/src/text/config_text.h index 5c50af6a52..4e5ca1a6ed 100644 --- a/panda/src/text/config_text.h +++ b/panda/src/text/config_text.h @@ -30,6 +30,7 @@ NotifyCategoryDecl(text, EXPCL_PANDA_TEXT, EXPTP_PANDA_TEXT); extern ConfigVariableBool text_flatten; extern ConfigVariableBool text_dynamic_merge; +extern ConfigVariableBool text_kerning; extern ConfigVariableInt text_anisotropic_degree; extern ConfigVariableInt text_texture_margin; extern ConfigVariableDouble text_poly_margin; diff --git a/panda/src/text/dynamicTextFont.cxx b/panda/src/text/dynamicTextFont.cxx index 09369a12b9..f066e07c35 100644 --- a/panda/src/text/dynamicTextFont.cxx +++ b/panda/src/text/dynamicTextFont.cxx @@ -278,6 +278,32 @@ get_glyph(int character, CPT(TextGlyph) &glyph) { return (glyph_index != 0); } +/** + * Returns the amount by which to offset the second glyph when it directly + * follows the first glyph. This is an additional offset that is added on top + * of the advance. + */ +PN_stdfloat DynamicTextFont:: +get_kerning(int first, int second) const { + if (!_is_valid) { + return 0; + } + + FT_Face face = acquire_face(); + if (!FT_HAS_KERNING(face)) { + release_face(face); + return 0; + } + + int first_index = FT_Get_Char_Index(face, first); + int second_index = FT_Get_Char_Index(face, second); + + FT_Vector delta; + FT_Get_Kerning(face, first_index, second_index, FT_KERNING_DEFAULT, &delta); + release_face(face); + + return delta.x / (_font_pixels_per_unit * 64); +} /** * Called from both constructors to set up some initial values. diff --git a/panda/src/text/dynamicTextFont.h b/panda/src/text/dynamicTextFont.h index c418ea34c3..26433deef6 100644 --- a/panda/src/text/dynamicTextFont.h +++ b/panda/src/text/dynamicTextFont.h @@ -123,6 +123,7 @@ PUBLISHED: public: virtual bool get_glyph(int character, CPT(TextGlyph) &glyph); + virtual PN_stdfloat get_kerning(int first, int second) const; private: void initialize(); diff --git a/panda/src/text/textAssembler.cxx b/panda/src/text/textAssembler.cxx index 3c8550a6a5..1f2cfc67d7 100644 --- a/panda/src/text/textAssembler.cxx +++ b/panda/src/text/textAssembler.cxx @@ -609,6 +609,8 @@ assemble_text() { * Returns the width of a single character, according to its associated font. * This also correctly calculates the width of cheesy ligatures and accented * characters, which may not exist in the font as such. + * + * This does not take kerning into account, however. */ PN_stdfloat TextAssembler:: calc_width(wchar_t character, const TextProperties &properties) { @@ -1399,6 +1401,9 @@ assemble_row(TextAssembler::TextRow &row, PN_stdfloat xpos = 0.0f; align = TextProperties::A_left; + // Remember previous character, for kerning. + int prev_char = -1; + bool underscore = false; PN_stdfloat underscore_start = 0.0f; const TextProperties *underscore_properties = NULL; @@ -1450,11 +1455,13 @@ assemble_row(TextAssembler::TextRow &row, if (character == ' ') { // A space is a special case. xpos += properties->get_glyph_scale() * properties->get_text_scale() * font->get_space_advance(); + prev_char = -1; } else if (character == '\t') { // So is a tab character. PN_stdfloat tab_width = properties->get_tab_width(); xpos = (floor(xpos / tab_width) + 1.0f) * tab_width; + prev_char = -1; } else if (character == text_soft_hyphen_key) { // And so is the 'soft-hyphen' key character. @@ -1493,6 +1500,7 @@ assemble_row(TextAssembler::TextRow &row, placed_glyphs.push_back(placement); xpos += advance * glyph_scale; + prev_char = -1; } else { // A printable character. @@ -1521,6 +1529,14 @@ assemble_row(TextAssembler::TextRow &row, << "\n"; } + // Add the kerning delta. + if (text_kerning) { + if (prev_char != -1) { + xpos += font->get_kerning(prev_char, character); + } + prev_char = character; + } + // Build up a GlyphPlacement, indicating all of the Geoms that go into // this character. Normally, there is only one Geom per character, but // it may involve multiple Geoms if we need to add cheesy accents or diff --git a/panda/src/text/textFont.cxx b/panda/src/text/textFont.cxx index 70ca9a74d5..add86483bd 100644 --- a/panda/src/text/textFont.cxx +++ b/panda/src/text/textFont.cxx @@ -54,6 +54,16 @@ TextFont:: ~TextFont() { } +/** + * Returns the amount by which to offset the second glyph when it directly + * follows the first glyph. This is an additional offset that is added on top + * of the advance. + */ +PN_stdfloat TextFont:: +get_kerning(int first, int second) const { + return 0; +} + /** * */ diff --git a/panda/src/text/textFont.h b/panda/src/text/textFont.h index 1249e7b817..7cea63e391 100644 --- a/panda/src/text/textFont.h +++ b/panda/src/text/textFont.h @@ -74,6 +74,8 @@ PUBLISHED: INLINE CPT(TextGlyph) get_glyph(int character); + virtual PN_stdfloat get_kerning(int first, int second) const; + virtual void write(ostream &out, int indent_level) const; public: