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 | |
36 | namespace Poco { |
37 | namespace PDF { |
38 | |
39 | |
40 | class StackedConfiguration : public Poco::Util::AbstractConfiguration |
41 | { |
42 | public: |
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 (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 | |
114 | private: |
115 | std::vector<ConfigPtr> _stack; |
116 | }; |
117 | |
118 | |
119 | class Box |
120 | { |
121 | public: |
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 | |
217 | private: |
218 | float _x; |
219 | float _y; |
220 | float _width; |
221 | float _height; |
222 | }; |
223 | |
224 | |
225 | class TemplateHandler : public Poco::XML::DefaultHandler |
226 | { |
227 | public: |
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 | |
587 | protected: |
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 | |
812 | private: |
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 | |
828 | XMLTemplate::XMLTemplate(std::istream& xmlStream, const std::string& base) : |
829 | _base(base), |
830 | _pDocument(0) |
831 | { |
832 | load(xmlStream); |
833 | } |
834 | |
835 | |
836 | XMLTemplate::XMLTemplate(const std::string& path) : |
837 | _base(path), |
838 | _pDocument(0) |
839 | { |
840 | Poco::FileInputStream xmlStream(path); |
841 | load(xmlStream); |
842 | } |
843 | |
844 | |
845 | XMLTemplate::~XMLTemplate() |
846 | { |
847 | delete _pDocument; |
848 | } |
849 | |
850 | |
851 | void 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 | |
865 | void XMLTemplate::create(const std::string& fileName) |
866 | { |
867 | _pDocument->save(fileName); |
868 | } |
869 | |
870 | |
871 | } } // namespace Poco::PDF |
872 | |