1//
2// XMLTemplate.cpp
3//
4// Library: PDF
5// Package: PDFCore
6// Module: XMLTemplate
7//
8// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/PDF/XMLTemplate.h"
16#include "Poco/PDF/Table.h"
17#include "Poco/SAX/SAXParser.h"
18#include "Poco/SAX/DefaultHandler.h"
19#include "Poco/SAX/Attributes.h"
20#include "Poco/SAX/InputSource.h"
21#include "Poco/Util/PropertyFileConfiguration.h"
22#include "Poco/FileStream.h"
23#include "Poco/AutoPtr.h"
24#include "Poco/String.h"
25#include "Poco/Exception.h"
26#include "Poco/Path.h"
27#include "Poco/TextConverter.h"
28#include "Poco/UTF8Encoding.h"
29#include "Poco/UTF8String.h"
30#include <vector>
31#include <set>
32#include <sstream>
33#include <algorithm>
34
35
36namespace Poco {
37namespace PDF {
38
39
40class StackedConfiguration : public Poco::Util::AbstractConfiguration
41{
42public:
43 typedef Poco::Util::AbstractConfiguration::Ptr ConfigPtr;
44 typedef std::vector<ConfigPtr> ConfigStack;
45
46 void push(ConfigPtr pConfig)
47 {
48 _stack.push_back(pConfig);
49 }
50
51 void pop()
52 {
53 _stack.pop_back();
54 }
55
56 ConfigPtr current() const
57 {
58 poco_assert(_stack.size() > 0);
59 return _stack.back();
60 }
61
62 float getFloat(const std::string& value)
63 {
64 return static_cast<float>(getDouble(value));
65 }
66
67 float getFloat(const std::string& value, float deflt)
68 {
69 return static_cast<float>(getDouble(value, deflt));
70 }
71
72 // AbstractConfiguration
73 bool getRaw(const std::string& key, std::string& value) const
74 {
75 for (ConfigStack::const_reverse_iterator it = _stack.rbegin(); it != _stack.rend(); ++it)
76 {
77 if ((*it)->has(key))
78 {
79 value = (*it)->getRawString(key);
80 return true;
81 }
82 }
83 return false;
84 }
85
86 void setRaw(const std::string& key, const std::string& value)
87 {
88 throw Poco::InvalidAccessException("not writable");
89 }
90
91 void enumerate(const std::string& key, Poco::Util::AbstractConfiguration::Keys& range) const
92 {
93 std::set<std::string> keys;
94 for (ConfigStack::const_iterator itc = _stack.begin(); itc != _stack.end(); ++itc)
95 {
96 Poco::Util::AbstractConfiguration::Keys partRange;
97 (*itc)->keys(key, partRange);
98 for (Poco::Util::AbstractConfiguration::Keys::const_iterator itr = partRange.begin(); itr != partRange.end(); ++itr)
99 {
100 if (keys.find(*itr) == keys.end())
101 {
102 range.push_back(*itr);
103 keys.insert(*itr);
104 }
105 }
106 }
107 }
108
109 void removeRaw(const std::string& key)
110 {
111 throw Poco::InvalidAccessException("not writable");
112 }
113
114private:
115 std::vector<ConfigPtr> _stack;
116};
117
118
119class Box
120{
121public:
122 Box() :
123 _x(0),
124 _y(0),
125 _width(0),
126 _height(0)
127 {
128 }
129
130 Box(float x, float y, float width, float height) :
131 _x(x),
132 _y(y),
133 _width(width),
134 _height(height)
135 {
136
137 }
138
139 Box(const Box& box) :
140 _x(box._x),
141 _y(box._y),
142 _width(box._width),
143 _height(box._height)
144 {
145 }
146
147 ~Box()
148 {
149 }
150
151 Box& operator = (const Box& box)
152 {
153 Box tmp(box);
154 tmp.swap(*this);
155 return *this;
156 }
157
158 void swap(Box& box)
159 {
160 std::swap(_x, box._x);
161 std::swap(_y, box._y);
162 std::swap(_width, box._width);
163 std::swap(_height, box._height);
164 }
165
166 float left() const
167 {
168 return _x;
169 }
170
171 float right() const
172 {
173 return _x + _width;
174 }
175
176 float bottom() const
177 {
178 return _y;
179 }
180
181 float top() const
182 {
183 return _y + _height;
184 }
185
186 float width() const
187 {
188 return _width;
189 }
190
191 float height() const
192 {
193 return _height;
194 }
195
196 void inset(float delta)
197 {
198 _x += delta;
199 _y -= delta;
200 _width -= 2 * delta;
201 _height -= 2 * delta;
202 }
203
204 void extend(float delta)
205 {
206 inset(-delta);
207 }
208
209 void inset(float left, float right, float top, float bottom)
210 {
211 _x += left;
212 _y += bottom;
213 _width -= (left + right);
214 _height -= (top + bottom);
215 }
216
217private:
218 float _x;
219 float _y;
220 float _width;
221 float _height;
222};
223
224
225class TemplateHandler : public Poco::XML::DefaultHandler
226{
227public:
228 typedef Poco::AutoPtr<Poco::Util::AbstractConfiguration> StylePtr;
229
230 TemplateHandler(const Poco::Path& base) :
231 _base(base),
232 _pDocument(0),
233 _pPage(0),
234 _y(0)
235 {
236 _styles.push(parseStyle("font-family: Helvetica; font-size: 12; line-height: 1.2"));
237 }
238
239 ~TemplateHandler()
240 {
241 _styles.pop();
242 delete _pPage;
243 delete _pDocument;
244 }
245
246 Document* document()
247 {
248 Document* pDocument = _pDocument;
249 _pDocument = 0;
250 return pDocument;
251 }
252
253 void startDoc(const Poco::XML::Attributes& attributes)
254 {
255 if (_pDocument) throw Poco::IllegalStateException("only one <document> element is allowed");
256
257 StylePtr pStyle = pushStyle(attributes);
258
259 _size = attributes.getValue("size");
260 if (_size.empty()) _size = "A4";
261 _orientation = attributes.getValue("orientation");
262 if (_orientation.empty()) _orientation = "portrait";
263
264 _encoding = attributes.getValue("encoding");
265 if (_encoding.empty()) _encoding = "WinAnsiEncoding";
266
267 _pDocument = new Document(0, parsePageSize(_size), parseOrientation(_orientation));
268 }
269
270 void endDoc()
271 {
272 popStyle();
273 }
274
275 void startPage(const Poco::XML::Attributes& attributes)
276 {
277 if (!_pDocument) throw Poco::IllegalStateException("missing <document> element");
278 if (_pPage) throw Poco::IllegalStateException("nested <page> elements are not allowed");
279
280 StylePtr pStyle = pushStyle(attributes);
281
282 std::string size = attributes.getValue("size");
283 if (size.empty()) size = _size;
284 std::string orientation = attributes.getValue("orientation");
285 if (orientation.empty()) orientation = _orientation;
286
287 _pPage = new Page(_pDocument->addPage(parsePageSize(_size), parseOrientation(orientation)));
288 _pPage->setLineWidth(0.2f);
289 _pPage->setRGBStroke({ 0, 0, 0 });
290 _boxes.push_back(Box(0, 0, _pPage->getWidth(), _pPage->getHeight()));
291
292 float margin = _styles.getFloat("margin", 0);
293 float marginLeft = _styles.getFloat("margin-left", margin);
294 float marginRight = _styles.getFloat("margin-right", margin);
295 float marginTop = _styles.getFloat("margin-top", margin);
296 float marginBottom = _styles.getFloat("margin-bottom", margin);
297
298 _boxes.back().inset(marginLeft, marginRight, marginTop, marginBottom);
299
300 _y = _boxes.back().top();
301 }
302
303 void endPage()
304 {
305 _boxes.pop_back();
306 delete _pPage;
307 _pPage = 0;
308 popStyle();
309 }
310
311 void startSpan(const Poco::XML::Attributes& attributes)
312 {
313 if (!_pPage) throw Poco::IllegalStateException("missing <page> element");
314
315 StylePtr pStyle = pushStyle(attributes);
316
317 _text.clear();
318 }
319
320 void endSpan()
321 {
322 std::string fontFamily = _styles.getString("font-family");
323 float fontSize = _styles.getFloat("font-size");
324 float lineHeight = _styles.getFloat("line-height");
325 std::string textAlign = _styles.getString("text-align", "left");
326 std::string fontStyle = _styles.getString("font-style", "normal");
327 std::string fontWeight = _styles.getString("font-weight", "normal");
328 std::string textTransform = _styles.getString("text-transform", "none");
329
330 _text = transform(_text, textTransform);
331 _text = transcode(_text);
332
333 Font font = loadFont(fontFamily, fontStyle, fontWeight);
334
335 _pPage->setFont(font, fontSize);
336
337 float width = static_cast<float>(font.textWidth(_text).width*fontSize / 1000);
338 float height = static_cast<float>(font.upperHeight()*fontSize / 1000)*lineHeight;
339
340 float x = static_cast<float>(_styles.current()->getDouble("left", _styles.current()->getDouble("right", width) - width));
341 float y = static_cast<float>(_styles.current()->getDouble("bottom", _styles.current()->getDouble("top", _y) - height));
342
343 if (textAlign == "center")
344 x = (box().width() - width) / 2;
345 else if (textAlign == "right")
346 x = box().width() - width;
347
348 translateInBox(x, y);
349
350 _pPage->writeOnce(x, y, _text);
351
352 moveY(y);
353
354 popStyle();
355 }
356
357 void startImg(const Poco::XML::Attributes& attributes)
358 {
359 if (!_pPage) throw Poco::IllegalStateException("missing <page> element");
360
361 StylePtr pStyle = pushStyle(attributes);
362
363 std::string path = attributes.getValue("src");
364 Image image = loadImage(path);
365
366 float width = static_cast<float>(pStyle->getDouble("width", image.width()));
367 float height = static_cast<float>(pStyle->getDouble("height", image.height()));
368
369 float scale = static_cast<float>(pStyle->getDouble("scale", 1.0));
370 float scaleX = static_cast<float>(pStyle->getDouble("scale-x", scale));
371 float scaleY = static_cast<float>(pStyle->getDouble("scale-y", scale));
372
373 width *= scaleX;
374 height *= scaleY;
375
376 bool aspectratioH = static_cast<bool>(pStyle->getBool("aspectratioH", false));
377 if (aspectratioH) {
378 float fAspectRation = image.width()/ image.height();
379 width = fAspectRation*height;
380 }
381
382 float x = static_cast<float>(_styles.current()->getDouble("left", _styles.current()->getDouble("right", width) - width));
383 float y = static_cast<float>(_styles.current()->getDouble("bottom", _styles.current()->getDouble("top", _y) - height));
384
385 float offsetX = static_cast<float>(pStyle->getDouble("offsetX", 0.0F));
386 float offsetY = static_cast<float>(pStyle->getDouble("offsetY", 0.0F));
387
388 x += offsetX;
389 y += offsetY;
390
391 translateInBox(x, y);
392
393 _pPage->drawImage(image, x, y, width, height);
394
395 moveY(y);
396 }
397
398 void endImg()
399 {
400 popStyle();
401 }
402
403 void startTable(const Poco::XML::Attributes& attributes)
404 {
405 if (!_pPage) throw Poco::IllegalStateException("missing <page> element");
406
407 StylePtr pStyle = pushStyle(attributes);
408
409 _pTable = new Table(0, 0, attributes.getValue("name"));
410 }
411
412 void endTable()
413 {
414 if (_pTable->rows() > 0)
415 {
416 std::string fontFamily = _styles.getString("font-family");
417 float fontSize = _styles.getFloat("font-size");
418 std::string fontStyle = _styles.getString("font-style", "normal");
419 std::string fontWeight = _styles.getString("font-weight", "normal");
420
421 Font font = loadFont(fontFamily, fontStyle, fontWeight);
422
423 float lineHeight = static_cast<float>((font.ascent() - font.descent())*fontSize / 1000)*_styles.getFloat("line-height");
424
425 float width = static_cast<float>(_styles.current()->getDouble("width", box().width()));
426 float height = static_cast<float>(_styles.current()->getDouble("height", _pTable->rows()*lineHeight));
427
428 float x = static_cast<float>(_styles.current()->getDouble("left", _styles.current()->getDouble("right", width) - width));
429 float y = static_cast<float>(_styles.current()->getDouble("bottom", _styles.current()->getDouble("top", _y) - height) + height);
430 y -= lineHeight;
431
432 translateInBox(x, y);
433
434 _pTable->draw(*_pPage, x, y, width, height);
435
436 y -= height - lineHeight;
437
438 moveY(y);
439 }
440 _pTable = 0;
441 popStyle();
442 }
443
444 void startTr(const Poco::XML::Attributes& attributes)
445 {
446 if (!_pTable) throw Poco::IllegalStateException("missing <table> element");
447
448 StylePtr pStyle = pushStyle(attributes);
449 _row.clear();
450 }
451
452 void endTr()
453 {
454 _pTable->addRow(_row);
455 _row.clear();
456 popStyle();
457 }
458
459 void startTd(const Poco::XML::Attributes& attributes)
460 {
461 StylePtr pStyle = pushStyle(attributes);
462
463 _text.clear();
464 }
465
466 void endTd()
467 {
468 AttributedString::Alignment align = AttributedString::ALIGN_LEFT;
469 int style = AttributedString::STYLE_PLAIN;
470
471 std::string fontFamily = _styles.getString("font-family");
472 float fontSize = _styles.getFloat("font-size");
473 std::string textAlign = _styles.getString("text-align", "left");
474 std::string fontStyle = _styles.getString("font-style", "normal");
475 std::string fontWeight = _styles.getString("font-weight", "normal");
476 std::string textTransform = _styles.getString("text-transform", "none");
477
478 _text = transform(_text, textTransform);
479 _text = transcode(_text);
480
481 if (textAlign == "right")
482 align = AttributedString::ALIGN_RIGHT;
483 else if (textAlign == "left")
484 align = AttributedString::ALIGN_LEFT;
485
486 if (fontStyle == "italic" || fontStyle == "oblique")
487 style |= AttributedString::STYLE_ITALIC;
488
489 if (fontWeight == "bold")
490 style |= AttributedString::STYLE_BOLD;
491
492 AttributedString content(_text, align, style);
493 content.setAttribute(AttributedString::ATTR_SIZE, fontSize);
494
495 Cell::FontMapPtr pFontMap = new Cell::FontMap;
496 std::string normalizedFontFamily(normalizeFontName(fontFamily));
497 (*pFontMap)[AttributedString::STYLE_PLAIN] = normalizedFontFamily;
498 (*pFontMap)[AttributedString::STYLE_BOLD] = boldFontName(normalizedFontFamily);
499 (*pFontMap)[AttributedString::STYLE_ITALIC] = italicFontName(normalizedFontFamily);
500 (*pFontMap)[AttributedString::STYLE_BOLD | AttributedString::STYLE_ITALIC] = boldItalicFontName(normalizedFontFamily);
501
502 _row.push_back(Cell(content, pFontMap, _encoding, false));
503
504 popStyle();
505 }
506
507 void startHr(const Poco::XML::Attributes& attributes)
508 {
509 if (!_pPage) throw Poco::IllegalStateException("missing <page> element");
510
511 StylePtr pStyle = pushStyle(attributes);
512
513 float width = static_cast<float>(_styles.current()->getDouble("width", box().width()));
514 float height = static_cast<float>(_styles.current()->getDouble("height", 0.2));
515
516 float x = static_cast<float>(_styles.current()->getDouble("left", _styles.current()->getDouble("right", width) - width));
517 float y = static_cast<float>(_styles.current()->getDouble("bottom", _styles.current()->getDouble("top", _y) - height));
518
519 translateInBox(x, y);
520
521 _pPage->moveTo(x, y);
522 _pPage->lineTo(x + width, y);
523 _pPage->stroke();
524
525 moveY(y);
526 }
527
528 void endHr()
529 {
530 popStyle();
531 }
532
533 // DocumentHandler
534 void startDocument()
535 {
536 }
537
538 void endDocument()
539 {
540 }
541
542 void startElement(const Poco::XML::XMLString& uri, const Poco::XML::XMLString& localName, const Poco::XML::XMLString& qname, const Poco::XML::Attributes& attributes)
543 {
544 if (localName == "document")
545 startDoc(attributes);
546 else if (localName == "page")
547 startPage(attributes);
548 else if (localName == "span")
549 startSpan(attributes);
550 else if (localName == "img")
551 startImg(attributes);
552 else if (localName == "table")
553 startTable(attributes);
554 else if (localName == "tr")
555 startTr(attributes);
556 else if (localName == "td")
557 startTd(attributes);
558 else if (localName == "hr")
559 startHr(attributes);
560 }
561
562 void endElement(const Poco::XML::XMLString& uri, const Poco::XML::XMLString& localName, const Poco::XML::XMLString& qname)
563 {
564 if (localName == "document")
565 endDoc();
566 else if (localName == "page")
567 endPage();
568 else if (localName == "span")
569 endSpan();
570 else if (localName == "img")
571 endImg();
572 else if (localName == "table")
573 endTable();
574 else if (localName == "tr")
575 endTr();
576 else if (localName == "td")
577 endTd();
578 else if (localName == "hr")
579 endHr();
580 }
581
582 void characters(const Poco::XML::XMLChar ch[], int start, int length)
583 {
584 _text.append(ch + start, length);
585 }
586
587protected:
588 StylePtr pushStyle(const Poco::XML::Attributes& attributes)
589 {
590 StylePtr pStyle = parseStyle(attributes);
591 if (_boxes.size() > 0)
592 {
593 pStyle->setDouble("box.width", box().width());
594 pStyle->setDouble("box.height", box().height());
595 }
596 _styles.push(pStyle);
597 return pStyle;
598 }
599
600 void popStyle()
601 {
602 _styles.pop();
603 }
604
605 StylePtr parseStyle(const Poco::XML::Attributes& attributes)
606 {
607 return parseStyle(attributes.getValue("style"));
608 }
609
610 StylePtr parseStyle(const std::string& style) const
611 {
612 std::string props = Poco::translate(style, ";", "\n");
613 std::istringstream istr(props);
614 return new Poco::Util::PropertyFileConfiguration(istr);
615 }
616
617 static Page::Size parsePageSize(const std::string& size)
618 {
619 using Poco::icompare;
620 if (icompare(size, "letter") == 0)
621 return Page::PAGE_SIZE_LETTER;
622 else if (icompare(size, "legal") == 0)
623 return Page::PAGE_SIZE_LEGAL;
624 else if (icompare(size, "a3") == 0)
625 return Page::PAGE_SIZE_A3;
626 else if (icompare(size, "a4") == 0)
627 return Page::PAGE_SIZE_A4;
628 else if (icompare(size, "a5") == 0)
629 return Page::PAGE_SIZE_A5;
630 else if (icompare(size, "b4") == 0)
631 return Page::PAGE_SIZE_B4;
632 else if (icompare(size, "b5") == 0)
633 return Page::PAGE_SIZE_B5;
634 else if (icompare(size, "executive") == 0)
635 return Page::PAGE_SIZE_EXECUTIVE;
636 else if (icompare(size, "us4x6") == 0)
637 return Page::PAGE_SIZE_US4x6;
638 else if (icompare(size, "us4x8") == 0)
639 return Page::PAGE_SIZE_US4x8;
640 else if (icompare(size, "us5x7") == 0)
641 return Page::PAGE_SIZE_US5x7;
642 else if (icompare(size, "comm10") == 0)
643 return Page::PAGE_SIZE_COMM10;
644 else throw Poco::InvalidArgumentException("size", size);
645 }
646
647 static Page::Orientation parseOrientation(const std::string& orientation)
648 {
649 if (icompare(orientation, "portrait") == 0)
650 return Page::ORIENTATION_PORTRAIT;
651 else if (icompare(orientation, "landscape") == 0)
652 return Page::ORIENTATION_LANDSCAPE;
653 else throw Poco::InvalidArgumentException("orientation", orientation);
654 }
655
656 std::string normalizeFontName(const std::string& fontFamily)
657 {
658 poco_assert(fontFamily.size());
659
660 std::string fontName = toLower(fontFamily);
661 fontName[0] = Poco::Ascii::toUpper(fontName[0]);
662 return fontName;
663 }
664
665 Font loadFont(const std::string& fontFamily, const std::string& fontStyle, const std::string& fontWeight)
666 {
667 poco_assert(_pDocument);
668
669 std::string normalizedFontFamily = normalizeFontName(fontFamily);
670 std::string fontName;
671 if (fontStyle == "italic" || fontStyle == "oblique")
672 {
673 if (fontWeight == "bold")
674 fontName = boldItalicFontName(normalizedFontFamily);
675 else
676 fontName = italicFontName(normalizedFontFamily);
677 } else if (fontWeight == "bold")
678 {
679 fontName = boldFontName(normalizedFontFamily);
680 } else
681 {
682 fontName = normalizedFontFamily;
683 }
684 return _pDocument->font(fontName, _encoding);
685 }
686
687 std::string boldFontName(const std::string& fontFamily)
688 {
689 try
690 {
691 return _pDocument->font(fontFamily + "-Bold", _encoding).name();
692 } catch (...)
693 {
694 }
695 return fontFamily;
696 }
697
698 std::string italicFontName(const std::string& fontFamily)
699 {
700 try
701 {
702 return _pDocument->font(fontFamily + "-Oblique", _encoding).name();
703 } catch (...)
704 {
705 }
706 try
707 {
708 return _pDocument->font(fontFamily + "-Italic", _encoding).name();
709 } catch (...)
710 {
711 }
712 return fontFamily;
713 }
714
715 std::string boldItalicFontName(const std::string& fontFamily)
716 {
717 try
718 {
719 return _pDocument->font(fontFamily + "-BoldOblique", _encoding).name();
720 } catch (...)
721 {
722 }
723 try
724 {
725 Font font = _pDocument->font(fontFamily + "-BoldItalic", _encoding);
726 } catch (...)
727 {
728 }
729 return fontFamily;
730 }
731
732 Image loadImage(const std::string& path)
733 {
734 Poco::Path p(_base);
735 p.resolve(path);
736 if (Poco::icompare(p.getExtension(), "jpg") == 0 || icompare(p.getExtension(), "jpeg") == 0)
737 return _pDocument->loadJPEGImage(p.toString());
738 else if (Poco::icompare(p.getExtension(), "png") == 0)
739 return _pDocument->loadPNGImage(p.toString());
740 else if (Poco::icompare(p.getExtension(), "bmp") == 0)
741 return _pDocument->loadBMPImage(p.toString());
742 else
743 throw Poco::InvalidArgumentException("cannot determine image type", path);
744 }
745
746 void translateInBox(float& x, float& y)
747 {
748 if (x < 0)
749 x = box().right() + x;
750 else
751 x += box().left();
752
753 if (y < 0)
754 y = box().top() + y;
755 else
756 y += box().bottom();
757 }
758
759 void moveY(float y)
760 {
761 float padding = _styles.getFloat("padding", 0);
762 float newY = y - padding;
763 newY -= box().bottom();
764
765 _y = std::min(_y, newY);
766 }
767
768 const Box& box() const
769 {
770 poco_assert(_boxes.size() > 0);
771 return _boxes.back();
772 }
773
774 std::string transcode(const std::string& text)
775 {
776 std::string result;
777 Poco::UTF8Encoding inEncoding;
778 Poco::TextEncoding& outEncoding = Poco::TextEncoding::byName(mapEncoding(_encoding));
779 Poco::TextConverter converter(inEncoding, outEncoding);
780 converter.convert(text, result);
781 return result;
782 }
783
784 static std::string mapEncoding(const std::string& encoding)
785 {
786 if (encoding == "WinAnsiEncoding")
787 return "Latin-1";
788 else if (encoding == "ISO8859-2")
789 return "Latin-2";
790 else if (encoding == "ISO8859-15")
791 return "Latin-9";
792 else if (encoding == "CP1250")
793 return "CP1250";
794 else if (encoding == "CP1251")
795 return "CP1251";
796 else if (encoding == "CP1252")
797 return "CP1252";
798 else
799 throw Poco::InvalidArgumentException("PDF Document encoding not supported", encoding);
800 }
801
802 static std::string transform(const std::string& text, const std::string& trans)
803 {
804 if (trans == "uppercase")
805 return UTF8::toUpper(text);
806 else if (trans == "lowercase")
807 return UTF8::toLower(text);
808 else
809 return text;
810 }
811
812private:
813 Poco::Path _base;
814 std::string _encoding;
815 Document* _pDocument;
816 Page* _pPage;
817 Table::Ptr _pTable;
818 TableRow _row;
819 StackedConfiguration _styles;
820 std::vector<Box> _boxes;
821 std::string _size;
822 std::string _orientation;
823 std::string _text;
824 float _y;
825};
826
827
828XMLTemplate::XMLTemplate(std::istream& xmlStream, const std::string& base) :
829 _base(base),
830 _pDocument(0)
831{
832 load(xmlStream);
833}
834
835
836XMLTemplate::XMLTemplate(const std::string& path) :
837 _base(path),
838 _pDocument(0)
839{
840 Poco::FileInputStream xmlStream(path);
841 load(xmlStream);
842}
843
844
845XMLTemplate::~XMLTemplate()
846{
847 delete _pDocument;
848}
849
850
851void XMLTemplate::load(std::istream& xmlStream)
852{
853 Poco::XML::InputSource xmlSource(xmlStream);
854 Poco::XML::SAXParser parser;
855 TemplateHandler handler(_base);
856 parser.setContentHandler(&handler);
857 parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, true);
858 parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACE_PREFIXES, true);
859 parser.parse(&xmlSource);
860
861 _pDocument = handler.document();
862}
863
864
865void XMLTemplate::create(const std::string& fileName)
866{
867 _pDocument->save(fileName);
868}
869
870
871} } // namespace Poco::PDF
872