BookTextParser: moved to Ogre::UTFString

Font height and unicode characters glyph width now accounted correctly.
This commit is contained in:
Sergey Shambir 2013-02-12 11:22:19 +04:00
parent 3b64389668
commit 03803f19b5
2 changed files with 96 additions and 74 deletions

View file

@ -6,7 +6,9 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <OgreUTFString.h>
using namespace MWGui; using namespace MWGui;
@ -67,118 +69,134 @@ namespace
return value; return value;
} }
Ogre::UTFString::unicode_char unicodeCharFromChar(char ch)
{
std::string s;
s += ch;
Ogre::UTFString string(s);
return string.getChar(0);
}
} }
std::vector<std::string> BookTextParser::split(std::string text, const int width, const int height) std::vector<std::string> BookTextParser::split(std::string utf8Text, const int width, const int height)
{ {
using Ogre::UTFString;
std::vector<std::string> result; std::vector<std::string> result;
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor 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, "<BR>", "\n"); boost::algorithm::replace_all(utf8Text, "<BR>", "\n");
boost::algorithm::replace_all(text, "<P>", "\n\n"); boost::algorithm::replace_all(utf8Text, "<P>", "\n\n");
UTFString text(utf8Text);
const int spacing = 48; 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 // read in characters until we have exceeded the size, or run out of text
int currentWidth = 0; int currentWidth = 0;
int currentHeight = 0; int currentHeight = 0;
std::string currentText;
std::string currentWord;
unsigned int i=0; size_t currentWordStart = 0;
while (currentHeight <= height-spacing && i<text.size()) size_t index = 0;
while (currentHeight <= height - spacing && index < text.size())
{ {
if (text[i] == '<') const UTFString::unicode_char ch = text.getChar(index);
if (ch == LEFT_ANGLE)
{ {
if (text.find('>', 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"); 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) == "<IMG") if (boost::algorithm::starts_with(tag, "IMG"))
{ {
int h = mHeight; const int h = mHeight;
parseImage(text.substr(i, text.find('>', i)-i), false); parseImage(tag, false);
currentHeight += (mHeight-h); currentHeight += (mHeight - h);
currentWidth = 0; currentWidth = 0;
} }
else if (text.size() > i+5 && text.substr(i, 5) == "<FONT") else if (boost::algorithm::starts_with(tag, "FONT"))
{ {
parseFont(text.substr(i, text.find('>', i)-i)); parseFont(tag);
currentHeight += 18; // keep this in sync with the font size if (currentWidth != 0) {
currentHeight += currentFontHeight();
currentWidth = 0;
}
currentWidth = 0; currentWidth = 0;
} }
else if (text.size() > i+4 && text.substr(i, 4) == "<DIV") else if (boost::algorithm::starts_with(tag, "DIV"))
{ {
parseDiv(text.substr(i, text.find('>', i)-i)); parseDiv(tag);
currentHeight += 18; // keep this in sync with the font size if (currentWidth != 0) {
currentWidth = 0; currentHeight += currentFontHeight();
currentWidth = 0;
}
} }
index = tagEnd;
currentText += text.substr(i, text.find('>', i)-i+1);
i = text.find('>', i);
} }
else if (text[i] == '\n') else if (ch == NEWLINE)
{ {
currentHeight += 18; // keep this in sync with the font size currentHeight += currentFontHeight();
currentWidth = 0; currentWidth = 0;
currentWord = ""; currentWordStart = index;
currentText += text[i];
} }
else if (text[i] == ' ') else if (ch == SPACE)
{ {
currentWidth += 3; // keep this in sync with the font's SpaceWidth property currentWidth += 3; // keep this in sync with the font's SpaceWidth property
currentWord = ""; currentWordStart = index;
currentText += text[i];
} }
else else
{ {
currentWidth += currentWidth += widthForCharGlyph(ch);
MyGUI::FontManager::getInstance().getByName (mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont)
->getGlyphInfo(static_cast<unsigned int>(text[i]))->width;
currentWord += text[i];
currentText += text[i];
} }
if (currentWidth > width) if (currentWidth > width)
{ {
currentHeight += 18; // keep this in sync with the font size currentHeight += currentFontHeight();
currentWidth = 0; currentWidth = 0;
// add size of the current word // add size of the current word
unsigned int j=0; UTFString word = text.substr(currentWordStart, index - currentWordStart);
while (j<currentWord.size()) for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it)
{ currentWidth += widthForCharGlyph(it.getCharacter());
currentWidth +=
MyGUI::FontManager::getInstance().getByName (mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont)
->getGlyphInfo(static_cast<unsigned int>(currentWord[j]))->width;
++j;
}
} }
index += UTFString::_utf16_char_length(ch);
++i;
}
if (currentHeight > height-spacing && currentText.size() != currentWord.size())
{
// remove the last word
currentText.erase(currentText.size()-currentWord.size(), currentText.size());
} }
const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0)
? currentWordStart : index;
result.push_back(currentText); result.push_back(text.substr(0, pageEnd).asUTF8());
text.erase(0, currentText.size()); text.erase(0, pageEnd);
} }
return result; 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) 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 MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
text = Interpreter::fixDefinesBook(text, interpreterContext); text = Interpreter::fixDefinesBook(text, interpreterContext);
mParent = parent; mParent = parent;
mWidth = width; mWidth = width;
mHeight = 0; mHeight = 0;
@ -193,8 +211,8 @@ MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, co
boost::algorithm::replace_all(text, "<P>", "\n\n"); boost::algorithm::replace_all(text, "<P>", "\n\n");
// remove leading newlines // remove leading newlines
//while (text[0] == '\n') // while (text[0] == '\n')
// text.erase(0); // text.erase(0);
// remove trailing " // remove trailing "
if (text[text.size()-1] == '\"') if (text[text.size()-1] == '\"')
@ -279,28 +297,30 @@ void BookTextParser::parseSubText(std::string text)
{ {
if (text[0] == '<') 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"); 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) == "<IMG") if (boost::algorithm::starts_with(tag, "IMG"))
parseImage(text.substr(0, text.find('>'))); parseImage(tag);
else if (text.size() > 5 && text.substr(0, 5) == "<FONT") if (boost::algorithm::starts_with(tag, "FONT"))
parseFont(text.substr(0, text.find('>'))); parseFont(tag);
else if (text.size() > 4 && text.substr(0, 4) == "<DIV") if (boost::algorithm::starts_with(tag, "DOV"))
parseDiv(text.substr(0, text.find('>'))); 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 std::string realText; // real text, without tags
unsigned int i=0; for (size_t i = 0; i<text.size(); ++i)
for (; i<text.size(); ++i)
{ {
char c = text[i]; char c = text[i];
if (c == '<') if (c == '<')
{ {
if (text[i+1] == '/') // ignore closing tags if ((i + 1 < text.size()) && text[i+1] == '/') // ignore closing tags
{ {
while (c != '>') while (c != '>')
{ {
@ -313,7 +333,7 @@ void BookTextParser::parseSubText(std::string text)
} }
else else
{ {
tagFound = true; tagStart = i;
break; break;
} }
} }
@ -336,8 +356,8 @@ void BookTextParser::parseSubText(std::string text)
box->setSize(box->getSize().width, box->getTextSize().height); box->setSize(box->getSize().width, box->getTextSize().height);
mHeight += 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()));
} }
} }

View file

@ -40,6 +40,8 @@ namespace MWGui
std::vector<std::string> split(std::string text, const int width, const int height); std::vector<std::string> split(std::string text, const int width, const int height);
protected: protected:
float widthForCharGlyph(unsigned unicodeChar) const;
float currentFontHeight() const;
void parseSubText(std::string text); void parseSubText(std::string text);
void parseImage(std::string tag, bool createWidget=true); void parseImage(std::string tag, bool createWidget=true);