#include "DocVisitor.h"
const float fontSizes[4] = { 12.0f, 30.0f, 24.0f, 18.0f };
const float lineSpacing = 1.5f;
const float pageLeftMargin = 60.0f;
const float pageRightMargin = 60.0f;
const float pageTopMargin = 120.0f;
const float pageBottomMargin = 60.0f;
/*
HPDF_Page_SetTextRenderingMode(page, HPDF_FILL);
HPDF_Page_SetTextRenderingMode(page, HPDF_STROKE);
*/
DocVisitor::DocVisitor(DocOutputDevice* doc, DocStyle* style, DocTemplate* templ) :
m_doc(doc),
m_page(0),
m_style(style),
m_templ(templ)
{
x = y = 0.0f;
currentStyle.push(0);
currentHeadingLevel.push(0);
insideCode = 0;
insideLink = 0;
for (int i = 0; i < 32; i++)
indentLevels[i] = 30.0f * i;
indentLevel = 0;
}
DocVisitor::~DocVisitor()
{
delete m_page; // calls endText
}
void DocVisitor::AddPage()
{
delete m_page; // calls endText
m_page = m_doc->newPage();
m_style->adornNewPage(*m_page);
m_templ->Apply(*m_page);
m_page->setColor(0);
m_page->setFontSize(currentStyle.top(), fontSizes[currentHeadingLevel.top()]);
}
void DocVisitor::CheckIfEndOfPage()
{
if (y > (m_page->height() - pageTopMargin - pageBottomMargin))
{
// Start a new page
AddPage();
y = 0.0f;
}
}
/// Visit a document.
bool DocVisitor::VisitEnter( const TiXmlDocument& /*doc*/ )
{
AddPage();
return true;
}
/// Visit a document.
bool DocVisitor::VisitExit( const TiXmlDocument& /*doc*/ )
{
delete m_page; // calls endText
m_page = 0;
return true;
}
/// Visit a declaration
bool DocVisitor::Visit(const TiXmlDeclaration& /*declaration*/)
{
return true;
}
/// Visit a stylesheet reference
bool DocVisitor::Visit(const TiXmlStylesheetReference& /*stylesheet*/)
{
return true;
}
/// Visit a comment node
bool DocVisitor::Visit( const TiXmlComment& /*comment*/ )
{
return true;
}
/// Visit an unknown node
bool DocVisitor::Visit(const TiXmlUnknown& /*unknown*/)
{
return true;
}
void DocVisitor::NewLine()
{
x = 0.0f;
y += fontSizes[currentHeadingLevel.top()] * lineSpacing;
CheckIfEndOfPage();
}
/// Visit an element.
bool DocVisitor::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* /*firstAttribute*/ )
{
if (strcmp(element.ValueTStr().c_str(), "p") == 0)
{
NewLine();
}
else if (strcmp(element.ValueTStr().c_str(), "ul") == 0)
{
NewLine();
}
else if (strcmp(element.ValueTStr().c_str(), "li") == 0)
{
indentLevel++;
m_page->drawCircle(pageLeftMargin + x + indentLevels[indentLevel], y + pageTopMargin - fontSizes[currentHeadingLevel.top()] * lineSpacing * 0.25f, 1.2f);
x += indentLevels[indentLevel] + 8.0f;
}
else if (strcmp(element.ValueTStr().c_str(), "code") == 0)
{
insideCode++;
m_page->setColor(0.2f, 0.3f, 0.2f); // dark green
}
else if (strcmp(element.ValueTStr().c_str(), "a") == 0)
{
insideLink++;
m_page->setColor(0.0f, 0.0f, 1.0f); // blue
}
else if (strcmp(element.ValueTStr().c_str(), "h1") == 0)
currentHeadingLevel.push(1), NewLine();
else if (strcmp(element.ValueTStr().c_str(), "h2") == 0)
currentHeadingLevel.push(2), NewLine();
else if (strcmp(element.ValueTStr().c_str(), "h3") == 0)
currentHeadingLevel.push(3), NewLine();
else if (strcmp(element.ValueTStr().c_str(), "strong") == 0)
currentStyle.push(2);
else if (strcmp(element.ValueTStr().c_str(), "em") == 0)
currentStyle.push(1);
else if (strcmp(element.ValueTStr().c_str(), "hr") == 0)
{
}
else {
return false;
}
return true;
}
/// Visit an element.
bool DocVisitor::VisitExit( const TiXmlElement& element )
{
if (strcmp(element.ValueTStr().c_str(), "p") == 0)
{
NewLine();
}
else if (strcmp(element.ValueTStr().c_str(), "ul") == 0)
{
NewLine();
}
else if (strcmp(element.ValueTStr().c_str(), "li") == 0)
{
indentLevel--;
NewLine();
}
else if (strcmp(element.ValueTStr().c_str(), "code") == 0)
{
insideCode--;
if (insideCode == 0)
m_page->setColor(0); // black
}
else if (strcmp(element.ValueTStr().c_str(), "a") == 0)
{
insideLink--;
if (insideLink == 0)
m_page->setColor(0); // black
}
else if (strcmp(element.ValueTStr().c_str(), "hr") == 0)
{
m_page->drawLine(pageLeftMargin, y + pageTopMargin, m_page->width() - pageRightMargin, y + pageTopMargin);
NewLine();
}
else if (strcmp(element.ValueTStr().c_str(), "h1") == 0) {
currentHeadingLevel.pop();
NewLine();
}
else if (strcmp(element.ValueTStr().c_str(), "h2") == 0)
{
currentHeadingLevel.pop();
x = 0.0;
y += fontSizes[currentHeadingLevel.top()] * lineSpacing * 0.5f;
m_page->drawLine(pageLeftMargin, y + pageTopMargin, m_page->width() - pageRightMargin, y + pageTopMargin);
y += fontSizes[currentHeadingLevel.top()] * lineSpacing * 0.5f;
CheckIfEndOfPage();
}
else if (strcmp(element.ValueTStr().c_str(), "h3") == 0)
currentHeadingLevel.pop(), NewLine();
else if (strcmp(element.ValueTStr().c_str(), "strong") == 0)
currentStyle.pop();
else if (strcmp(element.ValueTStr().c_str(), "em") == 0)
currentStyle.pop();
else {
return false;
}
return true;
}
float DocVisitor::OutputTextHelper(const char* str)
{
float w = m_page->textWidth(str);
if (insideCode)// || insideLink)
{
m_page->drawRect(pageLeftMargin + x - 1, y + pageTopMargin + 2, w + 2, fontSizes[currentHeadingLevel.top()] + 2);
if (insideCode)
m_page->setColor(0.1f, 0.1f, 0.1f); // dark green
else
m_page->setColor(0.3f, 0.3f, 1.0f); // blue
}
m_page->drawText(pageLeftMargin + x, y + pageTopMargin, str);
return w;
}
// outputs current word to pdf
// splits the string up by words if it has to, and outputs new words on a new line if it will over fill the current line
// but also if the word is too big for a complete line, then will split that word mid-word as needed.
void DocVisitor::OutputCurrentWord(const char* str)
{
// Need to see if need to break mid-word if the word is too long for the line
float wordWidthAndMargins = pageLeftMargin + pageRightMargin + m_page->textWidth(str);
if ((wordWidthAndMargins + x) >= m_page->width()) // Checking if it can fit on the current line or not
{
if (wordWidthAndMargins >= m_page->width()) // Can it fit on a new line by itself?
{
// we need to break up the text, write it char by char
int i = 0;
while (str[i]) {
char s[2] = { str[i], 0 };
// TODO: adding the dash is okay if the text is English, but not if text is other language, eg Japanese or Chinese
float chWidthAndMargins = pageLeftMargin + pageRightMargin + m_page->textWidth(s) + m_page->textWidth("-");
if ((chWidthAndMargins + x) >= m_page->width())
{
OutputTextHelper("-");
NewLine();
}
x += OutputTextHelper(s);
i++;
}
x += m_page->textWidth(" ");
return;
}
else
{
NewLine();
}
}
x += OutputTextHelper(str) + m_page->textWidth(" ");
}
/// Visit a text node
bool DocVisitor::Visit( const TiXmlText& text )
{
const char* str = text.ValueTStr().c_str();
m_page->setFontSize(currentStyle.top(), fontSizes[currentHeadingLevel.top()]);
//m_page->setColor(0);
std::string currentWord;
int i = 0;
while ( str[i] != 0 ) {
if ( str[i] == ' ' || str[i] == '\t' || str[i] == '\n' ) {
OutputCurrentWord(currentWord.c_str());
if (str[i] == '\n') { // for html, this is wrong
NewLine();
}
currentWord = "";
//break;
}
else
{
char s[2] = { str[i], 0 };
currentWord += s;
}
i++;
}
if ( str[i] == 0 ) {
OutputCurrentWord(currentWord.c_str());
}
return true;
}