From 03803f19b5ded40a476181189027d3b5fc706ba1 Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Tue, 12 Feb 2013 11:22:19 +0400 Subject: [PATCH] BookTextParser: moved to Ogre::UTFString Font height and unicode characters glyph width now accounted correctly. --- apps/openmw/mwgui/formatting.cpp | 168 +++++++++++++++++-------------- apps/openmw/mwgui/formatting.hpp | 2 + 2 files changed, 96 insertions(+), 74 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 1689d2fb1..c50381a87 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -6,7 +6,9 @@ #include "../mwworld/ptr.hpp" #include +#include #include +#include using namespace MWGui; @@ -67,118 +69,134 @@ namespace return value; } + + Ogre::UTFString::unicode_char unicodeCharFromChar(char ch) + { + std::string s; + s += ch; + Ogre::UTFString string(s); + return string.getChar(0); + } } -std::vector BookTextParser::split(std::string text, const int width, const int height) +std::vector BookTextParser::split(std::string utf8Text, const int width, const int height) { + using Ogre::UTFString; std::vector result; MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesBook(text, interpreterContext); + utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); - boost::algorithm::replace_all(text, "
", "\n"); - boost::algorithm::replace_all(text, "

", "\n\n"); + boost::algorithm::replace_all(utf8Text, "
", "\n"); + boost::algorithm::replace_all(utf8Text, "

", "\n\n"); + UTFString text(utf8Text); const int spacing = 48; - while (text.size() > 0) + const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); + const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); + const UTFString::unicode_char SPACE = unicodeCharFromChar(' '); + + while (!text.empty()) { // read in characters until we have exceeded the size, or run out of text int currentWidth = 0; int currentHeight = 0; - std::string currentText; - std::string currentWord; - unsigned int i=0; - while (currentHeight <= height-spacing && i', i) == std::string::npos) + const size_t tagStart = index + 1; + const size_t tagEnd = text.find('>', tagStart); + if (tagEnd == UTFString::npos) throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + const std::string tag = text.substr(tagStart, tagEnd - tagStart).asUTF8(); - if (text.size() > i+4 && text.substr(i, 4) == "', i)-i), false); - currentHeight += (mHeight-h); + const int h = mHeight; + parseImage(tag, false); + currentHeight += (mHeight - h); currentWidth = 0; } - else if (text.size() > i+5 && text.substr(i, 5) == "', i)-i)); - currentHeight += 18; // keep this in sync with the font size + parseFont(tag); + if (currentWidth != 0) { + currentHeight += currentFontHeight(); + currentWidth = 0; + } currentWidth = 0; } - else if (text.size() > i+4 && text.substr(i, 4) == "', i)-i)); - currentHeight += 18; // keep this in sync with the font size - currentWidth = 0; + parseDiv(tag); + if (currentWidth != 0) { + currentHeight += currentFontHeight(); + currentWidth = 0; + } } - - currentText += text.substr(i, text.find('>', i)-i+1); - i = text.find('>', i); + index = tagEnd; } - else if (text[i] == '\n') + else if (ch == NEWLINE) { - currentHeight += 18; // keep this in sync with the font size + currentHeight += currentFontHeight(); currentWidth = 0; - currentWord = ""; - currentText += text[i]; + currentWordStart = index; } - else if (text[i] == ' ') + else if (ch == SPACE) { currentWidth += 3; // keep this in sync with the font's SpaceWidth property - currentWord = ""; - currentText += text[i]; + currentWordStart = index; } else { - currentWidth += - MyGUI::FontManager::getInstance().getByName (mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont) - ->getGlyphInfo(static_cast(text[i]))->width; - currentWord += text[i]; - currentText += text[i]; + currentWidth += widthForCharGlyph(ch); } if (currentWidth > width) { - currentHeight += 18; // keep this in sync with the font size + currentHeight += currentFontHeight(); currentWidth = 0; - // add size of the current word - unsigned int j=0; - while (jgetGlyphInfo(static_cast(currentWord[j]))->width; - ++j; - } + UTFString word = text.substr(currentWordStart, index - currentWordStart); + for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it) + currentWidth += widthForCharGlyph(it.getCharacter()); } - - ++i; - } - if (currentHeight > height-spacing && currentText.size() != currentWord.size()) - { - // remove the last word - currentText.erase(currentText.size()-currentWord.size(), currentText.size()); + index += UTFString::_utf16_char_length(ch); } + const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0) + ? currentWordStart : index; - result.push_back(currentText); - text.erase(0, currentText.size()); + result.push_back(text.substr(0, pageEnd).asUTF8()); + text.erase(0, pageEnd); } return result; } +float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const +{ + std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName) + ->getGlyphInfo(unicodeChar)->width; +} + +float BookTextParser::currentFontHeight() const +{ + std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); +} + MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width) { MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor text = Interpreter::fixDefinesBook(text, interpreterContext); - mParent = parent; mWidth = width; mHeight = 0; @@ -193,8 +211,8 @@ MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, co boost::algorithm::replace_all(text, "

", "\n\n"); // remove leading newlines - //while (text[0] == '\n') - // text.erase(0); +// while (text[0] == '\n') +// text.erase(0); // remove trailing " if (text[text.size()-1] == '\"') @@ -279,28 +297,30 @@ void BookTextParser::parseSubText(std::string text) { if (text[0] == '<') { - if (text.find('>') == std::string::npos) + const size_t tagStart = 1; + const size_t tagEnd = text.find('>', tagStart); + if (tagEnd == std::string::npos) throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + const std::string tag = text.substr(tagStart, tagEnd - tagStart); - if (text.size() > 4 && text.substr(0, 4) == "'))); - else if (text.size() > 5 && text.substr(0, 5) == "'))); - else if (text.size() > 4 && text.substr(0, 4) == "'))); + if (boost::algorithm::starts_with(tag, "IMG")) + parseImage(tag); + if (boost::algorithm::starts_with(tag, "FONT")) + parseFont(tag); + if (boost::algorithm::starts_with(tag, "DOV")) + parseDiv(tag); - text.erase(0, text.find('>')+1); + text.erase(0, tagEnd + 1); } - bool tagFound = false; + size_t tagStart = std::string::npos; std::string realText; // real text, without tags - unsigned int i=0; - for (; isetSize(box->getSize().width, box->getTextSize().height); mHeight += box->getTextSize().height; - if (tagFound) + if (tagStart != std::string::npos) { - parseSubText(text.substr(i, text.size())); + parseSubText(text.substr(tagStart, text.size())); } } diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index a1e115491..ab1ee3af4 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -40,6 +40,8 @@ namespace MWGui std::vector split(std::string text, const int width, const int height); protected: + float widthForCharGlyph(unsigned unicodeChar) const; + float currentFontHeight() const; void parseSubText(std::string text); void parseImage(std::string tag, bool createWidget=true);