mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-02-23 13:17:14 +01:00
Switched to POCO library for unified platform/library interface. Deprecated the external module API. It was creating more problems than solving. Removed most built-in libraries in favor of system libraries for easier maintenance. Cleaned and secured code with help from static analyzers.
887 lines
21 KiB
C++
887 lines
21 KiB
C++
//
|
|
// XMLTemplate.cpp
|
|
//
|
|
|
|
|
|
#include "Poco/PDF/XMLTemplate.h"
|
|
#include "Poco/PDF/Table.h"
|
|
#include "Poco/SAX/SAXParser.h"
|
|
#include "Poco/SAX/DefaultHandler.h"
|
|
#include "Poco/SAX/Attributes.h"
|
|
#include "Poco/SAX/InputSource.h"
|
|
#include "Poco/Util/PropertyFileConfiguration.h"
|
|
#include "Poco/FileStream.h"
|
|
#include "Poco/AutoPtr.h"
|
|
#include "Poco/String.h"
|
|
#include "Poco/Exception.h"
|
|
#include "Poco/Path.h"
|
|
#include "Poco/TextConverter.h"
|
|
#include "Poco/UTF8Encoding.h"
|
|
#include "Poco/UTF8String.h"
|
|
#include <vector>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
|
|
namespace Poco {
|
|
namespace PDF {
|
|
|
|
|
|
class StackedConfiguration : public Poco::Util::AbstractConfiguration
|
|
{
|
|
public:
|
|
typedef Poco::AutoPtr<Poco::Util::AbstractConfiguration> ConfigPtr;
|
|
typedef std::vector<ConfigPtr> ConfigStack;
|
|
|
|
void push(ConfigPtr pConfig)
|
|
{
|
|
_stack.push_back(pConfig);
|
|
}
|
|
|
|
void pop()
|
|
{
|
|
_stack.pop_back();
|
|
}
|
|
|
|
ConfigPtr current() const
|
|
{
|
|
poco_assert(_stack.size() > 0);
|
|
return _stack.back();
|
|
}
|
|
|
|
float getFloat(const std::string& value)
|
|
{
|
|
return static_cast<float>(getDouble(value));
|
|
}
|
|
|
|
float getFloat(const std::string& value, float deflt)
|
|
{
|
|
return static_cast<float>(getDouble(value, deflt));
|
|
}
|
|
|
|
// AbstractConfiguration
|
|
bool getRaw(const std::string& key, std::string& value) const
|
|
{
|
|
for (ConfigStack::const_reverse_iterator it = _stack.rbegin(); it != _stack.rend(); ++it)
|
|
{
|
|
if ((*it)->has(key))
|
|
{
|
|
value = (*it)->getRawString(key);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void setRaw(const std::string& key, const std::string& value)
|
|
{
|
|
throw Poco::InvalidAccessException("not writable");
|
|
}
|
|
|
|
void enumerate(const std::string& key, Poco::Util::AbstractConfiguration::Keys& range) const
|
|
{
|
|
std::set<std::string> keys;
|
|
for (ConfigStack::const_iterator itc = _stack.begin(); itc != _stack.end(); ++itc)
|
|
{
|
|
Poco::Util::AbstractConfiguration::Keys partRange;
|
|
(*itc)->keys(key, partRange);
|
|
for (Poco::Util::AbstractConfiguration::Keys::const_iterator itr = partRange.begin(); itr != partRange.end(); ++itr)
|
|
{
|
|
if (keys.find(*itr) == keys.end())
|
|
{
|
|
range.push_back(*itr);
|
|
keys.insert(*itr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void removeRaw(const std::string& key)
|
|
{
|
|
throw Poco::InvalidAccessException("not writable");
|
|
}
|
|
|
|
private:
|
|
std::vector<ConfigPtr> _stack;
|
|
};
|
|
|
|
|
|
class Box
|
|
{
|
|
public:
|
|
Box() :
|
|
_x(0),
|
|
_y(0),
|
|
_width(0),
|
|
_height(0)
|
|
{
|
|
}
|
|
|
|
Box(float x, float y, float width, float height) :
|
|
_x(x),
|
|
_y(y),
|
|
_width(width),
|
|
_height(height)
|
|
{
|
|
|
|
}
|
|
|
|
Box(const Box& box) :
|
|
_x(box._x),
|
|
_y(box._y),
|
|
_width(box._width),
|
|
_height(box._height)
|
|
{
|
|
}
|
|
|
|
~Box()
|
|
{
|
|
}
|
|
|
|
Box& operator = (const Box& box)
|
|
{
|
|
Box tmp(box);
|
|
tmp.swap(*this);
|
|
return *this;
|
|
}
|
|
|
|
void swap(Box& box)
|
|
{
|
|
std::swap(_x, box._x);
|
|
std::swap(_y, box._y);
|
|
std::swap(_width, box._width);
|
|
std::swap(_height, box._height);
|
|
}
|
|
|
|
float left() const
|
|
{
|
|
return _x;
|
|
}
|
|
|
|
float right() const
|
|
{
|
|
return _x + _width;
|
|
}
|
|
|
|
float bottom() const
|
|
{
|
|
return _y;
|
|
}
|
|
|
|
float top() const
|
|
{
|
|
return _y + _height;
|
|
}
|
|
|
|
float width() const
|
|
{
|
|
return _width;
|
|
}
|
|
|
|
float height() const
|
|
{
|
|
return _height;
|
|
}
|
|
|
|
void inset(float delta)
|
|
{
|
|
_x += delta;
|
|
_y -= delta;
|
|
_width -= 2 * delta;
|
|
_height -= 2 * delta;
|
|
}
|
|
|
|
void extend(float delta)
|
|
{
|
|
inset(-delta);
|
|
}
|
|
|
|
void inset(float left, float right, float top, float bottom)
|
|
{
|
|
_x += left;
|
|
_y += bottom;
|
|
_width -= (left + right);
|
|
_height -= (top + bottom);
|
|
}
|
|
|
|
private:
|
|
float _x;
|
|
float _y;
|
|
float _width;
|
|
float _height;
|
|
};
|
|
|
|
|
|
class TemplateHandler : public Poco::XML::DefaultHandler
|
|
{
|
|
public:
|
|
typedef Poco::AutoPtr<Poco::Util::AbstractConfiguration> StylePtr;
|
|
|
|
TemplateHandler(const Poco::Path& base) :
|
|
_base(base),
|
|
_pDocument(0),
|
|
_pPage(0),
|
|
_y(0)
|
|
{
|
|
_styles.push(parseStyle("font-family: Helvetica; font-size: 12; line-height: 1.2"));
|
|
}
|
|
|
|
~TemplateHandler()
|
|
{
|
|
_styles.pop();
|
|
delete _pPage;
|
|
delete _pDocument;
|
|
}
|
|
|
|
Document* document()
|
|
{
|
|
Document* pDocument = _pDocument;
|
|
_pDocument = 0;
|
|
return pDocument;
|
|
}
|
|
|
|
void startDoc(const Poco::XML::Attributes& attributes)
|
|
{
|
|
if (_pDocument) throw Poco::IllegalStateException("only one <document> element is allowed");
|
|
|
|
StylePtr pStyle = pushStyle(attributes);
|
|
|
|
_size = attributes.getValue("size");
|
|
if (_size.empty()) _size = "A4";
|
|
_orientation = attributes.getValue("orientation");
|
|
if (_orientation.empty()) _orientation = "portrait";
|
|
|
|
_encoding = attributes.getValue("encoding");
|
|
if (_encoding.empty()) _encoding = "WinAnsiEncoding";
|
|
|
|
_pDocument = new Document(0, parsePageSize(_size), parseOrientation(_orientation));
|
|
}
|
|
|
|
void endDoc()
|
|
{
|
|
popStyle();
|
|
}
|
|
|
|
void startPage(const Poco::XML::Attributes& attributes)
|
|
{
|
|
if (!_pDocument) throw Poco::IllegalStateException("missing <document> element");
|
|
if (_pPage) throw Poco::IllegalStateException("nested <page> elements are not allowed");
|
|
|
|
StylePtr pStyle = pushStyle(attributes);
|
|
|
|
std::string size = attributes.getValue("size");
|
|
if (size.empty()) size = _size;
|
|
std::string orientation = attributes.getValue("orientation");
|
|
if (orientation.empty()) orientation = _orientation;
|
|
|
|
_pPage = new Page(_pDocument->addPage(parsePageSize(_size), parseOrientation(orientation)));
|
|
_pPage->setLineWidth(0.2f);
|
|
RGBColor black = {0, 0, 0};
|
|
_pPage->setRGBStroke(black);
|
|
|
|
// read or force font, page must have default
|
|
std::string fontFamily = _styles.getString("font-family", "helvetica");
|
|
float fontSize = _styles.getFloat("font-size", 10.0);
|
|
std::string fontStyle = _styles.getString("font-style", "normal");
|
|
std::string fontWeight = _styles.getString("font-weight", "normal");
|
|
|
|
Font font = loadFont(fontFamily, fontStyle, fontWeight);
|
|
_pPage->setFont(font, fontSize);
|
|
|
|
_boxes.push_back(Box(0, 0, _pPage->getWidth(), _pPage->getHeight()));
|
|
|
|
float margin = _styles.getFloat("margin", 0);
|
|
float marginLeft = _styles.getFloat("margin-left", margin);
|
|
float marginRight = _styles.getFloat("margin-right", margin);
|
|
float marginTop = _styles.getFloat("margin-top", margin);
|
|
float marginBottom = _styles.getFloat("margin-bottom", margin);
|
|
|
|
_boxes.back().inset(marginLeft, marginRight, marginTop, marginBottom);
|
|
|
|
_y = _boxes.back().top();
|
|
}
|
|
|
|
void endPage()
|
|
{
|
|
_boxes.pop_back();
|
|
delete _pPage;
|
|
_pPage = 0;
|
|
popStyle();
|
|
}
|
|
|
|
void startSpan(const Poco::XML::Attributes& attributes)
|
|
{
|
|
if (!_pPage) throw Poco::IllegalStateException("missing <page> element");
|
|
|
|
StylePtr pStyle = pushStyle(attributes);
|
|
|
|
_text.clear();
|
|
}
|
|
|
|
void endSpan()
|
|
{
|
|
std::string fontFamily = _styles.getString("font-family");
|
|
float fontSize = _styles.getFloat("font-size");
|
|
float lineHeight = _styles.getFloat("line-height");
|
|
std::string textAlign = _styles.getString("text-align", "left");
|
|
std::string fontStyle = _styles.getString("font-style", "normal");
|
|
std::string fontWeight = _styles.getString("font-weight", "normal");
|
|
std::string textTransform = _styles.getString("text-transform", "none");
|
|
|
|
_text = transform(_text, textTransform);
|
|
_text = transcode(_text);
|
|
|
|
Font font = loadFont(fontFamily, fontStyle, fontWeight);
|
|
_pPage->setFont(font, fontSize);
|
|
|
|
float width = static_cast<float>(font.textWidth(_text).width*fontSize / 1000);
|
|
float height = static_cast<float>(font.upperHeight()*fontSize / 1000)*lineHeight;
|
|
|
|
float x = static_cast<float>(_styles.current()->getDouble("left", _styles.current()->getDouble("right", width) - width));
|
|
float y = static_cast<float>(_styles.current()->getDouble("bottom", _styles.current()->getDouble("top", _y) - height));
|
|
|
|
if (textAlign == "center")
|
|
x = (box().width() - width) / 2;
|
|
else if (textAlign == "right")
|
|
x = box().width() - width;
|
|
|
|
translateInBox(x, y);
|
|
|
|
_pPage->writeOnce(x, y, _text);
|
|
|
|
moveY(y);
|
|
|
|
popStyle();
|
|
}
|
|
|
|
void startImg(const Poco::XML::Attributes& attributes)
|
|
{
|
|
if (!_pPage) throw Poco::IllegalStateException("missing <page> element");
|
|
|
|
StylePtr pStyle = pushStyle(attributes);
|
|
|
|
std::string path = attributes.getValue("src");
|
|
Image image = loadImage(path);
|
|
|
|
float width = static_cast<float>(pStyle->getDouble("width", image.width()));
|
|
float height = static_cast<float>(pStyle->getDouble("height", image.height()));
|
|
|
|
float scale = static_cast<float>(pStyle->getDouble("scale", 1.0));
|
|
float scaleX = static_cast<float>(pStyle->getDouble("scale-x", scale));
|
|
float scaleY = static_cast<float>(pStyle->getDouble("scale-y", scale));
|
|
|
|
width *= scaleX;
|
|
height *= scaleY;
|
|
|
|
float x = static_cast<float>(_styles.current()->getDouble("left", _styles.current()->getDouble("right", width) - width));
|
|
float y = static_cast<float>(_styles.current()->getDouble("bottom", _styles.current()->getDouble("top", _y) - height));
|
|
|
|
translateInBox(x, y);
|
|
|
|
_pPage->drawImage(image, x, y, width, height);
|
|
|
|
moveY(y);
|
|
}
|
|
|
|
void endImg()
|
|
{
|
|
popStyle();
|
|
}
|
|
|
|
void startTable(const Poco::XML::Attributes& attributes)
|
|
{
|
|
if (!_pPage) throw Poco::IllegalStateException("missing <page> element");
|
|
|
|
StylePtr pStyle = pushStyle(attributes);
|
|
|
|
_pTable = new Table(0, 0, attributes.getValue("name"));
|
|
}
|
|
|
|
void endTable()
|
|
{
|
|
if (_pTable->rows() > 0)
|
|
{
|
|
std::string fontFamily = _styles.getString("font-family");
|
|
float fontSize = _styles.getFloat("font-size");
|
|
std::string fontStyle = _styles.getString("font-style", "normal");
|
|
std::string fontWeight = _styles.getString("font-weight", "normal");
|
|
|
|
Font font = loadFont(fontFamily, fontStyle, fontWeight);
|
|
|
|
float lineHeight = static_cast<float>((font.ascent() - font.descent())*fontSize / 1000)*_styles.getFloat("line-height");
|
|
|
|
float width = static_cast<float>(_styles.current()->getDouble("width", box().width()));
|
|
float height = static_cast<float>(_styles.current()->getDouble("height", _pTable->rows()*lineHeight));
|
|
|
|
float x = static_cast<float>(_styles.current()->getDouble("left", _styles.current()->getDouble("right", width) - width));
|
|
float y = static_cast<float>(_styles.current()->getDouble("bottom", _styles.current()->getDouble("top", _y) - height) + height);
|
|
y -= lineHeight;
|
|
|
|
translateInBox(x, y);
|
|
|
|
_pTable->draw(*_pPage, x, y, width, height);
|
|
|
|
y -= height - lineHeight;
|
|
|
|
moveY(y);
|
|
}
|
|
_pTable = 0;
|
|
popStyle();
|
|
}
|
|
|
|
void startTr(const Poco::XML::Attributes& attributes)
|
|
{
|
|
if (!_pTable) throw Poco::IllegalStateException("missing <table> element");
|
|
|
|
StylePtr pStyle = pushStyle(attributes);
|
|
_row.clear();
|
|
}
|
|
|
|
void endTr()
|
|
{
|
|
_pTable->addRow(_row);
|
|
_row.clear();
|
|
popStyle();
|
|
}
|
|
|
|
void startTd(const Poco::XML::Attributes& attributes)
|
|
{
|
|
StylePtr pStyle = pushStyle(attributes);
|
|
|
|
_text.clear();
|
|
}
|
|
|
|
void endTd()
|
|
{
|
|
AttributedString::Alignment align = AttributedString::ALIGN_LEFT;
|
|
int style = AttributedString::STYLE_PLAIN;
|
|
|
|
|
|
std::string fontFamily = _styles.getString("font-family");
|
|
float fontSize = _styles.getFloat("font-size");
|
|
std::string textAlign = _styles.getString("text-align", "left");
|
|
std::string fontStyle = _styles.getString("font-style", "normal");
|
|
std::string fontWeight = _styles.getString("font-weight", "normal");
|
|
std::string textTransform = _styles.getString("text-transform", "none");
|
|
std::string widthPct = _styles.getString("width", "");
|
|
// solid only supported at this time
|
|
bool borderAll = _styles.getString("border-style", "") == "solid";
|
|
bool borderLeft = _styles.getString("border-left", "") == "solid";
|
|
bool borderTop = _styles.getString("border-top", "") == "solid";
|
|
bool borderRight = _styles.getString("border-right", "") == "solid";
|
|
bool borderBottom = _styles.getString("border-bottom", "") == "solid";
|
|
|
|
_text = transform(_text, textTransform);
|
|
_text = transcode(_text);
|
|
|
|
if (textAlign == "right")
|
|
align = AttributedString::ALIGN_RIGHT;
|
|
else if (textAlign == "left")
|
|
align = AttributedString::ALIGN_LEFT;
|
|
else if (textAlign == "center")
|
|
align = AttributedString::ALIGN_CENTER;
|
|
|
|
if (fontStyle == "italic" || fontStyle == "oblique")
|
|
style |= AttributedString::STYLE_ITALIC;
|
|
|
|
if (fontWeight == "bold")
|
|
style |= AttributedString::STYLE_BOLD;
|
|
|
|
AttributedString content(_text, align, style);
|
|
content.setAttribute(AttributedString::ATTR_SIZE, fontSize);
|
|
|
|
Cell::FontMapPtr pFontMap = new Cell::FontMap;
|
|
std::string normalizedFontFamily(normalizeFontName(fontFamily));
|
|
(*pFontMap)[AttributedString::STYLE_PLAIN] = normalizedFontFamily;
|
|
(*pFontMap)[AttributedString::STYLE_BOLD] = boldFontName(normalizedFontFamily);
|
|
(*pFontMap)[AttributedString::STYLE_ITALIC] = italicFontName(normalizedFontFamily);
|
|
(*pFontMap)[AttributedString::STYLE_BOLD | AttributedString::STYLE_ITALIC] = boldItalicFontName(normalizedFontFamily);
|
|
|
|
int width = -1;
|
|
if (!widthPct.empty())
|
|
{
|
|
if (*widthPct.rbegin() != '%')
|
|
throw Poco::InvalidArgumentException("Only percentage widths supported for table cells.");
|
|
else
|
|
{
|
|
widthPct.erase(widthPct.length() - 1);
|
|
width = NumberParser::parse(widthPct);
|
|
}
|
|
}
|
|
|
|
Cell cell(content, pFontMap, _encoding, false, width);
|
|
if (borderAll) cell.borderAll(true);
|
|
if (borderLeft) cell.borderLeft(true);
|
|
if (borderTop) cell.borderTop(true);
|
|
if (borderRight) cell.borderRight(true);
|
|
if (borderBottom) cell.borderBottom(true);
|
|
_row.push_back(cell);
|
|
|
|
popStyle();
|
|
}
|
|
|
|
void startHr(const Poco::XML::Attributes& attributes)
|
|
{
|
|
if (!_pPage) throw Poco::IllegalStateException("missing <page> element");
|
|
|
|
StylePtr pStyle = pushStyle(attributes);
|
|
|
|
float width = static_cast<float>(_styles.current()->getDouble("width", box().width()));
|
|
float height = static_cast<float>(_styles.current()->getDouble("height", 0.2));
|
|
|
|
float x = static_cast<float>(_styles.current()->getDouble("left", _styles.current()->getDouble("right", width) - width));
|
|
float y = static_cast<float>(_styles.current()->getDouble("bottom", _styles.current()->getDouble("top", _y) - height));
|
|
|
|
translateInBox(x, y);
|
|
|
|
_pPage->moveTo(x, y);
|
|
_pPage->lineTo(x + width, y);
|
|
_pPage->stroke();
|
|
|
|
moveY(y);
|
|
}
|
|
|
|
void endHr()
|
|
{
|
|
popStyle();
|
|
}
|
|
|
|
// DocumentHandler
|
|
void startDocument()
|
|
{
|
|
}
|
|
|
|
void endDocument()
|
|
{
|
|
}
|
|
|
|
void startElement(const Poco::XML::XMLString& uri, const Poco::XML::XMLString& localName, const Poco::XML::XMLString& qname, const Poco::XML::Attributes& attributes)
|
|
{
|
|
if (localName == "document")
|
|
startDoc(attributes);
|
|
else if (localName == "page")
|
|
startPage(attributes);
|
|
else if (localName == "span")
|
|
startSpan(attributes);
|
|
else if (localName == "img")
|
|
startImg(attributes);
|
|
else if (localName == "table")
|
|
startTable(attributes);
|
|
else if (localName == "tr")
|
|
startTr(attributes);
|
|
else if (localName == "td")
|
|
startTd(attributes);
|
|
else if (localName == "hr")
|
|
startHr(attributes);
|
|
}
|
|
|
|
void endElement(const Poco::XML::XMLString& uri, const Poco::XML::XMLString& localName, const Poco::XML::XMLString& qname)
|
|
{
|
|
if (localName == "document")
|
|
endDoc();
|
|
else if (localName == "page")
|
|
endPage();
|
|
else if (localName == "span")
|
|
endSpan();
|
|
else if (localName == "img")
|
|
endImg();
|
|
else if (localName == "table")
|
|
endTable();
|
|
else if (localName == "tr")
|
|
endTr();
|
|
else if (localName == "td")
|
|
endTd();
|
|
else if (localName == "hr")
|
|
endHr();
|
|
}
|
|
|
|
void characters(const Poco::XML::XMLChar ch[], int start, int length)
|
|
{
|
|
_text.append(ch + start, length);
|
|
}
|
|
|
|
protected:
|
|
StylePtr pushStyle(const Poco::XML::Attributes& attributes)
|
|
{
|
|
StylePtr pStyle = parseStyle(attributes);
|
|
if (_boxes.size() > 0)
|
|
{
|
|
pStyle->setDouble("box.width", box().width());
|
|
pStyle->setDouble("box.height", box().height());
|
|
}
|
|
_styles.push(pStyle);
|
|
return pStyle;
|
|
}
|
|
|
|
void popStyle()
|
|
{
|
|
_styles.pop();
|
|
}
|
|
|
|
StylePtr parseStyle(const Poco::XML::Attributes& attributes)
|
|
{
|
|
return parseStyle(attributes.getValue("style"));
|
|
}
|
|
|
|
StylePtr parseStyle(const std::string& style) const
|
|
{
|
|
std::string props = Poco::translate(style, ";", "\n");
|
|
std::istringstream istr(props);
|
|
return new Poco::Util::PropertyFileConfiguration(istr);
|
|
}
|
|
|
|
static Page::Size parsePageSize(const std::string& size)
|
|
{
|
|
using Poco::icompare;
|
|
if (icompare(size, "letter") == 0)
|
|
return Page::PAGE_SIZE_LETTER;
|
|
else if (icompare(size, "legal") == 0)
|
|
return Page::PAGE_SIZE_LEGAL;
|
|
else if (icompare(size, "a3") == 0)
|
|
return Page::PAGE_SIZE_A3;
|
|
else if (icompare(size, "a4") == 0)
|
|
return Page::PAGE_SIZE_A4;
|
|
else if (icompare(size, "a5") == 0)
|
|
return Page::PAGE_SIZE_A5;
|
|
else if (icompare(size, "b4") == 0)
|
|
return Page::PAGE_SIZE_B4;
|
|
else if (icompare(size, "b5") == 0)
|
|
return Page::PAGE_SIZE_B5;
|
|
else if (icompare(size, "executive") == 0)
|
|
return Page::PAGE_SIZE_EXECUTIVE;
|
|
else if (icompare(size, "us4x6") == 0)
|
|
return Page::PAGE_SIZE_US4x6;
|
|
else if (icompare(size, "us4x8") == 0)
|
|
return Page::PAGE_SIZE_US4x8;
|
|
else if (icompare(size, "us5x7") == 0)
|
|
return Page::PAGE_SIZE_US5x7;
|
|
else if (icompare(size, "comm10") == 0)
|
|
return Page::PAGE_SIZE_COMM10;
|
|
else throw Poco::InvalidArgumentException("size", size);
|
|
}
|
|
|
|
static Page::Orientation parseOrientation(const std::string& orientation)
|
|
{
|
|
if (icompare(orientation, "portrait") == 0)
|
|
return Page::ORIENTATION_PORTRAIT;
|
|
else if (icompare(orientation, "landscape") == 0)
|
|
return Page::ORIENTATION_LANDSCAPE;
|
|
else throw Poco::InvalidArgumentException("orientation", orientation);
|
|
}
|
|
|
|
std::string normalizeFontName(const std::string& fontFamily)
|
|
{
|
|
poco_assert(fontFamily.size());
|
|
|
|
std::string fontName = toLower(fontFamily);
|
|
fontName[0] = Poco::Ascii::toUpper(fontName[0]);
|
|
return fontName;
|
|
}
|
|
|
|
Font loadFont(const std::string& fontFamily, const std::string& fontStyle, const std::string& fontWeight)
|
|
{
|
|
poco_assert(_pDocument);
|
|
|
|
std::string normalizedFontFamily = normalizeFontName(fontFamily);
|
|
std::string fontName;
|
|
if (fontStyle == "italic" || fontStyle == "oblique")
|
|
{
|
|
if (fontWeight == "bold")
|
|
fontName = boldItalicFontName(normalizedFontFamily);
|
|
else
|
|
fontName = italicFontName(normalizedFontFamily);
|
|
} else if (fontWeight == "bold")
|
|
{
|
|
fontName = boldFontName(normalizedFontFamily);
|
|
} else
|
|
{
|
|
fontName = normalizedFontFamily;
|
|
}
|
|
return _pDocument->font(fontName, _encoding);
|
|
}
|
|
|
|
std::string boldFontName(const std::string& fontFamily)
|
|
{
|
|
try
|
|
{
|
|
return _pDocument->font(fontFamily + "-Bold", _encoding).name();
|
|
} catch (...)
|
|
{
|
|
}
|
|
return fontFamily;
|
|
}
|
|
|
|
std::string italicFontName(const std::string& fontFamily)
|
|
{
|
|
try
|
|
{
|
|
return _pDocument->font(fontFamily + "-Oblique", _encoding).name();
|
|
} catch (...)
|
|
{
|
|
}
|
|
try
|
|
{
|
|
return _pDocument->font(fontFamily + "-Italic", _encoding).name();
|
|
} catch (...)
|
|
{
|
|
}
|
|
return fontFamily;
|
|
}
|
|
|
|
std::string boldItalicFontName(const std::string& fontFamily)
|
|
{
|
|
try
|
|
{
|
|
return _pDocument->font(fontFamily + "-BoldOblique", _encoding).name();
|
|
} catch (...)
|
|
{
|
|
}
|
|
try
|
|
{
|
|
Font font = _pDocument->font(fontFamily + "-BoldItalic", _encoding);
|
|
} catch (...)
|
|
{
|
|
}
|
|
return fontFamily;
|
|
}
|
|
|
|
Image loadImage(const std::string& path)
|
|
{
|
|
Poco::Path p(_base);
|
|
p.resolve(path);
|
|
if (Poco::icompare(p.getExtension(), "jpg") == 0 || icompare(p.getExtension(), "jpeg") == 0)
|
|
return _pDocument->loadJPEGImage(p.toString());
|
|
else if (Poco::icompare(p.getExtension(), "png") == 0)
|
|
return _pDocument->loadPNGImage(p.toString());
|
|
else
|
|
throw Poco::InvalidArgumentException("cannot determine image type", path);
|
|
}
|
|
|
|
void translateInBox(float& x, float& y)
|
|
{
|
|
if (x < 0)
|
|
x = box().right() + x;
|
|
else
|
|
x += box().left();
|
|
|
|
if (y < 0)
|
|
y = box().top() + y;
|
|
else
|
|
y += box().bottom();
|
|
}
|
|
|
|
void moveY(float y)
|
|
{
|
|
float padding = _styles.getFloat("padding", 0);
|
|
float newY = y - padding;
|
|
newY -= box().bottom();
|
|
|
|
_y = std::min(_y, newY);
|
|
}
|
|
|
|
const Box& box() const
|
|
{
|
|
poco_assert(_boxes.size() > 0);
|
|
return _boxes.back();
|
|
}
|
|
|
|
std::string transcode(const std::string& text)
|
|
{
|
|
std::string result;
|
|
Poco::UTF8Encoding inEncoding;
|
|
Poco::TextEncoding& outEncoding = Poco::TextEncoding::byName(mapEncoding(_encoding));
|
|
Poco::TextConverter converter(inEncoding, outEncoding);
|
|
converter.convert(text, result);
|
|
return result;
|
|
}
|
|
|
|
static std::string mapEncoding(const std::string& encoding)
|
|
{
|
|
if (encoding == "WinAnsiEncoding")
|
|
return "Latin-1";
|
|
else if (encoding == "ISO8859-2")
|
|
return "Latin-2";
|
|
else if (encoding == "ISO8859-15")
|
|
return "Latin-9";
|
|
else if (encoding == "CP1250")
|
|
return "CP1250";
|
|
else if (encoding == "CP1251")
|
|
return "CP1251";
|
|
else if (encoding == "CP1252")
|
|
return "CP1252";
|
|
else
|
|
throw Poco::InvalidArgumentException("PDF Document encoding not supported", encoding);
|
|
}
|
|
|
|
static std::string transform(const std::string& text, const std::string& trans)
|
|
{
|
|
if (trans == "uppercase")
|
|
return UTF8::toUpper(text);
|
|
else if (trans == "lowercase")
|
|
return UTF8::toLower(text);
|
|
else
|
|
return text;
|
|
}
|
|
|
|
private:
|
|
Poco::Path _base;
|
|
std::string _encoding;
|
|
Document* _pDocument;
|
|
Page* _pPage;
|
|
Table::Ptr _pTable;
|
|
TableRow _row;
|
|
StackedConfiguration _styles;
|
|
std::vector<Box> _boxes;
|
|
std::string _size;
|
|
std::string _orientation;
|
|
std::string _text;
|
|
float _y;
|
|
};
|
|
|
|
|
|
XMLTemplate::XMLTemplate(std::istream& xmlStream, const std::string& base) :
|
|
_base(base),
|
|
_pDocument(0)
|
|
{
|
|
load(xmlStream);
|
|
}
|
|
|
|
|
|
XMLTemplate::XMLTemplate(const std::string& path) :
|
|
_base(path),
|
|
_pDocument(0)
|
|
{
|
|
Poco::FileInputStream xmlStream(path);
|
|
load(xmlStream);
|
|
}
|
|
|
|
|
|
XMLTemplate::~XMLTemplate()
|
|
{
|
|
delete _pDocument;
|
|
}
|
|
|
|
|
|
void XMLTemplate::load(std::istream& xmlStream)
|
|
{
|
|
Poco::XML::InputSource xmlSource(xmlStream);
|
|
Poco::XML::SAXParser parser;
|
|
TemplateHandler handler(_base);
|
|
parser.setContentHandler(&handler);
|
|
parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, true);
|
|
parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACE_PREFIXES, true);
|
|
parser.parse(&xmlSource);
|
|
|
|
_pDocument = handler.document();
|
|
}
|
|
|
|
|
|
void XMLTemplate::create(const std::string& path)
|
|
{
|
|
_pDocument->save(path);
|
|
}
|
|
|
|
|
|
} } // namespace Poco::PDF
|