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