From 4d5073b15432d30f9bd9434984ee84ae08976efb Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 15 May 2005 16:13:52 +0000 Subject: [PATCH] Added Font::wordWrapText method svn-id: r18109 --- graphics/font.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++++++ graphics/font.h | 27 ++++++++++----- 2 files changed, 107 insertions(+), 8 deletions(-) diff --git a/graphics/font.cpp b/graphics/font.cpp index 533c564afdb..f00e550c4d2 100644 --- a/graphics/font.cpp +++ b/graphics/font.cpp @@ -156,4 +156,92 @@ void Font::drawString(Surface *dst, const Common::String &s, int x, int y, int w } +struct WordWrapper { + Common::StringList &lines; + int actualMaxLineWidth; + + WordWrapper(Common::StringList &l) : lines(l), actualMaxLineWidth(0) { + } + + void add(Common::String &line, int &w) { + if (actualMaxLineWidth < w) + actualMaxLineWidth = w; + + lines.push_back(line); + + line.clear(); + w = 0; + } +}; + +int Font::wordWrapText(const Common::String &str, int maxWidth, Common::StringList &lines) const { + WordWrapper wrapper(lines); + Common::String line; + Common::String tmpStr; + int lineWidth = 0; + int tmpWidth = 0; + + // The rough idea behind this algorithm is as follows: + // We accumulate characters into the string tmpStr. Whenever a full word + // has been gathered together this way, we 'commit' it to the line buffer + // 'line', i.e. we add tmpStr to the end of line, then clear it. Before + // we do that, we check whether it would cause 'line' to exceed maxWidth; + // in that case, we first add line to lines, then reset it. + // + // If a newline character is read, then we also add line to lines and clear it. + // + // Special care has to be taken to account for 'words' that exceed the width + // of a line. If we encounter such a word, we have to wrap it over multiple + // lines. + + for (Common::String::const_iterator x = str.begin(); x != str.end(); ++x) { + const char c = *x; + const int w = getCharWidth(c); + + // If this char is a whitespace, then it represents a potential + // 'wrap point' where wrapping could take place. Everything that + // came before it can now safely be added to the line, as we know + // that it will not have to be wrapped. + if (isspace(c)) { + line += tmpStr; + lineWidth += tmpWidth; + + tmpStr.clear(); + tmpWidth = 0; + } + + // If we encounter a line break (\n), the line is complete. + if (c == '\n') { + wrapper.add(line, lineWidth); + continue; + } + + // If the max line width would be exceeded by adding this char, + // insert a line break. + if (lineWidth + tmpWidth + w > maxWidth) { + // Commit what we have so far, *if* we have anything. + // If line is empty, then we are looking at a word + // which exceeds the maximum line width. + if (lineWidth > 0) { + wrapper.add(line, lineWidth); + } else { + wrapper.add(tmpStr, tmpWidth); + } + } + + + tmpWidth += w; + tmpStr += c; + } + + // If some text is left over, add it as the final line + line += tmpStr; + lineWidth += tmpWidth; + if (lineWidth > 0) { + wrapper.add(line, lineWidth); + } + return wrapper.actualMaxLineWidth; +} + + } // End of namespace Graphics diff --git a/graphics/font.h b/graphics/font.h index 8f6aa7249f4..230ae94c2b8 100644 --- a/graphics/font.h +++ b/graphics/font.h @@ -37,14 +37,6 @@ enum TextAlignment { * Instances of this class represent a distinct font, with a built-in renderer. * @todo Maybe move the high-level methods (drawString etc.) to a separate * FontRenderer class? That way, we could have different variants... ? - * @todo Add more parameters to drawString, or additional similar methods, - * featuring abilities like - * - rendering with wrap-around instead of inserting an ellipsis or - * cutting them; needs a 'height' parameter - * - rendering multi-line strings (essentially, invoke the regular - * drawString for each line, and advance one line) - * - combinations of the two above: honor line feeds, and also wrap - * overlong lines */ class Font { public: @@ -55,7 +47,26 @@ public: virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const = 0; void drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlignment align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const; + + /** + * Compute and return the width the string str has when rendered using this font. + */ int getStringWidth(const Common::String &str) const; + + /** + * Take a text (which may contain newlines characters) and word wrap it so thata + * no text line is wider than maxWidth pixels. If necessary, additional line breaks + * are generated, preferably between words (i.e. were whitespaces are). + * The resulting lines are appended to the string list lines. + * It returns the maximal width of any of the new lines (i.e. a value which is less + * or equal to maxWidth). + * + * @param str the string to word wrap + * @param maxWidth the maximum width a line may have + * @param lines the string list to which the text lines from str are appended + * @return the maximal width of any of the lines added to lines + */ + int wordWrapText(const Common::String &str, int maxWidth, Common::StringList &lines) const; };