383 lines
11 KiB
C++
383 lines
11 KiB
C++
// Filename: textFont.cxx
|
|
// Created by: drose (08Feb02)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://www.panda3d.org/license.txt .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d@yahoogroups.com .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "textFont.h"
|
|
#include "config_text.h"
|
|
#include "ctype.h"
|
|
|
|
TypeHandle TextFont::_type_handle;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: isblank
|
|
// Description: An internal function, similar to isspace(), except it
|
|
// does not consider newlines to be whitespace.
|
|
////////////////////////////////////////////////////////////////////
|
|
INLINE bool
|
|
isblank(int ch) {
|
|
return (ch == ' ' || ch == '\t');
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: isspacew
|
|
// Description: An internal function that works like isspace() but is
|
|
// safe to call for a wide character.
|
|
////////////////////////////////////////////////////////////////////
|
|
INLINE bool
|
|
isspacew(int ch) {
|
|
return isascii(ch) && isspace(ch);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: TextFont::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
TextFont::
|
|
TextFont() {
|
|
_is_valid = false;
|
|
_line_height = 1.0f;
|
|
_space_advance = 0.25f;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: TextFont::Destructor
|
|
// Access: Published, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
TextFont::
|
|
~TextFont() {
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: TextFont::calc_width
|
|
// Access: Published
|
|
// Description: Returns the width of a single character of the font,
|
|
// or 0.0 if the character is not known.
|
|
////////////////////////////////////////////////////////////////////
|
|
float TextFont::
|
|
calc_width(int character) {
|
|
if (character == ' ') {
|
|
// A space is a special case.
|
|
return _space_advance;
|
|
}
|
|
|
|
const TextGlyph *glyph;
|
|
float glyph_scale;
|
|
get_glyph(character, glyph, glyph_scale);
|
|
if (glyph == (TextGlyph *)NULL) {
|
|
// Unknown character.
|
|
return 0.0f;
|
|
}
|
|
|
|
return glyph->get_advance() * glyph_scale;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: TextFont::calc_width
|
|
// Access: Published
|
|
// Description: Returns the width of a line of text of arbitrary
|
|
// characters. The line should not include the newline
|
|
// character.
|
|
////////////////////////////////////////////////////////////////////
|
|
float TextFont::
|
|
calc_width(const string &line) {
|
|
float width = 0.0f;
|
|
|
|
string::const_iterator si;
|
|
for (si = line.begin(); si != line.end(); ++si) {
|
|
width += calc_width(*si);
|
|
}
|
|
|
|
return width;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: TextFont::wordwrap_to
|
|
// Access: Published
|
|
// Description: Inserts newlines into the given text at the
|
|
// appropriate places in order to make each line be the
|
|
// longest possible line that is not longer than
|
|
// wordwrap_width (and does not break any words, if
|
|
// possible). Returns the new string.
|
|
////////////////////////////////////////////////////////////////////
|
|
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(text[p])) {
|
|
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(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(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(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(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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: TextFont::write
|
|
// Access: Published, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void TextFont::
|
|
write(ostream &out, int indent_level) const {
|
|
indent(out, indent_level)
|
|
<< "TextFont " << get_name() << "\n";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: TextFont::calc_width (wide char)
|
|
// Access: Public
|
|
// Description: Returns the width of a line of text of arbitrary
|
|
// characters. The line should not include the newline
|
|
// character.
|
|
////////////////////////////////////////////////////////////////////
|
|
float TextFont::
|
|
calc_width(const wstring &line) {
|
|
float width = 0.0f;
|
|
|
|
wstring::const_iterator si;
|
|
for (si = line.begin(); si != line.end(); ++si) {
|
|
width += calc_width(*si);
|
|
}
|
|
|
|
return width;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: TextFont::wordwrap_to (wide char)
|
|
// Access: Public
|
|
// Description: Inserts newlines into the given text at the
|
|
// appropriate places in order to make each line be the
|
|
// longest possible line that is not longer than
|
|
// wordwrap_width (and does not break any words, if
|
|
// possible). Returns the new string.
|
|
////////////////////////////////////////////////////////////////////
|
|
wstring TextFont::
|
|
wordwrap_to(const wstring &text, float wordwrap_width,
|
|
bool preserve_trailing_whitespace) {
|
|
wstring output_text;
|
|
|
|
size_t p = 0;
|
|
|
|
// Preserve any initial whitespace and newlines.
|
|
float initial_width = 0.0f;
|
|
while (p < text.length() && isspacew(text[p])) {
|
|
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(!isspacew(text[p]), wstring());
|
|
|
|
// 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 (isspacew(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 && !isspacew(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 && isspacew(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() && isspacew(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;
|
|
}
|