add soft hyphens

This commit is contained in:
David Rose 2003-06-10 00:24:28 +00:00
parent 5ad7edb42b
commit b661e7d400
3 changed files with 115 additions and 125 deletions

View File

@ -26,6 +26,7 @@
#include "unicodeLatinMap.h" #include "unicodeLatinMap.h"
#include "dconfig.h" #include "dconfig.h"
#include "config_express.h"
Configure(config_text); Configure(config_text);
NotifyCategoryDef(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 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", ""); 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_minfilter = Texture::FT_invalid;
Texture::FilterType text_magfilter = Texture::FT_invalid; Texture::FilterType text_magfilter = Texture::FT_invalid;
@ -104,4 +117,10 @@ init_libtext() {
text_magfilter = Texture::FT_linear; 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));
} }

View File

@ -40,6 +40,9 @@ extern const float text_scale_factor;
extern const bool text_small_caps; extern const bool text_small_caps;
extern const float text_small_caps_scale; extern const float text_small_caps_scale;
extern const string text_default_font; 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_minfilter;
extern Texture::FilterType text_magfilter; extern Texture::FilterType text_magfilter;

View File

@ -22,15 +22,15 @@
TypeHandle TextFont::_type_handle; TypeHandle TextFont::_type_handle;
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: isblank // Function: isbreakpoint
// Description: An internal function, similar to isspace(), except it // 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 static INLINE bool
isblank(unsigned int ch) { isbreakpoint(unsigned int ch) {
return (ch == ' ' || ch == '\t'); 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 // Description: An internal function that works like isspace() but is
// safe to call for a wide character. // safe to call for a wide character.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE bool static INLINE bool
isspacew(unsigned int ch) { isspacew(unsigned int ch) {
return isascii(ch) && isspace(ch); return isascii(ch) && isspace(ch);
} }
@ -118,114 +118,25 @@ calc_width(const string &line) {
string TextFont:: string TextFont::
wordwrap_to(const string &text, float wordwrap_width, wordwrap_to(const string &text, float wordwrap_width,
bool preserve_trailing_whitespace) { bool preserve_trailing_whitespace) {
string output_text; // Elevate the string to a wstring for this operation.
wstring wtext;
size_t p = 0; wtext.reserve(text.size());
for (string::const_iterator ti = text.begin(); ti != text.end(); ++ti) {
// Preserve any initial whitespace and newlines. wtext += (unsigned int)(*ti);
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]); wtext = wordwrap_to(wtext, wordwrap_width, preserve_trailing_whitespace);
q++;
if (width > wordwrap_width) { // Back down from wstring to string.
// Oops, too many. string result;
q--; result.reserve(wtext.size());
overflow = true; for (wstring::const_iterator wi = wtext.begin();
break; wi != wtext.end();
} ++wi) {
result += (char)(*wi);
} }
if (overflow && any_spaces) { return result;
// 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++;
}
}
return output_text;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -295,15 +206,51 @@ wordwrap_to(const wstring &text, float wordwrap_width,
size_t q = p; size_t q = p;
bool any_spaces = false; 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 overflow = false;
bool last_was_space = false;
float hyphen_width = calc_width(*text_soft_hyphen_output);
float width = initial_width; float width = initial_width;
while (q < text.length() && text[q] != '\n') { while (q < text.length() && text[q] != '\n') {
if (isspacew(text[q])) { if (isspacew(text[q])) {
if (!last_was_space) {
any_spaces = true; 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;
} }
// 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]); width += calc_width(text[q]);
}
q++; q++;
if (width > wordwrap_width) { if (width > wordwrap_width) {
@ -314,17 +261,30 @@ wordwrap_to(const wstring &text, float wordwrap_width,
} }
} }
if (overflow && any_spaces) { if (overflow) {
// If we stopped because we exceeded the wordwrap width, then // If we stopped because we exceeded the wordwrap width, then
// back up to the end of the last complete word. // try to find an appropriate place to wrap the line or to
while (q > p && !isspacew(text[q])) { // hyphenate, if necessary.
q--; 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. // Skip additional whitespace between the lines.
size_t next_start = q; 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++; next_start++;
} }
@ -339,7 +299,7 @@ wordwrap_to(const wstring &text, float wordwrap_width,
// have a substantial number of leading spaces in a line. // have a substantial number of leading spaces in a line.
q++; q++;
next_start++; next_start++;
while (next_start < text.length() && isblank(text[next_start])) { while (next_start < text.length() && isbreakpoint(text[next_start])) {
next_start++; next_start++;
} }
} }
@ -352,7 +312,15 @@ wordwrap_to(const wstring &text, float wordwrap_width,
if (preserve_trailing_whitespace) { if (preserve_trailing_whitespace) {
q = next_start; 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. // Now prepare to wrap the next line.