From b661e7d40030fb3d7889dc6e7cbad4a8e70845aa Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 10 Jun 2003 00:24:28 +0000 Subject: [PATCH] add soft hyphens --- panda/src/text/config_text.cxx | 21 +++- panda/src/text/config_text.h | 3 + panda/src/text/textFont.cxx | 216 ++++++++++++++------------------- 3 files changed, 115 insertions(+), 125 deletions(-) diff --git a/panda/src/text/config_text.cxx b/panda/src/text/config_text.cxx index f231c26850..4d1daf4edb 100644 --- a/panda/src/text/config_text.cxx +++ b/panda/src/text/config_text.cxx @@ -26,6 +26,7 @@ #include "unicodeLatinMap.h" #include "dconfig.h" +#include "config_express.h" Configure(config_text); NotifyCategoryDef(text, ""); @@ -48,6 +49,18 @@ const bool text_small_caps = config_text.GetBool("text-small-caps", false); const float text_small_caps_scale = config_text.GetFloat("text-small-caps-scale", 0.8f); const string text_default_font = config_text.GetString("text-default-font", ""); +// This is the decimal character number that, embedded in a string, is +// identified as the soft-hyphen character. +const int text_soft_hyphen_key = config_text.GetInt("text-soft-hyphen-key", 3); + +// This is the string that is output, encoded in the default encoding, +// to represent the soft-hyphen character. +wstring *text_soft_hyphen_output; + +// If the rightmost whitespace character falls before this fraction of +// the line, hyphenate a word to the right of that if possible. +const float text_hyphen_ratio = config_text.GetFloat("text-hyphen-ratio", 0.7); + Texture::FilterType text_minfilter = Texture::FT_invalid; Texture::FilterType text_magfilter = Texture::FT_invalid; @@ -103,5 +116,11 @@ init_libtext() { << "Invalid text-magfilter: " << text_magfilter_str << "\n"; text_magfilter = Texture::FT_linear; } - + + // Make sure libexpress is initialized before we ask something of + // TextEncoder. + init_libexpress(); + string encoded = config_text.GetString("text-soft-hyphen-output", "-"); + TextEncoder encoder; + text_soft_hyphen_output = new wstring(encoder.decode_text(encoded)); } diff --git a/panda/src/text/config_text.h b/panda/src/text/config_text.h index 4e0e604496..ccd43fe102 100644 --- a/panda/src/text/config_text.h +++ b/panda/src/text/config_text.h @@ -40,6 +40,9 @@ extern const float text_scale_factor; extern const bool text_small_caps; extern const float text_small_caps_scale; extern const string text_default_font; +extern const int text_soft_hyphen_key; +extern wstring *text_soft_hyphen_output; +extern const float text_hyphen_ratio; extern Texture::FilterType text_minfilter; extern Texture::FilterType text_magfilter; diff --git a/panda/src/text/textFont.cxx b/panda/src/text/textFont.cxx index 7f20eb28d3..ca9c3852a0 100644 --- a/panda/src/text/textFont.cxx +++ b/panda/src/text/textFont.cxx @@ -22,15 +22,15 @@ TypeHandle TextFont::_type_handle; - //////////////////////////////////////////////////////////////////// -// Function: isblank +// Function: isbreakpoint // Description: An internal function, similar to isspace(), except it -// does not consider newlines to be whitespace. +// does not consider newlines to be whitespace. It also +// includes the soft-hyphen character. //////////////////////////////////////////////////////////////////// -INLINE bool -isblank(unsigned int ch) { - return (ch == ' ' || ch == '\t'); +static INLINE bool +isbreakpoint(unsigned int ch) { + return (ch == ' ' || ch == '\t' || ch == (unsigned int)text_soft_hyphen_key); } //////////////////////////////////////////////////////////////////// @@ -38,7 +38,7 @@ isblank(unsigned int ch) { // Description: An internal function that works like isspace() but is // safe to call for a wide character. //////////////////////////////////////////////////////////////////// -INLINE bool +static INLINE bool isspacew(unsigned int ch) { return isascii(ch) && isspace(ch); } @@ -118,114 +118,25 @@ calc_width(const string &line) { string TextFont:: wordwrap_to(const string &text, float wordwrap_width, bool preserve_trailing_whitespace) { - string output_text; - - size_t p = 0; - - // Preserve any initial whitespace and newlines. - float initial_width = 0.0f; - while (p < text.length() && isspace((unsigned int)text[p])) { // dbg runtime will bomb if text[p]>=128 without (unsigned int) cast - if (text[p] == '\n') { - initial_width = 0.0f; - } else { - initial_width += calc_width(text[p]); - } - output_text += text[p]; - p++; - } - bool needs_newline = false; - - while (p < text.length()) { - nassertr(!isspace((unsigned int)text[p]), string()); - - // Scan the next n characters, until the end of the string or an - // embedded newline character, or we exceed wordwrap_width. - - size_t q = p; - bool any_spaces = false; - bool overflow = false; - - float width = initial_width; - while (q < text.length() && text[q] != '\n') { - if (isspace((unsigned int)text[q])) { - any_spaces = true; - } - - width += calc_width(text[q]); - q++; - - if (width > wordwrap_width) { - // Oops, too many. - q--; - overflow = true; - break; - } - } - - if (overflow && any_spaces) { - // If we stopped because we exceeded the wordwrap width, then - // back up to the end of the last complete word. - while (q > p && !isspace((unsigned int)text[q])) { - q--; - } - } - - // Skip additional whitespace between the lines. - size_t next_start = q; - while (next_start < text.length() && isblank(text[next_start])) { - next_start++; - } - - // Trim off any more blanks on the end. - while (q > p && isspace(text[q - 1])) { - q--; - } - - if (next_start == p) { - // No characters got in at all. This could only happen if the - // wordwrap width is narrower than a single character, or if we - // have a substantial number of leading spaces in a line. - q++; - next_start++; - while (next_start < text.length() && isblank(text[next_start])) { - next_start++; - } - } - - if (needs_newline) { - output_text += '\n'; - } - needs_newline = true; - - if (preserve_trailing_whitespace) { - q = next_start; - } - output_text += text.substr(p, q - p); - - // Now prepare to wrap the next line. - - if (next_start < text.length() && text[next_start] == '\n') { - // Preserve a single embedded newline. - output_text += '\n'; - next_start++; - needs_newline = false; - } - p = next_start; - - // Preserve any initial whitespace and newlines. - initial_width = 0.0f; - while (p < text.length() && isspace((unsigned int)text[p])) { - if (text[p] == '\n') { - initial_width = 0.0f; - } else { - initial_width += calc_width(text[p]); - } - output_text += text[p]; - p++; - } + // Elevate the string to a wstring for this operation. + wstring wtext; + wtext.reserve(text.size()); + for (string::const_iterator ti = text.begin(); ti != text.end(); ++ti) { + wtext += (unsigned int)(*ti); } - return output_text; + wtext = wordwrap_to(wtext, wordwrap_width, preserve_trailing_whitespace); + + // Back down from wstring to string. + string result; + result.reserve(wtext.size()); + for (wstring::const_iterator wi = wtext.begin(); + wi != wtext.end(); + ++wi) { + result += (char)(*wi); + } + + return result; } //////////////////////////////////////////////////////////////////// @@ -295,17 +206,53 @@ wordwrap_to(const wstring &text, float wordwrap_width, size_t q = p; bool any_spaces = false; + size_t last_space = 0; + float last_space_width = 0.0f; + + bool any_hyphens = false; + size_t last_hyphen = 0; + bool output_hyphen = false; + bool overflow = false; + bool last_was_space = false; + float hyphen_width = calc_width(*text_soft_hyphen_output); float width = initial_width; while (q < text.length() && text[q] != '\n') { if (isspacew(text[q])) { - any_spaces = true; + if (!last_was_space) { + any_spaces = true; + // We only care about logging whether there is a soft-hyphen + // character to the right of the rightmost space. Each time + // we encounter a space, we reset this counter. + any_hyphens = false; + last_space = q; + last_space_width = width; + last_was_space = true; + } + } else { + last_was_space = false; } - width += calc_width(text[q]); - q++; + // A soft hyphen character is not printed, but marks a point + // that we might hyphenate a word if we need to. + if (text[q] == text_soft_hyphen_key) { + // We only consider this as a possible hyphenation point if + // (a) it is not the very first character, and (b) there is + // enough room for a hyphen character to be printed following + // it. + if (q != p && width + hyphen_width <= wordwrap_width) { + any_hyphens = true; + last_hyphen = q; + } + } else { + // Some normal, printable character. + width += calc_width(text[q]); + } + + q++; + if (width > wordwrap_width) { // Oops, too many. q--; @@ -313,18 +260,31 @@ wordwrap_to(const wstring &text, float wordwrap_width, break; } } - - if (overflow && any_spaces) { + + if (overflow) { // If we stopped because we exceeded the wordwrap width, then - // back up to the end of the last complete word. - while (q > p && !isspacew(text[q])) { - q--; + // try to find an appropriate place to wrap the line or to + // hyphenate, if necessary. + if (any_spaces && last_space_width / wordwrap_width >= text_hyphen_ratio) { + // If we have a space that ended up within our safety margin, + // don't use any soft-hyphen characters. + any_hyphens = false; + } + + if (any_hyphens) { + // If we still have a soft-hyphen character, use it. + q = last_hyphen; + output_hyphen = true; + + } else if (any_spaces) { + // Otherwise, break at a space if we can. + q = last_space; } } // Skip additional whitespace between the lines. size_t next_start = q; - while (next_start < text.length() && isblank(text[next_start])) { + while (next_start < text.length() && isbreakpoint(text[next_start])) { next_start++; } @@ -339,7 +299,7 @@ wordwrap_to(const wstring &text, float wordwrap_width, // have a substantial number of leading spaces in a line. q++; next_start++; - while (next_start < text.length() && isblank(text[next_start])) { + while (next_start < text.length() && isbreakpoint(text[next_start])) { next_start++; } } @@ -352,7 +312,15 @@ wordwrap_to(const wstring &text, float wordwrap_width, if (preserve_trailing_whitespace) { q = next_start; } - output_text += text.substr(p, q - p); + + for (size_t pi = p; pi < q; pi++) { + if (text[pi] != text_soft_hyphen_key) { + output_text += text[pi]; + } + } + if (output_hyphen) { + output_text += *text_soft_hyphen_output; + } // Now prepare to wrap the next line.