1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-21 03:53:54 +00:00

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 <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>
#include <OgreUTFString.h>
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<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;
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(text, "<P>", "\n\n");
boost::algorithm::replace_all(utf8Text, "<BR>", "\n");
boost::algorithm::replace_all(utf8Text, "<P>", "\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<text.size())
size_t currentWordStart = 0;
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");
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;
parseImage(text.substr(i, text.find('>', 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) == "<FONT")
else if (boost::algorithm::starts_with(tag, "FONT"))
{
parseFont(text.substr(i, text.find('>', 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) == "<DIV")
else if (boost::algorithm::starts_with(tag, "DIV"))
{
parseDiv(text.substr(i, text.find('>', 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<unsigned int>(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 (j<currentWord.size())
{
currentWidth +=
MyGUI::FontManager::getInstance().getByName (mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont)
->getGlyphInfo(static_cast<unsigned int>(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, "<P>", "\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) == "<IMG")
parseImage(text.substr(0, text.find('>')));
else if (text.size() > 5 && text.substr(0, 5) == "<FONT")
parseFont(text.substr(0, text.find('>')));
else if (text.size() > 4 && text.substr(0, 4) == "<DIV")
parseDiv(text.substr(0, text.find('>')));
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 (; i<text.size(); ++i)
for (size_t i = 0; i<text.size(); ++i)
{
char c = text[i];
if (c == '<')
{
if (text[i+1] == '/') // ignore closing tags
if ((i + 1 < text.size()) && text[i+1] == '/') // ignore closing tags
{
while (c != '>')
{
@ -313,7 +333,7 @@ void BookTextParser::parseSubText(std::string text)
}
else
{
tagFound = true;
tagStart = i;
break;
}
}
@ -336,8 +356,8 @@ void BookTextParser::parseSubText(std::string text)
box->setSize(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()));
}
}

View file

@ -40,6 +40,8 @@ namespace MWGui
std::vector<std::string> 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);