Coverage Report

Created: 2026-09-14 06:37

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