1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3 * This file is part of the libqxp project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10 #include "QXP4Parser.h"
11
12 #include <librevenge-stream/librevenge-stream.h>
13 #include <memory>
14
15 #include "QXP4Deobfuscator.h"
16 #include "QXP4Header.h"
17 #include "QXPCollector.h"
18
19 namespace libqxp
20 {
21
22 using librevenge::RVNGStringStream;
23 using std::make_shared;
24 using std::shared_ptr;
25
26 namespace
27 {
28
29 template<typename T>
createBox(const QXP4Parser::ObjectHeader & header)30 shared_ptr<T> createBox(const QXP4Parser::ObjectHeader &header)
31 {
32 auto box = make_shared<T>();
33 box->cornerType = header.cornerType;
34 box->boxType = header.boxType;
35 box->rotation = header.rotation;
36 box->fill = header.fillColor;
37 return box;
38 }
39
40 template<typename T>
createLine(const QXP4Parser::ObjectHeader & header)41 shared_ptr<T> createLine(const QXP4Parser::ObjectHeader &header)
42 {
43 auto line = make_shared<T>();
44 line->rotation = header.rotation;
45 return line;
46 }
47
48 }
49
QXP4Parser(const std::shared_ptr<librevenge::RVNGInputStream> & input,librevenge::RVNGDrawingInterface * painter,const std::shared_ptr<QXP4Header> & header)50 QXP4Parser::QXP4Parser(const std::shared_ptr<librevenge::RVNGInputStream> &input, librevenge::RVNGDrawingInterface *painter, const std::shared_ptr<QXP4Header> &header)
51 : QXPParser(input, painter, header)
52 , m_header(header)
53 , m_paragraphTabStops()
54 {
55 }
56
parseDocument(const std::shared_ptr<librevenge::RVNGInputStream> & docStream,QXPCollector & collector)57 bool QXP4Parser::parseDocument(const std::shared_ptr<librevenge::RVNGInputStream> &docStream, QXPCollector &collector)
58 {
59 collector.collectDocumentProperties(m_header->documentProperties());
60
61 for (int i = 0; i < 5; ++i)
62 {
63 skipRecord(docStream);
64 }
65
66 parseFonts(docStream);
67
68 skipRecord(docStream);
69
70 parseColors(docStream);
71
72 // don't need stylesheets, everything is included in the current style
73 skipParagraphStylesheets(docStream);
74 skipRecord(docStream);
75
76 parseHJs(docStream);
77
78 parseLineStyles(docStream);
79
80 skipRecord(docStream);
81
82 skipTemplates(docStream);
83
84 parseCharFormats(docStream);
85
86 parseTabStops(docStream);
87
88 parseParagraphFormats(docStream);
89
90 skipRecord(docStream);
91
92 return true;
93 }
94
parsePages(const std::shared_ptr<librevenge::RVNGInputStream> & stream,QXPCollector & collector)95 bool QXP4Parser::parsePages(const std::shared_ptr<librevenge::RVNGInputStream> &stream, QXPCollector &collector)
96 {
97 QXP4Deobfuscator deobfuscate(m_header->seed(), m_header->increment());
98 QXPDummyCollector dummyCollector;
99
100 unsigned ind = 0;
101 while (ind < m_header->pagesCount() + m_header->masterPagesCount())
102 {
103 // don't output master pages, everything is included in normal pages
104 QXPCollector &coll = ind < m_header->masterPagesCount() ? dummyCollector : collector;
105
106 auto page = parsePage(stream, deobfuscate);
107 coll.startPage(page);
108 deobfuscate.nextRev();
109
110 for (unsigned i = 0; i < page.objectsCount; ++i)
111 {
112 parseObject(stream, deobfuscate, coll, page);
113 }
114
115 coll.endPage();
116
117 ind++;
118 }
119
120 return true;
121 }
122
parseColors(const std::shared_ptr<librevenge::RVNGInputStream> & docStream)123 void QXP4Parser::parseColors(const std::shared_ptr<librevenge::RVNGInputStream> &docStream)
124 {
125 unsigned length = readU32(docStream, be);
126 auto stream = make_shared<RVNGStringStream>(readNBytes(docStream, length), length);
127
128 try
129 {
130 skip(stream, 14);
131 const unsigned blocksCount = readU16(stream, be);
132 if (blocksCount == 0 || blocksCount * 4 > length)
133 {
134 QXP_DEBUG_MSG(("Invalid number of blocks %u\n", blocksCount));
135 return;
136 }
137 skip(stream, 20);
138 std::vector<ColorBlockSpec> blocks;
139 blocks.resize(blocksCount + 1); // first dummy block
140 for (unsigned i = 1; i <= blocksCount; ++i)
141 {
142 blocks[i] = parseColorBlockSpec(stream);
143 }
144
145 for (unsigned i = 2; i <= blocksCount; ++i)
146 {
147 seek(stream, blocks[i].offset);
148 if (readU16(stream, be) + readU16(stream, be) == 6) // 04 02
149 {
150 parseColor(stream, blocks);
151 }
152 }
153 }
154 catch (...)
155 {
156 QXP_DEBUG_MSG(("Failed to parse colors, offset %ld\n", stream->tell()));
157 }
158 }
159
parseColorBlockSpec(const std::shared_ptr<librevenge::RVNGInputStream> & stream)160 QXP4Parser::ColorBlockSpec QXP4Parser::parseColorBlockSpec(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
161 {
162 const uint32_t info = readU32(stream, be);
163 ColorBlockSpec spec;
164 spec.offset = info & 0xFFFFFFF;
165 spec.padding = (info >> 28) & 0x7;
166 return spec;
167 }
168
parseColor(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const std::vector<ColorBlockSpec> & blocks)169 void QXP4Parser::parseColor(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const std::vector<ColorBlockSpec> &blocks)
170 {
171 skip(stream, 30);
172 unsigned id = readU16(stream, be);
173
174 skip(stream, 70);
175 unsigned rgbBlockInd = readU16(stream, be);
176
177 if (rgbBlockInd != 0)
178 {
179 if (rgbBlockInd >= blocks.size())
180 {
181 QXP_DEBUG_MSG(("RGB block %u not found\n", rgbBlockInd));
182 return;
183 }
184 seek(stream, blocks[rgbBlockInd].offset + 16);
185 Color color;
186 color.red = readColorComp(stream);
187 color.green = readColorComp(stream);
188 color.blue = readColorComp(stream);
189 m_colors[id] = color;
190 }
191 }
192
skipParagraphStylesheets(const std::shared_ptr<librevenge::RVNGInputStream> & stream)193 void QXP4Parser::skipParagraphStylesheets(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
194 {
195 const unsigned end = readRecordEndOffset(stream);
196
197 unsigned tabRecordsCount = 0;
198 while (stream->tell() < end)
199 {
200 skip(stream, 90);
201 const unsigned tabsCount = readU16(stream, be);
202 if (tabsCount > 0)
203 {
204 tabRecordsCount++;
205 }
206 skip(stream, 152);
207 }
208
209 seek(stream, end);
210
211 for (unsigned i = 0; i < tabRecordsCount; ++i)
212 {
213 skipRecord(stream);
214 }
215 }
216
parseCharFormat(const std::shared_ptr<librevenge::RVNGInputStream> & stream)217 CharFormat QXP4Parser::parseCharFormat(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
218 {
219 skip(stream, 8);
220
221 CharFormat result;
222 parseCommonCharFormatProps(stream, result);
223
224 skip(stream, 4);
225
226 const unsigned colorId = readU16(stream, be);
227
228 skip(stream, 2);
229
230 const double shade = readFraction(stream, be);
231 result.color = getColor(colorId).applyShade(shade);
232
233 skip(stream, 8);
234 result.baselineShift = readFraction(stream, be);
235
236 result.isControlChars = readU8(stream) != 0;
237
238 skip(stream, 23);
239
240 return result;
241 }
242
parseLineStyles(const std::shared_ptr<librevenge::RVNGInputStream> & stream)243 void QXP4Parser::parseLineStyles(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
244 {
245 parseCollection(stream, [=]()
246 {
247 const unsigned long start = stream->tell();
248 const unsigned long end = start + 252;
249
250 try
251 {
252 skip(stream, 168);
253
254 const unsigned id = readU16(stream, be);
255 auto &result = (m_lineStyles[id] = LineStyle());
256
257 result.isStripe = readU8(stream) == 1;
258 skip(stream, 1);
259 const unsigned segmentsCount = readU16(stream, be);
260 if (segmentsCount > 42)
261 {
262 QXP_DEBUG_MSG(("Invalid line style segments count %u\n", segmentsCount));
263 throw ParseError();
264 }
265 result.isProportional = readU8(stream) == 1;
266 skip(stream, 69);
267 result.patternLength = readFraction(stream, be);
268
269 const unsigned miter = readU16(stream, be);
270 switch (miter)
271 {
272 default:
273 QXP_DEBUG_MSG(("Unknown line join type %u\n", miter));
274 QXP_FALLTHROUGH; // pick a default
275 case 0:
276 result.joinType = LineJoinType::MITER;
277 break;
278 case 1:
279 result.joinType = LineJoinType::ROUND;
280 break;
281 case 2:
282 result.joinType = LineJoinType::BEVEL;
283 break;
284 }
285
286 const unsigned endcap = readU16(stream, be);
287 switch (endcap)
288 {
289 default:
290 QXP_DEBUG_MSG(("Unknown line cap type %u\n", endcap));
291 QXP_FALLTHROUGH; // pick a default
292 case 0:
293 result.endcapType = LineCapType::BUTT;
294 break;
295 case 1:
296 result.endcapType = LineCapType::ROUND;
297 break;
298 case 2:
299 result.endcapType = LineCapType::RECT;
300 break;
301 case 3:
302 result.endcapType = LineCapType::STRETCH;
303 break;
304 }
305
306 seek(stream, start);
307 result.segmentLengths.resize(segmentsCount);
308 for (double &segmentLength : result.segmentLengths)
309 {
310 segmentLength = readFraction(stream, be);
311 }
312 }
313 catch (...)
314 {
315 QXP_DEBUG_MSG(("Failed to parse line style, offset %ld\n", stream->tell()));
316 }
317
318 seek(stream, end);
319 });
320 }
321
skipTemplates(const std::shared_ptr<librevenge::RVNGInputStream> & stream)322 void QXP4Parser::skipTemplates(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
323 {
324 const unsigned indexEnd = readRecordEndOffset(stream);
325
326 const unsigned count = readU32(stream, be);
327
328 seek(stream, indexEnd);
329
330 for (unsigned i = 0; i < count; ++i)
331 {
332 skipRecord(stream);
333 }
334 }
335
parseTabStops(const std::shared_ptr<librevenge::RVNGInputStream> & stream)336 void QXP4Parser::parseTabStops(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
337 {
338 const unsigned specLength = readU32(stream, be);
339 if (specLength > getLength(stream))
340 {
341 QXP_DEBUG_MSG(("Invalid tab stop spec length %u\n", specLength));
342 throw ParseError();
343 }
344
345 std::vector<unsigned> tabStopsCounts;
346 tabStopsCounts.resize(specLength / 8);
347 for (auto countIt = tabStopsCounts.rbegin(); countIt != tabStopsCounts.rend(); ++countIt)
348 {
349 skip(stream, 2);
350 const unsigned count = readU16(stream, be);
351 if (count > getLength(stream) / 8)
352 {
353 QXP_DEBUG_MSG(("Invalid tab stop count %u\n", count));
354 throw ParseError();
355 }
356 *countIt = count;
357 skip(stream, 4);
358 }
359
360 m_paragraphTabStops.resize(tabStopsCounts.size());
361 unsigned i = 0;
362 for (auto it = m_paragraphTabStops.rbegin(); it != m_paragraphTabStops.rend(); ++it)
363 {
364 skip(stream, 4);
365 const unsigned tabStopsCount = tabStopsCounts[i++];
366 it->resize(tabStopsCount);
367 for (auto &tabStop : *it)
368 {
369 tabStop = parseTabStop(stream);
370 }
371 }
372 }
373
parseParagraphFormat(const std::shared_ptr<librevenge::RVNGInputStream> & stream)374 ParagraphFormat QXP4Parser::parseParagraphFormat(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
375 {
376 ParagraphFormat result;
377
378 skip(stream, 8);
379
380 bool hasRuleAbove, hasRuleBelow;
381 readParagraphFlags(stream, result.incrementalLeading, hasRuleAbove, hasRuleBelow);
382
383 skip(stream, 2);
384 result.alignment = readHorAlign(stream);
385
386 skip(stream, 4);
387 const unsigned hj = readU16(stream, be);
388 if (hj < m_hjs.size())
389 result.hj = m_hjs[hj];
390 skip(stream, 2);
391
392 result.margin.left = readFraction(stream, be);
393 result.firstLineIndent = readFraction(stream, be);
394 result.margin.right = readFraction(stream, be);
395 result.leading = readFraction(stream, be);
396 result.margin.top = readFraction(stream, be);
397 result.margin.bottom = readFraction(stream, be);
398
399 skip(stream, 4);
400
401 auto ruleAbove = readParagraphRule(stream);
402 auto ruleBelow = readParagraphRule(stream);
403 if (hasRuleAbove)
404 result.ruleAbove = ruleAbove;
405 if (hasRuleBelow)
406 result.ruleBelow = ruleBelow;
407
408 const unsigned tabStopsIndex = readU16(stream, be);
409 if (tabStopsIndex != 0xffff)
410 {
411 if (tabStopsIndex >= m_paragraphTabStops.size())
412 {
413 QXP_DEBUG_MSG(("Tab stop %u not found\n", tabStopsIndex));
414 }
415 else
416 {
417 result.tabStops = m_paragraphTabStops[tabStopsIndex];
418 }
419 }
420
421 skip(stream, 2);
422
423 return result;
424 }
425
parseHJ(const std::shared_ptr<librevenge::RVNGInputStream> & stream)426 std::shared_ptr<HJ> QXP4Parser::parseHJ(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
427 {
428 auto hj = make_shared<HJ>();
429
430 skip(stream, 4);
431 parseHJProps(stream, *hj);
432 skip(stream, 64);
433
434 return hj;
435 }
436
parsePage(const std::shared_ptr<librevenge::RVNGInputStream> & stream,QXP4Deobfuscator & deobfuscate)437 Page QXP4Parser::parsePage(const std::shared_ptr<librevenge::RVNGInputStream> &stream, QXP4Deobfuscator &deobfuscate)
438 {
439 Page page;
440 page.pageSettings = parsePageSettings(stream);
441 page.objectsCount = deobfuscate(uint16_t(readU32(stream, be) & 0xffff));
442 return page;
443 }
444
parseObject(const std::shared_ptr<librevenge::RVNGInputStream> & stream,QXP4Deobfuscator & deobfuscate,QXPCollector & collector,const Page & page)445 void QXP4Parser::parseObject(const std::shared_ptr<librevenge::RVNGInputStream> &stream, QXP4Deobfuscator &deobfuscate, QXPCollector &collector, const Page &page)
446 {
447 const auto header = parseObjectHeader(stream, deobfuscate);
448
449 switch (header.contentType)
450 {
451 case ContentType::NONE:
452 switch (header.shapeType)
453 {
454 case ShapeType::LINE:
455 case ShapeType::ORTHOGONAL_LINE:
456 parseLine(stream, header, collector);
457 break;
458 case ShapeType::BEZIER_LINE:
459 parseBezierLine(stream, header, collector);
460 break;
461 case ShapeType::BEZIER_BOX:
462 parseBezierEmptyBox(stream, header, collector);
463 break;
464 case ShapeType::RECTANGLE:
465 case ShapeType::ROUNDED_RECTANGLE:
466 case ShapeType::CONCAVE_RECTANGLE:
467 case ShapeType::BEVELED_RECTANGLE:
468 case ShapeType::OVAL:
469 parseEmptyBox(stream, header, collector);
470 break;
471 default:
472 QXP_DEBUG_MSG(("Unsupported shape\n"));
473 throw GenericException();
474 }
475 break;
476 case ContentType::PICTURE:
477 switch (header.shapeType)
478 {
479 case ShapeType::BEZIER_BOX:
480 parseBezierPictureBox(stream, header, collector);
481 break;
482 case ShapeType::RECTANGLE:
483 case ShapeType::ROUNDED_RECTANGLE:
484 case ShapeType::CONCAVE_RECTANGLE:
485 case ShapeType::BEVELED_RECTANGLE:
486 case ShapeType::OVAL:
487 parsePictureBox(stream, header, collector);
488 break;
489 default:
490 QXP_DEBUG_MSG(("Unsupported shape\n"));
491 throw GenericException();
492 }
493 break;
494 case ContentType::TEXT:
495 switch (header.shapeType)
496 {
497 case ShapeType::LINE:
498 case ShapeType::ORTHOGONAL_LINE:
499 parseLineText(stream, header, collector);
500 break;
501 case ShapeType::BEZIER_LINE:
502 parseBezierText(stream, header, collector);
503 break;
504 case ShapeType::BEZIER_BOX:
505 parseBezierTextBox(stream, header, collector);
506 break;
507 case ShapeType::RECTANGLE:
508 case ShapeType::ROUNDED_RECTANGLE:
509 case ShapeType::CONCAVE_RECTANGLE:
510 case ShapeType::BEVELED_RECTANGLE:
511 case ShapeType::OVAL:
512 parseTextBox(stream, header, collector);
513 break;
514 default:
515 QXP_DEBUG_MSG(("Unsupported shape\n"));
516 throw GenericException();
517 }
518 break;
519 case ContentType::OBJECTS:
520 parseGroup(stream, header, collector, page);
521 break;
522 default:
523 QXP_DEBUG_MSG(("Unsupported content\n"));
524 throw GenericException();
525 }
526
527 deobfuscate.next(uint16_t(header.contentIndex));
528 }
529
parseObjectHeader(const std::shared_ptr<librevenge::RVNGInputStream> & stream,QXP4Deobfuscator & deobfuscate)530 QXP4Parser::ObjectHeader QXP4Parser::parseObjectHeader(const std::shared_ptr<librevenge::RVNGInputStream> &stream, QXP4Deobfuscator &deobfuscate)
531 {
532 ObjectHeader result;
533
534 bool noColor;
535 readObjectFlags(stream, noColor);
536
537 skip(stream, 1);
538
539 const unsigned colorId = readU16(stream, be);
540 const double shade = readFraction(stream, be);
541 result.color = getColor(colorId).applyShade(shade);
542 if (!noColor)
543 {
544 result.fillColor = result.color;
545 }
546
547 skip(stream, 4);
548
549 const uint16_t contentIndexObf = uint16_t(readU32(stream, be) & 0xffff);
550
551 result.rotation = readFraction(stream, be);
552 result.skew = readFraction(stream, be);
553
554 result.linkId = readU32(stream, be);
555 result.oleId = readU32(stream, be);
556 result.gradientId = readU32(stream, be);
557
558 skip(stream, 4);
559
560 const bool boxFlag1 = readU8(stream);
561 const bool boxFlag2 = readU8(stream);
562 if (be)
563 {
564 result.hflip = boxFlag1 & 0x80;
565 result.vflip = boxFlag2 & 0x80;
566 }
567 else
568 {
569 result.hflip = boxFlag1 & 0x1;
570 result.vflip = boxFlag2 & 0x1;
571 }
572
573 const uint8_t contentType = deobfuscate(readU8(stream));
574 deobfuscate.nextShift(contentType);
575
576 result.contentIndex = deobfuscate(contentIndexObf);
577
578 const uint8_t shapeType = deobfuscate(readU8(stream));
579
580 switch (contentType)
581 {
582 case 0:
583 result.contentType = ContentType::NONE;
584 break;
585 case 2:
586 result.contentType = ContentType::OBJECTS;
587 break;
588 case 3:
589 result.contentType = ContentType::TEXT;
590 break;
591 case 4:
592 result.contentType = ContentType::PICTURE;
593 break;
594 default:
595 QXP_DEBUG_MSG(("Unknown content type %u\n", contentType));
596 throw ParseError();
597 }
598
599 switch (shapeType)
600 {
601 case 1:
602 result.shapeType = ShapeType::LINE;
603 break;
604 case 2:
605 result.shapeType = ShapeType::ORTHOGONAL_LINE;
606 break;
607 case 4:
608 result.shapeType = ShapeType::BEZIER_LINE;
609 break;
610 case 5:
611 result.shapeType = ShapeType::RECTANGLE;
612 result.boxType = BoxType::RECTANGLE;
613 break;
614 case 6:
615 result.shapeType = ShapeType::ROUNDED_RECTANGLE;
616 result.boxType = BoxType::RECTANGLE;
617 result.cornerType = CornerType::ROUNDED;
618 break;
619 case 7:
620 result.shapeType = ShapeType::CONCAVE_RECTANGLE;
621 result.boxType = BoxType::RECTANGLE;
622 result.cornerType = CornerType::CONCAVE;
623 break;
624 case 8:
625 result.shapeType = ShapeType::BEVELED_RECTANGLE;
626 result.boxType = BoxType::RECTANGLE;
627 result.cornerType = CornerType::BEVELED;
628 break;
629 case 9:
630 result.shapeType = ShapeType::OVAL;
631 result.boxType = BoxType::OVAL;
632 break;
633 case 11:
634 result.shapeType = ShapeType::BEZIER_BOX;
635 result.boxType = BoxType::BEZIER;
636 break;
637 default:
638 QXP_DEBUG_MSG(("Unknown shape type %u\n", shapeType));
639 throw ParseError();
640 }
641
642 return result;
643 }
644
parseLine(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,QXPCollector & collector)645 void QXP4Parser::parseLine(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, QXPCollector &collector)
646 {
647 auto line = createLine<Line>(header);
648
649 line->style = readFrame(stream);
650 skip(stream, 4);
651 line->runaround = readRunaround(stream);
652 skip(stream, 4);
653
654 line->boundingBox = readObjectBBox(stream);
655
656 skip(stream, 24);
657
658 collector.collectLine(line);
659 }
660
parseBezierLine(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,QXPCollector & collector)661 void QXP4Parser::parseBezierLine(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, QXPCollector &collector)
662 {
663 auto line = createLine<Line>(header);
664
665 line->style = readFrame(stream);
666 skip(stream, 4);
667 line->runaround = readRunaround(stream);
668 skip(stream, 44);
669
670 readBezierData(stream, line->curveComponents, line->boundingBox);
671
672 collector.collectLine(line);
673 }
674
parseBezierEmptyBox(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,QXPCollector & collector)675 void QXP4Parser::parseBezierEmptyBox(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, QXPCollector &collector)
676 {
677 auto box = createBox<Box>(header);
678
679 box->frame = readFrame(stream);
680 skip(stream, 4);
681 box->runaround = readRunaround(stream);
682 skip(stream, 44);
683
684 if (header.gradientId != 0)
685 {
686 box->fill = readGradient(stream, header.color);
687 }
688
689 readBezierData(stream, box->curveComponents, box->boundingBox);
690
691 collector.collectBox(box);
692 }
693
parseEmptyBox(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,QXPCollector & collector)694 void QXP4Parser::parseEmptyBox(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, QXPCollector &collector)
695 {
696 auto box = createBox<Box>(header);
697
698 box->frame = readFrame(stream);
699 skip(stream, 4);
700 box->runaround = readRunaround(stream);
701 skip(stream, 4);
702
703 box->boundingBox = readObjectBBox(stream);
704
705 box->cornerRadius = readFraction(stream, be);
706
707 skip(stream, 20);
708
709 if (header.gradientId != 0)
710 {
711 box->fill = readGradient(stream, header.color);
712 }
713
714 collector.collectBox(box);
715 }
716
parseBezierPictureBox(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,QXPCollector & collector)717 void QXP4Parser::parseBezierPictureBox(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, QXPCollector &collector)
718 {
719 auto box = createBox<PictureBox>(header);
720
721 box->frame = readFrame(stream);
722 skip(stream, 4);
723 box->runaround = readRunaround(stream);
724 skip(stream, 40);
725
726 readOleObject(stream);
727
728 if (header.gradientId != 0)
729 {
730 box->fill = readGradient(stream, header.color);
731 }
732
733 readPictureSettings(stream, box);
734
735 skip(stream, 76);
736
737 if (header.contentIndex != 0 && header.oleId == 0)
738 {
739 readImageData(stream);
740 }
741
742 readBezierData(stream, box->curveComponents, box->boundingBox);
743
744 collector.collectBox(box);
745 }
746
parsePictureBox(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,QXPCollector & collector)747 void QXP4Parser::parsePictureBox(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, QXPCollector &collector)
748 {
749 auto picturebox = createBox<PictureBox>(header);
750
751 picturebox->frame = readFrame(stream);
752 skip(stream, 4);
753 picturebox->runaround = readRunaround(stream);
754 skip(stream, 4);
755
756 picturebox->boundingBox = readObjectBBox(stream);
757
758 picturebox->cornerRadius = readFraction(stream, be);
759
760 skip(stream, 16);
761
762 readOleObject(stream);
763
764 if (header.gradientId != 0)
765 {
766 picturebox->fill = readGradient(stream, header.color);
767 }
768
769 readPictureSettings(stream, picturebox);
770
771 skip(stream, 76);
772
773 if (header.contentIndex != 0 && header.oleId == 0)
774 {
775 readImageData(stream);
776 }
777
778 collector.collectBox(picturebox);
779 }
780
parseLineText(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,QXPCollector & collector)781 void QXP4Parser::parseLineText(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, QXPCollector &collector)
782 {
783 auto textpath = createLine<TextPath>(header);
784 textpath->linkSettings.linkId = header.linkId;
785
786 textpath->style = readFrame(stream);
787 skip(stream, 4);
788 textpath->runaround = readRunaround(stream);
789 skip(stream, 4);
790
791 textpath->boundingBox = readObjectBBox(stream);
792
793 skip(stream, 24);
794
795 textpath->linkSettings.offsetIntoText = readU32(stream, be);
796 skip(stream, 44);
797 readLinkedTextSettings(stream, textpath->linkSettings);
798 skip(stream, 4);
799 readTextPathSettings(stream, textpath->settings);
800 skip(stream, 4);
801
802 skipTextObjectEnd(stream, header, textpath->linkSettings);
803
804 if (header.contentIndex == 0)
805 {
806 collector.collectLine(textpath);
807 }
808 else
809 {
810 if (textpath->linkSettings.offsetIntoText > 0)
811 {
812 textpath->linkSettings.linkedIndex = header.contentIndex;
813 }
814 else
815 {
816 textpath->text = parseText(header.contentIndex, header.linkId, collector);
817 }
818
819 collector.collectTextPath(textpath);
820 }
821 }
822
parseBezierText(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,QXPCollector & collector)823 void QXP4Parser::parseBezierText(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, QXPCollector &collector)
824 {
825 auto textpath = createLine<TextPath>(header);
826 textpath->linkSettings.linkId = header.linkId;
827
828 textpath->style = readFrame(stream);
829 skip(stream, 4);
830 textpath->runaround = readRunaround(stream);
831 skip(stream, 44);
832
833 textpath->linkSettings.offsetIntoText = readU32(stream, be);
834 skip(stream, 44);
835 readLinkedTextSettings(stream, textpath->linkSettings);
836 skip(stream, 4);
837 readTextPathSettings(stream, textpath->settings);
838 skip(stream, 4);
839
840 readBezierData(stream, textpath->curveComponents, textpath->boundingBox);
841
842 skipTextObjectEnd(stream, header, textpath->linkSettings);
843
844 if (header.contentIndex == 0)
845 {
846 collector.collectLine(textpath);
847 }
848 else
849 {
850 if (textpath->linkSettings.offsetIntoText > 0)
851 {
852 textpath->linkSettings.linkedIndex = header.contentIndex;
853 }
854 else
855 {
856 textpath->text = parseText(header.contentIndex, header.linkId, collector);
857 }
858
859 collector.collectTextPath(textpath);
860 }
861 }
862
parseBezierTextBox(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,QXPCollector & collector)863 void QXP4Parser::parseBezierTextBox(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, QXPCollector &collector)
864 {
865 auto textbox = createBox<TextBox>(header);
866 textbox->linkSettings.linkId = header.linkId;
867
868 textbox->frame = readFrame(stream);
869 skip(stream, 4);
870 textbox->runaround = readRunaround(stream);
871 skip(stream, 44);
872
873 if (header.gradientId != 0)
874 {
875 textbox->fill = readGradient(stream, header.color);
876 }
877
878 textbox->linkSettings.offsetIntoText = readU32(stream, be);
879 skip(stream, 2);
880 readTextSettings(stream, textbox->settings);
881 readLinkedTextSettings(stream, textbox->linkSettings);
882 skip(stream, 12);
883
884 readBezierData(stream, textbox->curveComponents, textbox->boundingBox);
885
886 skipTextObjectEnd(stream, header, textbox->linkSettings);
887
888 if (header.contentIndex == 0)
889 {
890 collector.collectBox(textbox);
891 }
892 else
893 {
894 if (textbox->linkSettings.offsetIntoText > 0)
895 {
896 textbox->linkSettings.linkedIndex = header.contentIndex;
897 }
898 else
899 {
900 textbox->text = parseText(header.contentIndex, header.linkId, collector);
901 }
902
903 collector.collectTextBox(textbox);
904 }
905 }
906
parseTextBox(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,QXPCollector & collector)907 void QXP4Parser::parseTextBox(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, QXPCollector &collector)
908 {
909 auto textbox = createBox<TextBox>(header);
910 textbox->linkSettings.linkId = header.linkId;
911
912 textbox->frame = readFrame(stream);
913 skip(stream, 4);
914 textbox->runaround = readRunaround(stream);
915 skip(stream, 4);
916
917 textbox->boundingBox = readObjectBBox(stream);
918
919 textbox->cornerRadius = readFraction(stream, be);
920
921 skip(stream, 20);
922
923 if (header.gradientId != 0)
924 {
925 textbox->fill = readGradient(stream, header.color);
926 }
927
928 textbox->linkSettings.offsetIntoText = readU32(stream, be);
929 skip(stream, 2);
930 readTextSettings(stream, textbox->settings);
931 readLinkedTextSettings(stream, textbox->linkSettings);
932 skip(stream, 12);
933
934 skipTextObjectEnd(stream, header, textbox->linkSettings);
935
936 if (header.contentIndex == 0)
937 {
938 collector.collectBox(textbox);
939 }
940 else
941 {
942 if (textbox->linkSettings.offsetIntoText > 0)
943 {
944 textbox->linkSettings.linkedIndex = header.contentIndex;
945 }
946 else
947 {
948 textbox->text = parseText(header.contentIndex, header.linkId, collector);
949 }
950
951 collector.collectTextBox(textbox);
952 }
953 }
954
parseGroup(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader &,QXPCollector & collector,const Page & page)955 void QXP4Parser::parseGroup(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &, QXPCollector &collector, const Page &page)
956 {
957 auto group = make_shared<Group>();
958
959 skip(stream, 68);
960
961 group->boundingBox = readObjectBBox(stream);
962
963 skip(stream, 24);
964
965 const unsigned count = readU16(stream, be);
966 if (count > page.objectsCount - 1)
967 {
968 QXP_DEBUG_MSG(("Invalid group elements count %u\n", count));
969 throw ParseError();
970 }
971 skip(stream, 10);
972
973 group->objectsIndexes.resize(count);
974 for (unsigned &ind : group->objectsIndexes)
975 {
976 ind = readU32(stream, be);
977 if (ind >= page.objectsCount)
978 {
979 QXP_DEBUG_MSG(("Invalid group element index %u\n", ind));
980 throw ParseError();
981 }
982 }
983
984 collector.collectGroup(group);
985 }
986
readFrame(const std::shared_ptr<librevenge::RVNGInputStream> & stream)987 Frame QXP4Parser::readFrame(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
988 {
989 Frame frame;
990 frame.width = readFraction(stream, be);
991 const double shade = readFraction(stream, be);
992 const unsigned colorId = readU16(stream, be);
993 frame.color = getColor(colorId).applyShade(shade);
994 const unsigned gapColorId = readU16(stream, be);
995 const double gapShade = readFraction(stream, be);
996 frame.gapColor = getColor(gapColorId).applyShade(gapShade);
997
998 const uint8_t arrowType = readU8(stream);
999 setArrow((arrowType >> 2) & 0xf, frame);
1000
1001 const bool isBitmapFrame = readU8(stream) == 1;
1002 const unsigned styleIndex = readU16(stream, be);
1003 if (!isBitmapFrame)
1004 {
1005 frame.lineStyle = getLineStyle(styleIndex);
1006 }
1007
1008 return frame;
1009 }
1010
readRunaround(const std::shared_ptr<librevenge::RVNGInputStream> & stream)1011 bool QXP4Parser::readRunaround(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
1012 {
1013 bool result = readU8(stream) == 1;
1014 skip(stream, 39);
1015 return result;
1016 }
1017
readLinkedTextSettings(const std::shared_ptr<librevenge::RVNGInputStream> & stream,LinkedTextSettings & settings)1018 void QXP4Parser::readLinkedTextSettings(const std::shared_ptr<librevenge::RVNGInputStream> &stream, LinkedTextSettings &settings)
1019 {
1020 settings.nextLinkedIndex = readU32(stream, be);
1021 skip(stream, 4);
1022 }
1023
readTextSettings(const std::shared_ptr<librevenge::RVNGInputStream> & stream,TextSettings & settings)1024 void QXP4Parser::readTextSettings(const std::shared_ptr<librevenge::RVNGInputStream> &stream, TextSettings &settings)
1025 {
1026 skip(stream, 2);
1027 settings.gutterWidth = readFraction(stream, be);
1028 settings.inset.top = readFraction(stream, be);
1029 settings.inset.left = readFraction(stream, be);
1030 settings.inset.right = readFraction(stream, be);
1031 settings.inset.bottom = readFraction(stream, be);
1032 settings.rotation = readFraction(stream, be);
1033 settings.skew = readFraction(stream, be);
1034 settings.columnsCount = readU8(stream);
1035 settings.verticalAlignment = readVertAlign(stream);
1036 skip(stream, 10);
1037 }
1038
readTextPathSettings(const std::shared_ptr<librevenge::RVNGInputStream> & stream,TextPathSettings & settings)1039 void QXP4Parser::readTextPathSettings(const std::shared_ptr<librevenge::RVNGInputStream> &stream, TextPathSettings &settings)
1040 {
1041 settings.skew = readU8(stream) == 1;
1042 settings.rotate = readU8(stream) == 1;
1043 const uint8_t align = readU8(stream);
1044 switch (align)
1045 {
1046 default:
1047 QXP_DEBUG_MSG(("Unknown text path align %u\n", align));
1048 QXP_FALLTHROUGH; // pick a default
1049 case 2:
1050 settings.alignment = TextPathAlignment::BASELINE;
1051 break;
1052 case 0:
1053 settings.alignment = TextPathAlignment::ASCENT;
1054 break;
1055 case 1:
1056 settings.alignment = TextPathAlignment::CENTER;
1057 break;
1058 case 3:
1059 settings.alignment = TextPathAlignment::DESCENT;
1060 break;
1061 }
1062 const uint8_t lineAlign = readU8(stream);
1063 switch (lineAlign)
1064 {
1065 default:
1066 QXP_DEBUG_MSG(("Unknown text path line align %u\n", lineAlign));
1067 QXP_FALLTHROUGH; // pick a default
1068 case 0:
1069 settings.lineAlignment = TextPathLineAlignment::TOP;
1070 break;
1071 case 1:
1072 settings.lineAlignment = TextPathLineAlignment::CENTER;
1073 break;
1074 case 2:
1075 settings.lineAlignment = TextPathLineAlignment::BOTTOM;
1076 break;
1077 }
1078 }
1079
readOleObject(const std::shared_ptr<librevenge::RVNGInputStream> & stream)1080 void QXP4Parser::readOleObject(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
1081 {
1082 const unsigned length = readU32(stream, be);
1083 skip(stream, length);
1084 }
1085
readPictureSettings(const std::shared_ptr<librevenge::RVNGInputStream> & stream,std::shared_ptr<PictureBox> & picturebox)1086 void QXP4Parser::readPictureSettings(const std::shared_ptr<librevenge::RVNGInputStream> &stream, std::shared_ptr<PictureBox> &picturebox)
1087 {
1088 skip(stream, 24);
1089 picturebox->pictureRotation = readFraction(stream, be);
1090 picturebox->pictureSkew = readFraction(stream, be);
1091 picturebox->offsetLeft = readFraction(stream, be);
1092 picturebox->offsetTop = readFraction(stream, be);
1093 picturebox->scaleHor = readFraction(stream, be);
1094 picturebox->scaleVert = readFraction(stream, be);
1095 }
1096
readImageData(const std::shared_ptr<librevenge::RVNGInputStream> & stream)1097 void QXP4Parser::readImageData(const std::shared_ptr<librevenge::RVNGInputStream> &stream)
1098 {
1099 const unsigned length = readU32(stream, be);
1100 skip(stream, length);
1101 }
1102
readBezierData(const std::shared_ptr<librevenge::RVNGInputStream> & stream,std::vector<CurveComponent> & curveComponents,Rect & bbox)1103 void QXP4Parser::readBezierData(const std::shared_ptr<librevenge::RVNGInputStream> &stream, std::vector<CurveComponent> &curveComponents, Rect &bbox)
1104 {
1105 const unsigned length = readU32(stream, be);
1106 if (length > getLength(stream))
1107 {
1108 QXP_DEBUG_MSG(("Invalid bezier data length %ul\n", length));
1109 throw ParseError();
1110 }
1111 const unsigned long start = stream->tell();
1112 const unsigned long end = start + length;
1113
1114 try
1115 {
1116 skip(stream, 2);
1117 const unsigned componentsCount = readU16(stream, be);
1118 if (componentsCount > length / 24)
1119 {
1120 QXP_DEBUG_MSG(("Invalid bezier components count %u\n", componentsCount));
1121 throw ParseError();
1122 }
1123
1124 bbox = readObjectBBox(stream);
1125
1126 std::vector<unsigned long> componentsOffsets;
1127 componentsOffsets.resize(componentsCount);
1128 for (auto &off : componentsOffsets)
1129 {
1130 off = start + readU32(stream, be);
1131 }
1132
1133 curveComponents.resize(componentsCount);
1134 unsigned i = 0;
1135 for (auto &comp : curveComponents)
1136 {
1137 seek(stream, componentsOffsets[i++]);
1138
1139 skip(stream, 2);
1140 const unsigned pointsCount = readU16(stream, be);
1141 if (pointsCount > length / 8)
1142 {
1143 QXP_DEBUG_MSG(("Invalid bezier points count %u\n", componentsCount));
1144 throw ParseError();
1145 }
1146
1147 comp.boundingBox = readObjectBBox(stream);
1148
1149 comp.points.resize(pointsCount);
1150 for (auto &p : comp.points)
1151 {
1152 p = readYX(stream);
1153 }
1154 }
1155 }
1156 catch (...)
1157 {
1158 QXP_DEBUG_MSG(("Failed to parse bezier data, offset %ld\n", stream->tell()));
1159 }
1160
1161 seek(stream, end);
1162 }
1163
skipTextObjectEnd(const std::shared_ptr<librevenge::RVNGInputStream> & stream,const QXP4Parser::ObjectHeader & header,const LinkedTextSettings & linkedTextSettings)1164 void QXP4Parser::skipTextObjectEnd(const std::shared_ptr<librevenge::RVNGInputStream> &stream, const QXP4Parser::ObjectHeader &header, const LinkedTextSettings &linkedTextSettings)
1165 {
1166 if (header.contentIndex == 0 || linkedTextSettings.offsetIntoText == 0)
1167 {
1168 skip(stream, 4);
1169 const unsigned fileInfoId = readU32(stream, be);
1170 skip(stream, 4);
1171 if (fileInfoId != 0)
1172 {
1173 skipFileInfo(stream);
1174 }
1175 if (header.contentIndex == 0)
1176 {
1177 skip(stream, 16);
1178 }
1179 }
1180 }
1181
1182 }
1183
1184 /* vim:set shiftwidth=2 softtabstop=2 expandtab: */
1185