|
|
|
@ -2,6 +2,7 @@
|
|
|
|
|
|
|
|
|
|
#include <components/interpreter/defines.hpp>
|
|
|
|
|
#include <components/misc/resourcehelpers.hpp>
|
|
|
|
|
#include <components/misc/stringops.hpp>
|
|
|
|
|
|
|
|
|
|
#include "../mwscript/interpretercontext.hpp"
|
|
|
|
|
|
|
|
|
@ -14,39 +15,167 @@
|
|
|
|
|
|
|
|
|
|
#include <MyGUI_EditText.h>
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
Ogre::UTFString::unicode_char unicodeCharFromChar(char ch)
|
|
|
|
|
{
|
|
|
|
|
std::string s;
|
|
|
|
|
s += ch;
|
|
|
|
|
Ogre::UTFString string(s);
|
|
|
|
|
return string.getChar(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace MWGui
|
|
|
|
|
{
|
|
|
|
|
namespace Formatting
|
|
|
|
|
{
|
|
|
|
|
Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, std::string utf8Text, const int pageWidth, const int pageHeight)
|
|
|
|
|
/* BookTextParser */
|
|
|
|
|
BookTextParser::BookTextParser(const std::string & text)
|
|
|
|
|
: mIndex(-1), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true)
|
|
|
|
|
{
|
|
|
|
|
using Ogre::UTFString;
|
|
|
|
|
|
|
|
|
|
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
|
|
|
|
|
utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext);
|
|
|
|
|
mText = Interpreter::fixDefinesBook(mText, interpreterContext);
|
|
|
|
|
|
|
|
|
|
boost::algorithm::replace_all(mText, "\r", "");
|
|
|
|
|
|
|
|
|
|
registerTag("br", Event_BrTag);
|
|
|
|
|
registerTag("p", Event_PTag);
|
|
|
|
|
registerTag("img", Event_ImgTag);
|
|
|
|
|
registerTag("div", Event_DivTag);
|
|
|
|
|
registerTag("font", Event_FontTag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BookTextParser::registerTag(const std::string & tag, BookTextParser::Events type)
|
|
|
|
|
{
|
|
|
|
|
mTagTypes[tag] = type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string BookTextParser::getReadyText()
|
|
|
|
|
{
|
|
|
|
|
return mReadyText;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BookTextParser::Events BookTextParser::next()
|
|
|
|
|
{
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
++mIndex;
|
|
|
|
|
if (mIndex >= mText.size())
|
|
|
|
|
return Event_EOF;
|
|
|
|
|
|
|
|
|
|
char ch = mText[mIndex];
|
|
|
|
|
if (ch == '<')
|
|
|
|
|
{
|
|
|
|
|
const size_t tagStart = mIndex + 1;
|
|
|
|
|
const size_t tagEnd = mText.find('>', tagStart);
|
|
|
|
|
if (tagEnd == std::string::npos)
|
|
|
|
|
throw std::runtime_error("BookTextParser Error: Tag is not terminated");
|
|
|
|
|
parseTag(mText.substr(tagStart, tagEnd - tagStart));
|
|
|
|
|
mIndex = tagEnd;
|
|
|
|
|
|
|
|
|
|
if (mTagTypes.find(mTag) != mTagTypes.end())
|
|
|
|
|
{
|
|
|
|
|
Events type = mTagTypes.at(mTag);
|
|
|
|
|
|
|
|
|
|
if (type == Event_BrTag || type == Event_PTag)
|
|
|
|
|
{
|
|
|
|
|
if (!mIgnoreNewlineTags)
|
|
|
|
|
{
|
|
|
|
|
if (type == Event_BrTag)
|
|
|
|
|
mBuffer.push_back('\n');
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mBuffer.append("\n\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mIgnoreLineEndings = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
flushBuffer();
|
|
|
|
|
|
|
|
|
|
if (type == Event_ImgTag)
|
|
|
|
|
{
|
|
|
|
|
mIgnoreLineEndings = false;
|
|
|
|
|
mIgnoreNewlineTags = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!mIgnoreLineEndings || ch != '\n')
|
|
|
|
|
{
|
|
|
|
|
mBuffer.push_back(ch);
|
|
|
|
|
mIgnoreLineEndings = false;
|
|
|
|
|
mIgnoreNewlineTags = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mIndex == mText.size() - 1)
|
|
|
|
|
{
|
|
|
|
|
flushBuffer();
|
|
|
|
|
return Event_LastText;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BookTextParser::flushBuffer()
|
|
|
|
|
{
|
|
|
|
|
mReadyText = mBuffer;
|
|
|
|
|
mBuffer.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const BookTextParser::Attributes & BookTextParser::getAttributes() const
|
|
|
|
|
{
|
|
|
|
|
return mAttributes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BookTextParser::parseTag(std::string tag)
|
|
|
|
|
{
|
|
|
|
|
size_t tagNameEndPos = tag.find(' ');
|
|
|
|
|
mTag = tag.substr(0, tagNameEndPos);
|
|
|
|
|
Misc::StringUtils::toLower(mTag);
|
|
|
|
|
mAttributes.clear();
|
|
|
|
|
if (tagNameEndPos == std::string::npos)
|
|
|
|
|
return;
|
|
|
|
|
tag.erase(0, tagNameEndPos+1);
|
|
|
|
|
|
|
|
|
|
while (!tag.empty())
|
|
|
|
|
{
|
|
|
|
|
int sepPos = tag.find('=');
|
|
|
|
|
if (sepPos == std::string::npos)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
boost::algorithm::replace_all(utf8Text, "\n", "");
|
|
|
|
|
boost::algorithm::replace_all(utf8Text, "\r", "");
|
|
|
|
|
boost::algorithm::replace_all(utf8Text, "<BR>", "\n");
|
|
|
|
|
boost::algorithm::replace_all(utf8Text, "<P>", "\n\n");
|
|
|
|
|
std::string key = tag.substr(0, sepPos);
|
|
|
|
|
Misc::StringUtils::toLower(key);
|
|
|
|
|
tag.erase(0, sepPos+1);
|
|
|
|
|
|
|
|
|
|
UTFString text(utf8Text);
|
|
|
|
|
UTFString plainText;
|
|
|
|
|
std::string value;
|
|
|
|
|
|
|
|
|
|
const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<');
|
|
|
|
|
const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n');
|
|
|
|
|
if (tag.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (tag[0] == '"')
|
|
|
|
|
{
|
|
|
|
|
size_t quoteEndPos = tag.find('"', 1);
|
|
|
|
|
if (quoteEndPos == std::string::npos)
|
|
|
|
|
throw std::runtime_error("BookTextParser Error: Missing end quote in tag");
|
|
|
|
|
value = tag.substr(1, quoteEndPos-1);
|
|
|
|
|
tag.erase(0, quoteEndPos+2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
size_t valEndPos = tag.find(' ');
|
|
|
|
|
if (valEndPos == std::string::npos)
|
|
|
|
|
{
|
|
|
|
|
value = tag;
|
|
|
|
|
tag.erase();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
value = tag.substr(0, valEndPos);
|
|
|
|
|
tag.erase(0, valEndPos+1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mAttributes[key] = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* BookFormatter */
|
|
|
|
|
Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, const std::string & markup, const int pageWidth, const int pageHeight)
|
|
|
|
|
{
|
|
|
|
|
Paginator pag(pageWidth, pageHeight);
|
|
|
|
|
|
|
|
|
|
while (parent->getChildCount())
|
|
|
|
@ -57,55 +186,49 @@ namespace MWGui
|
|
|
|
|
MyGUI::Widget * paper = parent->createWidget<MyGUI::Widget>("Widget", MyGUI::IntCoord(0, 0, pag.getPageWidth(), pag.getPageHeight()), MyGUI::Align::Left | MyGUI::Align::Top);
|
|
|
|
|
paper->setNeedMouseFocus(false);
|
|
|
|
|
|
|
|
|
|
bool ignoreNewlines = true;
|
|
|
|
|
for (size_t index = 0; index < text.size(); ++index)
|
|
|
|
|
BookTextParser parser(markup);
|
|
|
|
|
BookTextParser::Events event;
|
|
|
|
|
while ((event = parser.next()) != BookTextParser::Event_EOF)
|
|
|
|
|
{
|
|
|
|
|
const UTFString::unicode_char ch = text.getChar(index);
|
|
|
|
|
if (!plainText.empty() && (ch == LEFT_ANGLE || index == text.size() - 1))
|
|
|
|
|
if (event == BookTextParser::Event_BrTag || event == BookTextParser::Event_PTag)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
std::string plainText = parser.getReadyText();
|
|
|
|
|
if (!plainText.empty())
|
|
|
|
|
{
|
|
|
|
|
// if there's a newline at the end of the box caption, remove it
|
|
|
|
|
if (plainText[plainText.size()-1] == NEWLINE)
|
|
|
|
|
if (plainText[plainText.size()-1] == '\n')
|
|
|
|
|
plainText.erase(plainText.end()-1);
|
|
|
|
|
|
|
|
|
|
TextElement elem(paper, pag, mTextStyle, plainText.asUTF8());
|
|
|
|
|
TextElement elem(paper, pag, mTextStyle, plainText);
|
|
|
|
|
elem.paginate();
|
|
|
|
|
|
|
|
|
|
plainText.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ch == LEFT_ANGLE)
|
|
|
|
|
switch (event)
|
|
|
|
|
{
|
|
|
|
|
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 (boost::algorithm::starts_with(tag, "IMG"))
|
|
|
|
|
case BookTextParser::Event_ImgTag:
|
|
|
|
|
{
|
|
|
|
|
ImageElement elem(paper, pag, mTextStyle, tag);
|
|
|
|
|
elem.paginate();
|
|
|
|
|
const BookTextParser::Attributes & attr = parser.getAttributes();
|
|
|
|
|
|
|
|
|
|
ignoreNewlines = false;
|
|
|
|
|
}
|
|
|
|
|
else if (boost::algorithm::starts_with(tag, "FONT"))
|
|
|
|
|
{
|
|
|
|
|
parseFont(tag);
|
|
|
|
|
}
|
|
|
|
|
else if (boost::algorithm::starts_with(tag, "DIV"))
|
|
|
|
|
{
|
|
|
|
|
parseDiv(tag);
|
|
|
|
|
}
|
|
|
|
|
if (attr.find("src") == attr.end() || attr.find("width") == attr.end() || attr.find("height") == attr.end())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
index = tagEnd;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!ignoreNewlines || ch != NEWLINE)
|
|
|
|
|
{
|
|
|
|
|
plainText.push_back(ch);
|
|
|
|
|
ignoreNewlines = false;
|
|
|
|
|
std::string src = attr.at("src");
|
|
|
|
|
int width = boost::lexical_cast<int>(attr.at("width"));
|
|
|
|
|
int height = boost::lexical_cast<int>(attr.at("height"));
|
|
|
|
|
|
|
|
|
|
ImageElement elem(paper, pag, src, width, height);
|
|
|
|
|
elem.paginate();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case BookTextParser::Event_FontTag:
|
|
|
|
|
handleFont(parser.getAttributes());
|
|
|
|
|
break;
|
|
|
|
|
case BookTextParser::Event_DivTag:
|
|
|
|
|
handleDiv(parser.getAttributes());
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -117,33 +240,31 @@ namespace MWGui
|
|
|
|
|
return pag.getPages();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, std::string utf8Text)
|
|
|
|
|
Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, const std::string & markup)
|
|
|
|
|
{
|
|
|
|
|
return markupToWidget(parent, utf8Text, parent->getWidth(), parent->getHeight());
|
|
|
|
|
return markupToWidget(parent, markup, parent->getWidth(), parent->getHeight());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BookFormatter::parseDiv(std::string tag)
|
|
|
|
|
void BookFormatter::handleDiv(const BookTextParser::Attributes & attr)
|
|
|
|
|
{
|
|
|
|
|
if (tag.find("ALIGN=") == std::string::npos)
|
|
|
|
|
if (attr.find("align") == attr.end())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int align_start = tag.find("ALIGN=")+7;
|
|
|
|
|
std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start);
|
|
|
|
|
if (align == "CENTER")
|
|
|
|
|
std::string align = attr.at("align");
|
|
|
|
|
|
|
|
|
|
if (Misc::StringUtils::ciEqual(align, "center"))
|
|
|
|
|
mTextStyle.mTextAlign = MyGUI::Align::HCenter;
|
|
|
|
|
else if (align == "LEFT")
|
|
|
|
|
else if (Misc::StringUtils::ciEqual(align, "left"))
|
|
|
|
|
mTextStyle.mTextAlign = MyGUI::Align::Left;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BookFormatter::parseFont(std::string tag)
|
|
|
|
|
void BookFormatter::handleFont(const BookTextParser::Attributes & attr)
|
|
|
|
|
{
|
|
|
|
|
if (tag.find("COLOR=") != std::string::npos)
|
|
|
|
|
if (attr.find("color") != attr.end())
|
|
|
|
|
{
|
|
|
|
|
int color_start = tag.find("COLOR=")+7;
|
|
|
|
|
|
|
|
|
|
int color;
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << tag.substr(color_start, tag.find('"', color_start)-color_start);
|
|
|
|
|
ss << attr.at("color");
|
|
|
|
|
ss >> std::hex >> color;
|
|
|
|
|
|
|
|
|
|
mTextStyle.mColour = MyGUI::Colour(
|
|
|
|
@ -151,23 +272,22 @@ namespace MWGui
|
|
|
|
|
(color>>8 & 0xFF) / 255.f,
|
|
|
|
|
(color & 0xFF) / 255.f);
|
|
|
|
|
}
|
|
|
|
|
if (tag.find("FACE=") != std::string::npos)
|
|
|
|
|
if (attr.find("face") != attr.end())
|
|
|
|
|
{
|
|
|
|
|
int face_start = tag.find("FACE=")+6;
|
|
|
|
|
std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start);
|
|
|
|
|
std::string face = attr.at("face");
|
|
|
|
|
|
|
|
|
|
if (face != "Magic Cards")
|
|
|
|
|
mTextStyle.mFont = face;
|
|
|
|
|
}
|
|
|
|
|
if (tag.find("SIZE=") != std::string::npos)
|
|
|
|
|
if (attr.find("size") != attr.end())
|
|
|
|
|
{
|
|
|
|
|
/// \todo
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* GraphicElement */
|
|
|
|
|
GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style)
|
|
|
|
|
: mParent(parent), mPaginator(pag), mStyle(style)
|
|
|
|
|
GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag)
|
|
|
|
|
: mParent(parent), mPaginator(pag)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -189,22 +309,11 @@ namespace MWGui
|
|
|
|
|
return mPaginator.getStartTop() + mPaginator.getPageHeight();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GraphicElement::currentFontHeight() const
|
|
|
|
|
{
|
|
|
|
|
std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont);
|
|
|
|
|
return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float GraphicElement::widthForCharGlyph(MyGUI::Char unicodeChar) const
|
|
|
|
|
{
|
|
|
|
|
std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont);
|
|
|
|
|
return MyGUI::FontManager::getInstance().getByName(fontName)
|
|
|
|
|
->getGlyphInfo(unicodeChar)->width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TextElement */
|
|
|
|
|
TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & text)
|
|
|
|
|
: GraphicElement(parent, pag, style)
|
|
|
|
|
TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag,
|
|
|
|
|
const TextStyle & style, const std::string & text)
|
|
|
|
|
: GraphicElement(parent, pag),
|
|
|
|
|
mStyle(style)
|
|
|
|
|
{
|
|
|
|
|
MyGUI::EditBox* box = parent->createWidget<MyGUI::EditBox>("NormalText",
|
|
|
|
|
MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top,
|
|
|
|
@ -222,6 +331,12 @@ namespace MWGui
|
|
|
|
|
mEditBox = box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TextElement::currentFontHeight() const
|
|
|
|
|
{
|
|
|
|
|
std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont);
|
|
|
|
|
return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TextElement::getHeight()
|
|
|
|
|
{
|
|
|
|
|
return mEditBox->getTextSize().height;
|
|
|
|
@ -250,19 +365,11 @@ namespace MWGui
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ImageElement */
|
|
|
|
|
ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & tag)
|
|
|
|
|
: GraphicElement(parent, pag, style),
|
|
|
|
|
mImageHeight(0)
|
|
|
|
|
ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag,
|
|
|
|
|
const std::string & src, int width, int height)
|
|
|
|
|
: GraphicElement(parent, pag),
|
|
|
|
|
mImageHeight(height)
|
|
|
|
|
{
|
|
|
|
|
int src_start = tag.find("SRC=")+5;
|
|
|
|
|
std::string src = tag.substr(src_start, tag.find('"', src_start)-src_start);
|
|
|
|
|
|
|
|
|
|
int width_start = tag.find("WIDTH=")+7;
|
|
|
|
|
int width = boost::lexical_cast<int>(tag.substr(width_start, tag.find('"', width_start)-width_start));
|
|
|
|
|
|
|
|
|
|
int height_start = tag.find("HEIGHT=")+8;
|
|
|
|
|
mImageHeight = boost::lexical_cast<int>(tag.substr(height_start, tag.find('"', height_start)-height_start));
|
|
|
|
|
|
|
|
|
|
mImageBox = parent->createWidget<MyGUI::ImageBox> ("ImageBox",
|
|
|
|
|
MyGUI::IntCoord(0, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top,
|
|
|
|
|
parent->getName() + boost::lexical_cast<std::string>(parent->getChildCount()));
|
|
|
|
@ -279,6 +386,9 @@ namespace MWGui
|
|
|
|
|
|
|
|
|
|
int ImageElement::pageSplit()
|
|
|
|
|
{
|
|
|
|
|
// if the image is larger than the page, fall back to the default pageSplit implementation
|
|
|
|
|
if (mImageHeight > mPaginator.getPageHeight())
|
|
|
|
|
return GraphicElement::pageSplit();
|
|
|
|
|
return mPaginator.getCurrentTop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|