Coverage Report

Created: 2026-03-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmspub/src/lib/MSPUBParser.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 libmspub 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 "MSPUBParser.h"
11
12
#include <algorithm>
13
#include <cassert>
14
#include <list>
15
#include <memory>
16
#include <set>
17
#include <sstream>
18
#include <string>
19
#include <utility>
20
21
#include <boost/numeric/conversion/cast.hpp>
22
23
#include <librevenge-stream/librevenge-stream.h>
24
25
#include "Arrow.h"
26
#include "ColorReference.h"
27
#include "Coordinate.h"
28
#include "Dash.h"
29
#include "EscherContainerType.h"
30
#include "EscherFieldIds.h"
31
#include "Fill.h"
32
#include "FillType.h"
33
#include "Line.h"
34
#include "ListInfo.h"
35
#include "MSPUBBlockID.h"
36
#include "MSPUBBlockType.h"
37
#include "MSPUBCollector.h"
38
#include "MSPUBConstants.h"
39
#include "MSPUBContentChunkType.h"
40
#include "MSPUBMetaData.h"
41
#include "Shadow.h"
42
#include "ShapeFlags.h"
43
#include "ShapeType.h"
44
#include "TableInfo.h"
45
#include "VerticalAlign.h"
46
#include "libmspub_utils.h"
47
48
namespace libmspub
49
{
50
51
namespace
52
{
53
54
Underline readUnderline(const unsigned value)
55
25.7k
{
56
25.7k
  switch (value & 0xff)
57
25.7k
  {
58
11.7k
  case 0x0:
59
11.7k
    return Underline::None;
60
1.57k
  default:
61
1.57k
    MSPUB_DEBUG_MSG(("unknown underline type %u\n", value & 0xff));
62
1.57k
    MSPUB_FALLTHROUGH;
63
2.73k
  case 0x1:
64
2.73k
    return Underline::Single;
65
360
  case 0x2:
66
360
    return Underline::WordsOnly;
67
346
  case 0x3:
68
346
    return Underline::Double;
69
734
  case 0x4:
70
734
    return Underline::Dotted;
71
619
  case 0x6:
72
619
    return Underline::Thick;
73
314
  case 0x7:
74
314
    return Underline::Dash;
75
976
  case 0x9:
76
976
    return Underline::DotDash;
77
767
  case 0xa:
78
767
    return Underline::DotDotDash;
79
1.01k
  case 0xb:
80
1.01k
    return Underline::Wave;
81
938
  case 0x10:
82
938
    return Underline::ThickWave;
83
731
  case 0x11:
84
731
    return Underline::ThickDot;
85
687
  case 0x12:
86
687
    return Underline::ThickDash;
87
930
  case 0x13:
88
930
    return Underline::ThickDotDash;
89
342
  case 0x14:
90
342
    return Underline::ThickDotDotDash;
91
463
  case 0x15:
92
463
    return Underline::LongDash;
93
1.01k
  case 0x16:
94
1.01k
    return Underline::ThickLongDash;
95
1.00k
  case 0x17:
96
1.00k
    return Underline::DoubleWave;
97
25.7k
  }
98
25.7k
}
99
100
}
101
102
MSPUBParser::MSPUBParser(librevenge::RVNGInputStream *input, MSPUBCollector *collector)
103
7.90k
  : m_input(input),
104
7.90k
    m_length(boost::numeric_cast<unsigned>(getLength(input))),
105
7.90k
    m_collector(collector),
106
7.90k
    m_blockInfo(), m_contentChunks(),
107
7.90k
    m_cellsChunkIndices(),
108
7.90k
    m_pageChunkIndices(), m_shapeChunkIndices(),
109
7.90k
    m_paletteChunkIndices(), m_borderArtChunkIndices(),
110
7.90k
    m_fontChunkIndices(),
111
7.90k
    m_unknownChunkIndices(), m_documentChunkIndex(),
112
7.90k
    m_lastSeenSeqNum(-1), m_lastAddedImage(0),
113
7.90k
    m_alternateShapeSeqNums(), m_escherDelayIndices()
114
7.90k
{
115
7.90k
}
116
117
MSPUBParser::~MSPUBParser()
118
7.90k
{
119
7.90k
}
120
121
bool MSPUBParser::lineExistsByFlagPointer(unsigned *flags,
122
                                          unsigned *geomFlags)
123
25.9k
{
124
25.9k
  return flags &&
125
20.1k
         !(((*flags) & FLAG_USE_LINE) && !((*flags) & FLAG_LINE)) &&
126
13.3k
         ((!geomFlags) || !((*geomFlags) & FLAG_GEOM_USE_LINE_OK)
127
71
          || ((*geomFlags) & FLAG_GEOM_LINE_OK));
128
129
25.9k
}
130
131
unsigned MSPUBParser::getColorIndexByQuillEntry(unsigned entry)
132
43.0k
{
133
43.0k
  return entry;
134
43.0k
}
135
136
short MSPUBParser::getBlockDataLength(unsigned type) // -1 for variable-length block with the data length as the first DWORD
137
70.0M
{
138
70.0M
  switch (type)
139
70.0M
  {
140
633k
  case DUMMY:
141
808k
  case 0x5:
142
1.72M
  case 0x8:
143
1.91M
  case 0xa:
144
1.91M
    return 0;
145
678k
  case 0x10:
146
792k
  case 0x12:
147
1.97M
  case 0x18:
148
2.06M
  case 0x1a:
149
2.86M
  case 0x07:
150
2.86M
    return 2;
151
1.76M
  case 0x20:
152
2.51M
  case 0x22:
153
2.53M
  case 0x58:
154
3.14M
  case 0x68:
155
3.47M
  case 0x70:
156
4.01M
  case 0xb8:
157
4.01M
    return 4;
158
330k
  case 0x28:
159
330k
    return 8;
160
51.9k
  case 0x38:
161
51.9k
    return 16;
162
34.8k
  case 0x48:
163
34.8k
    return 24;
164
163k
  case STRING_CONTAINER:
165
193k
  case 0x80:
166
220k
  case 0x82:
167
1.02M
  case GENERAL_CONTAINER:
168
1.14M
  case 0x8a:
169
1.16M
  case 0x90:
170
1.17M
  case 0x98:
171
1.21M
  case 0xa0:
172
1.21M
    return -1;
173
70.0M
  }
174
  //FIXME: Debug assertion here? Should never get here.
175
59.5M
  MSPUB_DEBUG_MSG(("Block of unknown type seen!\n"));
176
59.5M
  return 0;
177
70.0M
}
178
179
bool MSPUBParser::parse()
180
4.35k
{
181
4.35k
  MSPUB_DEBUG_MSG(("***NOTE***: Where applicable, the meanings of block/chunk IDs and Types printed below may be found in:\n\t***MSPUBBlockType.h\n\t***MSPUBBlockID.h\n\t***MSPUBContentChunkType.h\n*****\n"));
182
4.35k
  if (!m_input->isStructured())
183
0
    return false;
184
  // No check: metadata are not important enough to fail if they can't be parsed
185
4.35k
  parseMetaData();
186
4.35k
  std::unique_ptr<librevenge::RVNGInputStream> quill(m_input->getSubStreamByName("Quill/QuillSub/CONTENTS"));
187
4.35k
  if (!quill)
188
308
  {
189
308
    MSPUB_DEBUG_MSG(("Couldn't get quill stream.\n"));
190
308
    return false;
191
308
  }
192
4.04k
  if (!parseQuill(quill.get()))
193
0
  {
194
0
    MSPUB_DEBUG_MSG(("Couldn't parse quill stream.\n"));
195
0
    return false;
196
0
  }
197
4.04k
  std::unique_ptr<librevenge::RVNGInputStream> contents(m_input->getSubStreamByName("Contents"));
198
4.04k
  if (!contents)
199
0
  {
200
0
    MSPUB_DEBUG_MSG(("Couldn't get contents stream.\n"));
201
0
    return false;
202
0
  }
203
4.04k
  if (!parseContents(contents.get()))
204
227
  {
205
227
    MSPUB_DEBUG_MSG(("Couldn't parse contents stream.\n"));
206
227
    return false;
207
227
  }
208
3.81k
  std::unique_ptr<librevenge::RVNGInputStream> escherDelay(m_input->getSubStreamByName("Escher/EscherDelayStm"));
209
3.81k
  if (escherDelay)
210
1.23k
  {
211
1.23k
    parseEscherDelay(escherDelay.get());
212
1.23k
  }
213
3.81k
  std::unique_ptr<librevenge::RVNGInputStream> escher(m_input->getSubStreamByName("Escher/EscherStm"));
214
3.81k
  if (!escher)
215
521
  {
216
521
    MSPUB_DEBUG_MSG(("Couldn't get escher stream.\n"));
217
521
    return false;
218
521
  }
219
3.29k
  if (!parseEscher(escher.get()))
220
0
  {
221
0
    MSPUB_DEBUG_MSG(("Couldn't parse escher stream.\n"));
222
0
    return false;
223
0
  }
224
225
3.29k
  return m_collector->go();
226
3.29k
}
227
228
ImgType MSPUBParser::imgTypeByBlipType(unsigned short type)
229
59.8k
{
230
59.8k
  switch (type)
231
59.8k
  {
232
209
  case OFFICE_ART_BLIP_PNG:
233
209
    return PNG;
234
862
  case OFFICE_ART_BLIP_JPEG:
235
862
    return JPEG;
236
6
  case OFFICE_ART_BLIP_JPEGCMYK:
237
6
    return JPEGCMYK;
238
817
  case OFFICE_ART_BLIP_WMF:
239
817
    return WMF;
240
43
  case OFFICE_ART_BLIP_DIB:
241
43
    return DIB;
242
1.05k
  case OFFICE_ART_BLIP_EMF:
243
1.05k
    return EMF;
244
16
  case OFFICE_ART_BLIP_TIFF:
245
16
    return TIFF;
246
29
  case OFFICE_ART_BLIP_PICT:
247
29
    return PICT;
248
59.8k
  }
249
56.8k
  return UNKNOWN;
250
59.8k
}
251
252
int MSPUBParser::getStartOffset(ImgType type, unsigned short initial)
253
3.03k
{
254
3.03k
  bool oneUid = true;
255
3.03k
  int offset = 0x11;
256
3.03k
  unsigned short recInstance = initial >> 4;
257
3.03k
  switch (type)
258
3.03k
  {
259
817
  case WMF:
260
817
    oneUid = recInstance == 0x216;
261
817
    offset = 0x34;
262
817
    break;
263
1.05k
  case EMF:
264
1.05k
    oneUid = recInstance == 0x3D4;
265
1.05k
    offset = 0x34;
266
1.05k
    break;
267
209
  case PNG:
268
209
    oneUid = recInstance == 0x6E0;
269
209
    offset = 0x11;
270
209
    break;
271
862
  case JPEG:
272
862
    oneUid = recInstance == 0x46A || recInstance == 0x6E2;
273
862
    offset = 0x11;
274
862
    break;
275
6
  case JPEGCMYK:
276
6
    oneUid = recInstance == 0x46B || recInstance == 0x6E3;
277
6
    offset = 33;
278
6
    break;
279
43
  case DIB:
280
43
    oneUid = recInstance == 0x7A8;
281
43
    offset = 0x11;
282
43
    break;
283
16
  case TIFF:
284
16
    oneUid = recInstance == 0x6E4;
285
16
    offset = 0x11;
286
16
    break;
287
29
  default:
288
29
    break;
289
3.03k
  }
290
3.03k
  return offset + (oneUid ? 0 : 0x10);
291
3.03k
}
292
293
bool MSPUBParser::parseEscherDelay(librevenge::RVNGInputStream *input)
294
1.23k
{
295
61.1k
  while (stillReading(input, (unsigned long)-1))
296
59.9k
  {
297
59.9k
    EscherContainerInfo info = parseEscherContainer(input);
298
59.9k
    const ImgType imgType = imgTypeByBlipType(info.type);
299
59.9k
    if (imgType != UNKNOWN)
300
3.03k
    {
301
3.03k
      librevenge::RVNGBinaryData img;
302
3.03k
      unsigned long toRead = info.contentsLength;
303
3.03k
      input->seek(input->tell() + getStartOffset(imgType, info.initial), librevenge::RVNG_SEEK_SET);
304
6.02k
      while (toRead > 0 && stillReading(input, (unsigned long)-1))
305
2.99k
      {
306
2.99k
        unsigned long howManyRead = 0;
307
2.99k
        const unsigned char *buf = input->read(toRead, howManyRead);
308
2.99k
        img.append(buf, howManyRead);
309
2.99k
        toRead -= howManyRead;
310
2.99k
      }
311
3.03k
      if (imgType == WMF || imgType == EMF)
312
1.86k
      {
313
1.86k
        img = inflateData(img);
314
1.86k
      }
315
1.16k
      else if (imgType == DIB)
316
43
      {
317
        // Reconstruct BMP header
318
        // cf. http://en.wikipedia.org/wiki/BMP_file_format , accessed 2012-5-31
319
43
        librevenge::RVNGInputStream *buf = img.getDataStream();
320
43
        if (img.size() < 0x2E + 4)
321
12
        {
322
12
          ++m_lastAddedImage;
323
12
          MSPUB_DEBUG_MSG(("Garbage DIB at index 0x%x\n", m_lastAddedImage));
324
12
          input->seek(info.contentsOffset + info.contentsLength, librevenge::RVNG_SEEK_SET);
325
12
          continue;
326
12
        }
327
31
        buf->seek(0x0E, librevenge::RVNG_SEEK_SET);
328
31
        unsigned short bitsPerPixel = readU16(buf);
329
31
        buf->seek(0x20, librevenge::RVNG_SEEK_SET);
330
31
        unsigned numPaletteColors = readU32(buf);
331
31
        if (numPaletteColors == 0 && bitsPerPixel <= 8)
332
2
        {
333
2
          numPaletteColors = 1;
334
10
          for (int i = 0; i < bitsPerPixel; ++i)
335
8
          {
336
8
            numPaletteColors *= 2;
337
8
          }
338
2
        }
339
340
31
        librevenge::RVNGBinaryData tmpImg;
341
31
        tmpImg.append((unsigned char)0x42);
342
31
        tmpImg.append((unsigned char)0x4d);
343
344
31
        tmpImg.append((unsigned char)((img.size() + 14) & 0x000000ff));
345
31
        tmpImg.append((unsigned char)(((img.size() + 14) & 0x0000ff00) >> 8));
346
31
        tmpImg.append((unsigned char)(((img.size() + 14) & 0x00ff0000) >> 16));
347
31
        tmpImg.append((unsigned char)(((img.size() + 14) & 0xff000000) >> 24));
348
349
31
        tmpImg.append((unsigned char)0x00);
350
31
        tmpImg.append((unsigned char)0x00);
351
31
        tmpImg.append((unsigned char)0x00);
352
31
        tmpImg.append((unsigned char)0x00);
353
354
31
        tmpImg.append((unsigned char)(0x36 + 4 * numPaletteColors));
355
31
        tmpImg.append((unsigned char)0x00);
356
31
        tmpImg.append((unsigned char)0x00);
357
31
        tmpImg.append((unsigned char)0x00);
358
31
        tmpImg.append(img);
359
31
        img = tmpImg;
360
31
      }
361
3.02k
      m_collector->addImage(++m_lastAddedImage, imgType, img);
362
3.02k
    }
363
56.8k
    else
364
56.8k
    {
365
56.8k
      ++m_lastAddedImage;
366
56.8k
      MSPUB_DEBUG_MSG(("Image of unknown type at index 0x%x\n", m_lastAddedImage));
367
56.8k
    }
368
59.8k
    input->seek(info.contentsOffset + info.contentsLength, librevenge::RVNG_SEEK_SET);
369
59.8k
  }
370
1.23k
  return true;
371
1.23k
}
372
373
bool MSPUBParser::parseContents(librevenge::RVNGInputStream *input)
374
3.02k
{
375
3.02k
  MSPUB_DEBUG_MSG(("MSPUBParser::parseContents\n"));
376
3.02k
  input->seek(0x1a, librevenge::RVNG_SEEK_SET);
377
3.02k
  unsigned trailerOffset = readU32(input);
378
3.02k
  MSPUB_DEBUG_MSG(("MSPUBParser: trailerOffset %.8x\n", trailerOffset));
379
3.02k
  input->seek(trailerOffset, librevenge::RVNG_SEEK_SET);
380
3.02k
  unsigned trailerLength = readU32(input);
381
11.0k
  for (unsigned i=0; i<3; i++)
382
8.22k
  {
383
8.22k
    MSPUBBlockInfo trailerPart = parseBlock(input);
384
8.22k
    MSPUB_DEBUG_MSG(("Trailer SubBlock %i, startPosition 0x%lx, id %i, type 0x%x, dataLength 0x%lx\n", i+1, trailerPart.startPosition, trailerPart.id, trailerPart.type, trailerPart.dataLength));
385
8.22k
    if (trailerPart.type == TRAILER_DIRECTORY)
386
2.38k
    {
387
388
971k
      while (stillReading(input, trailerPart.dataOffset + trailerPart.dataLength))
389
968k
      {
390
968k
        m_blockInfo.push_back(parseBlock(input));
391
968k
        ++m_lastSeenSeqNum;
392
968k
        if (m_blockInfo.back().type == GENERAL_CONTAINER)
393
109k
        {
394
109k
          if (parseContentChunkReference(input, m_blockInfo.back()))
395
54.4k
          {
396
54.4k
            if (m_contentChunks.size() > 1)
397
52.4k
            {
398
52.4k
              m_contentChunks[m_contentChunks.size() - 2].end = m_contentChunks.back().offset;
399
52.4k
            }
400
54.4k
          }
401
109k
        }
402
859k
        else(skipBlock(input, m_blockInfo.back()));
403
968k
      }
404
2.38k
      if (!m_contentChunks.empty())
405
2.35k
      {
406
2.35k
        m_contentChunks.back().end = trailerPart.dataOffset + trailerPart.dataLength;
407
2.35k
      }
408
2.38k
      if (!m_documentChunkIndex)
409
104
      {
410
104
        return false;
411
104
      }
412
2.28k
      const ContentChunkReference &documentChunk = m_contentChunks.at(m_documentChunkIndex.get());
413
2.28k
      for (unsigned int paletteChunkIndex : m_paletteChunkIndices)
414
2.53k
      {
415
2.53k
        const ContentChunkReference &paletteChunk = m_contentChunks.at(paletteChunkIndex);
416
2.53k
        input->seek(paletteChunk.offset, librevenge::RVNG_SEEK_SET);
417
2.53k
        if (! parsePaletteChunk(input, paletteChunk))
418
0
        {
419
0
          return false;
420
0
        }
421
2.53k
      }
422
2.28k
      for (unsigned int borderArtChunkIndex : m_borderArtChunkIndices)
423
2.33k
      {
424
2.33k
        const ContentChunkReference &baChunk =
425
2.33k
          m_contentChunks.at(borderArtChunkIndex);
426
2.33k
        input->seek(baChunk.offset, librevenge::RVNG_SEEK_SET);
427
2.33k
        if (!parseBorderArtChunk(input, baChunk))
428
0
        {
429
0
          return false;
430
0
        }
431
2.33k
      }
432
2.28k
      for (unsigned int shapeChunkIndex : m_shapeChunkIndices)
433
31.6k
      {
434
31.6k
        const ContentChunkReference &shapeChunk =
435
31.6k
          m_contentChunks.at(shapeChunkIndex);
436
31.6k
        input->seek(shapeChunk.offset, librevenge::RVNG_SEEK_SET);
437
31.6k
        if (!parseShape(input, shapeChunk))
438
123
        {
439
123
          return false;
440
123
        }
441
31.6k
      }
442
2.16k
      for (unsigned int fontChunkIndex : m_fontChunkIndices)
443
1.65k
      {
444
1.65k
        const ContentChunkReference &fontChunk =
445
1.65k
          m_contentChunks.at(fontChunkIndex);
446
1.65k
        input->seek(fontChunk.offset, librevenge::RVNG_SEEK_SET);
447
1.65k
        if (!parseFontChunk(input, fontChunk))
448
0
        {
449
0
          return false;
450
0
        }
451
1.65k
      }
452
2.16k
      input->seek(documentChunk.offset, librevenge::RVNG_SEEK_SET);
453
2.16k
      if (!parseDocumentChunk(input, documentChunk))
454
0
      {
455
0
        return false;
456
0
      }
457
2.16k
      for (unsigned int pageChunkIndex : m_pageChunkIndices)
458
10.3k
      {
459
10.3k
        const ContentChunkReference &pageChunk = m_contentChunks.at(pageChunkIndex);
460
10.3k
        input->seek(pageChunk.offset, librevenge::RVNG_SEEK_SET);
461
10.3k
        if (!parsePageChunk(input, pageChunk))
462
0
        {
463
0
          return false;
464
0
        }
465
10.3k
      }
466
2.16k
    }
467
8.22k
  }
468
2.79k
  input->seek(trailerOffset + trailerLength, librevenge::RVNG_SEEK_SET);
469
470
2.79k
  return true;
471
3.02k
}
472
473
#ifdef DEBUG
474
bool MSPUBParser::parseDocumentChunk(librevenge::RVNGInputStream *input, const ContentChunkReference &chunk)
475
#else
476
bool MSPUBParser::parseDocumentChunk(librevenge::RVNGInputStream *input, const ContentChunkReference &)
477
#endif
478
2.07k
{
479
2.07k
  MSPUB_DEBUG_MSG(("parseDocumentChunk: offset 0x%lx, end 0x%lx\n", input->tell(), chunk.end));
480
2.07k
  unsigned long begin = input->tell();
481
2.07k
  unsigned long len = readU32(input);
482
345k
  while (stillReading(input, begin + len))
483
343k
  {
484
343k
    MSPUBBlockInfo info = parseBlock(input);
485
343k
    if (info.id == DOCUMENT_SIZE)
486
2.65k
    {
487
37.2k
      while (stillReading(input, info.dataOffset + info.dataLength))
488
34.6k
      {
489
34.6k
        MSPUBBlockInfo subInfo = parseBlock(input, true);
490
34.6k
        if (subInfo.id == DOCUMENT_WIDTH)
491
2.67k
        {
492
2.67k
          m_collector->setWidthInEmu(subInfo.data);
493
2.67k
        }
494
31.9k
        else if (subInfo.id == DOCUMENT_HEIGHT)
495
2.01k
        {
496
2.01k
          m_collector->setHeightInEmu(subInfo.data);
497
2.01k
        }
498
34.6k
      }
499
2.65k
    }
500
340k
    else if (info.id == DOCUMENT_PAGE_LIST)
501
11.5k
    {
502
11.5k
      input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET);
503
73.0k
      while (stillReading(input, info.dataOffset + info.dataLength))
504
61.5k
      {
505
61.5k
        MSPUBBlockInfo subInfo = parseBlock(input, true);
506
61.5k
        if (subInfo.id == 0)
507
36.7k
        {
508
36.7k
          m_collector->setNextPage(subInfo.data);
509
36.7k
        }
510
61.5k
      }
511
11.5k
    }
512
329k
    else
513
329k
    {
514
329k
      skipBlock(input, info);
515
329k
    }
516
343k
  }
517
2.07k
  return true; //FIXME: return false for failure
518
2.07k
}
519
520
bool MSPUBParser::parseFontChunk(
521
  librevenge::RVNGInputStream *input, const ContentChunkReference &chunk)
522
1.65k
{
523
1.65k
  unsigned length = readU32(input);
524
146k
  while (stillReading(input, chunk.offset + length))
525
144k
  {
526
144k
    MSPUBBlockInfo info = parseBlock(input, true);
527
144k
    if (info.id == FONT_CONTAINER_ARRAY)
528
3.55k
    {
529
3.55k
      input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET);
530
135k
      while (stillReading(input, info.dataOffset + info.dataLength))
531
132k
      {
532
132k
        MSPUBBlockInfo subInfo = parseBlock(input, true);
533
132k
        if (subInfo.id == 0)
534
39.2k
        {
535
39.2k
          boost::optional<librevenge::RVNGString> name;
536
39.2k
          boost::optional<unsigned> eotOffset;
537
39.2k
          unsigned eotLength = 0;
538
39.2k
          input->seek(subInfo.dataOffset + 4, librevenge::RVNG_SEEK_SET);
539
106k
          while (stillReading(input, subInfo.dataOffset + subInfo.dataLength))
540
66.8k
          {
541
66.8k
            MSPUBBlockInfo subSubInfo = parseBlock(input, true);
542
66.8k
            if (subSubInfo.id == EMBEDDED_FONT_NAME)
543
9.12k
            {
544
9.12k
              name = librevenge::RVNGString();
545
              // drop trailing 0
546
              // TODO: This could be a general problem. Check.
547
9.12k
              const std::size_t len = subSubInfo.stringData.size();
548
9.12k
              if ((len > 2) && (subSubInfo.stringData[len - 1] == 0) && (subSubInfo.stringData[len - 2] == 0))
549
1.14k
              {
550
1.14k
                subSubInfo.stringData.pop_back();
551
1.14k
                subSubInfo.stringData.pop_back();
552
1.14k
              }
553
9.12k
              appendCharacters(name.get(), subSubInfo.stringData, "UTF-16LE");
554
9.12k
            }
555
57.6k
            else if (subSubInfo.id == EMBEDDED_EOT)
556
1.45k
            {
557
1.45k
              eotOffset = subSubInfo.dataOffset;
558
1.45k
              eotLength = subSubInfo.dataLength;
559
1.45k
            }
560
66.8k
          }
561
39.2k
          if (bool(name) && bool(eotOffset))
562
1.03k
          {
563
            // skip length, we've already read that
564
            // TODO: Why do we not read the data as part of the block info?
565
1.03k
            input->seek(eotOffset.get() + 4, librevenge::RVNG_SEEK_SET);
566
1.03k
            librevenge::RVNGBinaryData data;
567
1.03k
            unsigned long toRead = eotLength;
568
1.87k
            while (toRead > 0 && stillReading(input, (unsigned long)-1))
569
838
            {
570
838
              unsigned long howManyRead = 0;
571
838
              const unsigned char *buf = input->read(toRead, howManyRead);
572
838
              data.append(buf, howManyRead);
573
838
              toRead -= howManyRead;
574
838
            }
575
1.03k
            m_collector->addEOTFont(name.get(), data);
576
1.03k
            input->seek(subInfo.dataOffset + subInfo.dataLength, librevenge::RVNG_SEEK_SET);
577
1.03k
          }
578
39.2k
        }
579
132k
      }
580
3.55k
    }
581
144k
  }
582
1.65k
  return true;
583
1.65k
}
584
585
bool MSPUBParser::parseBorderArtChunk(
586
  librevenge::RVNGInputStream *input, const ContentChunkReference &chunk)
587
2.33k
{
588
2.33k
  unsigned length = readU32(input);
589
818k
  while (stillReading(input, chunk.offset + length))
590
815k
  {
591
815k
    MSPUBBlockInfo info = parseBlock(input, true);
592
815k
    if (info.id == BA_ARRAY)
593
18.6k
    {
594
18.6k
      input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET);
595
18.6k
      unsigned i = 0;
596
268k
      while (stillReading(input, info.dataOffset + info.dataLength))
597
250k
      {
598
250k
        MSPUBBlockInfo entry = parseBlock(input, false);
599
383k
        while (stillReading(input, entry.dataOffset + entry.dataLength))
600
133k
        {
601
133k
          MSPUBBlockInfo subRecord = parseBlock(input, true);
602
133k
          if (subRecord.id == BA_IMAGE_ARRAY)
603
1.74k
          {
604
1.74k
            input->seek(subRecord.dataOffset + 4, librevenge::RVNG_SEEK_SET);
605
143k
            while (stillReading(input, subRecord.dataOffset + subRecord.dataLength))
606
141k
            {
607
141k
              MSPUBBlockInfo subSubRecord = parseBlock(input, false);
608
141k
              if (subSubRecord.id == BA_IMAGE_CONTAINER)
609
40.7k
              {
610
40.7k
                MSPUBBlockInfo imgRecord = parseBlock(input, false);
611
40.7k
                if (imgRecord.id == BA_IMAGE)
612
3.87k
                {
613
3.87k
                  librevenge::RVNGBinaryData &img = *(m_collector->addBorderImage(
614
3.87k
                                                        WMF, i));
615
3.87k
                  unsigned long toRead = imgRecord.dataLength;
616
6.79k
                  while (toRead > 0 && stillReading(input, (unsigned long)-1))
617
2.91k
                  {
618
2.91k
                    unsigned long howManyRead = 0;
619
2.91k
                    const unsigned char *buf = input->read(toRead, howManyRead);
620
2.91k
                    img.append(buf, howManyRead);
621
2.91k
                    toRead -= howManyRead;
622
2.91k
                  }
623
3.87k
                }
624
40.7k
              }
625
141k
            }
626
1.74k
          }
627
131k
          else if (subRecord.id == BA_OFFSET_CONTAINER)
628
3.33k
          {
629
3.33k
            input->seek(subRecord.dataOffset + 4, librevenge::RVNG_SEEK_SET);
630
94.9k
            while (stillReading(
631
94.9k
                     input, subRecord.dataOffset + subRecord.dataLength))
632
91.6k
            {
633
91.6k
              MSPUBBlockInfo subSubRecord = parseBlock(input, true);
634
91.6k
              if (subSubRecord.id == BA_OFFSET_ENTRY)
635
21.0k
              {
636
21.0k
                m_collector->setBorderImageOffset(i, subSubRecord.data);
637
21.0k
              }
638
91.6k
            }
639
3.33k
          }
640
133k
        }
641
250k
        ++i;
642
250k
        input->seek(entry.dataOffset + entry.dataLength, librevenge::RVNG_SEEK_SET);
643
250k
      }
644
18.6k
    }
645
815k
  }
646
2.33k
  return true;
647
2.33k
}
648
649
bool MSPUBParser::parsePageChunk(librevenge::RVNGInputStream *input, const ContentChunkReference &chunk)
650
10.3k
{
651
10.3k
  MSPUB_DEBUG_MSG(("parsePageChunk: offset 0x%lx, end 0x%lx, seqnum 0x%x, parent 0x%x\n", input->tell(), chunk.end, chunk.seqNum, chunk.parentSeqNum));
652
10.3k
  unsigned long length = readU32(input);
653
10.3k
  PageType type = getPageTypeBySeqNum(chunk.seqNum);
654
10.3k
  if (type == NORMAL)
655
9.70k
  {
656
9.70k
    m_collector->addPage(chunk.seqNum);
657
9.70k
  }
658
3.01M
  while (stillReading(input, chunk.offset + length))
659
3.00M
  {
660
3.00M
    MSPUBBlockInfo info = parseBlock(input);
661
3.00M
    if (info.id == PAGE_BG_SHAPE)
662
13.4k
    {
663
13.4k
      m_collector->setPageBgShape(chunk.seqNum, info.data);
664
13.4k
    }
665
2.98M
    else if (info.id == PAGE_SHAPES)
666
43.3k
    {
667
43.3k
      parsePageShapeList(input, info, chunk.seqNum);
668
43.3k
    }
669
2.94M
    else if (info.id == THIS_MASTER_NAME)
670
18.5k
    {
671
18.5k
      for (unsigned char i : info.stringData)
672
209k
      {
673
209k
        if (i != 0)
674
93.5k
        {
675
93.5k
          m_collector->designateMasterPage(chunk.seqNum);
676
93.5k
        }
677
209k
      }
678
18.5k
    }
679
2.92M
    else if (info.id == APPLIED_MASTER_NAME)
680
33.8k
    {
681
33.8k
      m_collector->setMasterPage(chunk.seqNum, info.data);
682
33.8k
    }
683
2.89M
    else
684
2.89M
    {
685
2.89M
      skipBlock(input, info);
686
2.89M
    }
687
3.00M
  }
688
10.3k
  return true;
689
10.3k
}
690
691
bool MSPUBParser::parsePageShapeList(librevenge::RVNGInputStream *input, MSPUBBlockInfo info, unsigned pageSeqNum)
692
43.3k
{
693
43.3k
  MSPUB_DEBUG_MSG(("parsePageShapeList: page seqnum 0x%x\n", pageSeqNum));
694
267k
  while (stillReading(input, info.dataOffset + info.dataLength))
695
224k
  {
696
224k
    MSPUBBlockInfo subInfo = parseBlock(input, true);
697
224k
    if (subInfo.type == SHAPE_SEQNUM)
698
57.1k
    {
699
57.1k
      m_collector->setShapePage(subInfo.data, pageSeqNum);
700
57.1k
    }
701
224k
  }
702
43.3k
  return true;
703
43.3k
}
704
705
bool MSPUBParser::parseShape(librevenge::RVNGInputStream *input,
706
                             const ContentChunkReference &chunk)
707
31.6k
{
708
31.6k
  MSPUB_DEBUG_MSG(("parseShape: seqNum 0x%x\n", chunk.seqNum));
709
31.6k
  unsigned long pos = input->tell();
710
31.6k
  unsigned length = readU32(input);
711
31.6k
  bool isTable = chunk.type == TABLE;
712
31.6k
  bool isGroup = chunk.type == GROUP || chunk.type == LOGO;
713
31.6k
  if (isTable)
714
1.23k
  {
715
1.23k
    boost::optional<unsigned> cellsSeqNum;
716
1.23k
    boost::optional<unsigned> numRows;
717
1.23k
    boost::optional<unsigned> numCols;
718
1.23k
    boost::optional<unsigned> rowcolArrayOffset;
719
1.23k
    boost::optional<unsigned> textId;
720
114k
    while (stillReading(input, pos + length))
721
113k
    {
722
113k
      MSPUBBlockInfo info = parseBlock(input, true);
723
113k
      if (info.id == TABLE_CELLS_SEQNUM)
724
1.72k
      {
725
1.72k
        cellsSeqNum = info.data;
726
1.72k
      }
727
111k
      else if (info.id == TABLE_NUM_ROWS)
728
1.92k
      {
729
1.92k
        numRows = info.data;
730
1.92k
      }
731
109k
      else if (info.id == TABLE_NUM_COLS)
732
1.69k
      {
733
1.69k
        numCols = info.data;
734
1.69k
      }
735
108k
      else if (info.id == TABLE_ROWCOL_ARRAY)
736
1.58k
      {
737
1.58k
        rowcolArrayOffset = info.dataOffset;
738
1.58k
      }
739
106k
      else if (info.id == SHAPE_TEXT_ID)
740
1.49k
      {
741
1.49k
        textId = info.data;
742
1.49k
      }
743
113k
    }
744
1.23k
    if (bool(cellsSeqNum) && bool(numRows) && bool(numCols) && bool(rowcolArrayOffset))
745
1.17k
    {
746
1.17k
      unsigned nr = numRows.get();
747
1.17k
      unsigned nc = numCols.get();
748
1.17k
      unsigned rcao = rowcolArrayOffset.get();
749
1.17k
      unsigned csn = cellsSeqNum.get();
750
1.17k
      std::vector<unsigned> rowHeightsInEmu;
751
1.17k
      std::vector<unsigned> columnWidthsInEmu;
752
1.17k
      input->seek(rcao, librevenge::RVNG_SEEK_SET);
753
1.17k
      unsigned arrayLength = readU32(input);
754
100k
      while (stillReading(input, rcao + arrayLength))
755
99.4k
      {
756
99.4k
        MSPUBBlockInfo info = parseBlock(input, true);
757
99.4k
        if (info.id == 0)
758
39.3k
        {
759
39.3k
          input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET);
760
119k
          while (stillReading(input, info.dataOffset + info.dataLength))
761
80.3k
          {
762
80.3k
            MSPUBBlockInfo subInfo = parseBlock(input, true);
763
80.3k
            if (subInfo.id == TABLE_ROWCOL_SIZE)
764
12.9k
            {
765
12.9k
              if (columnWidthsInEmu.size() < nc)
766
3.28k
                columnWidthsInEmu.push_back(subInfo.data);
767
9.66k
              else if (rowHeightsInEmu.size() < nr)
768
4.81k
                rowHeightsInEmu.push_back(subInfo.data);
769
12.9k
            }
770
80.3k
          }
771
39.3k
        }
772
99.4k
      }
773
1.17k
      if (rowHeightsInEmu.size() != nr || columnWidthsInEmu.size() != nc)
774
36
      {
775
36
        MSPUB_DEBUG_MSG(("ERROR: Wrong number of rows or columns found in table definition.\n"));
776
36
        return false;
777
36
      }
778
1.13k
      boost::optional<unsigned> index;
779
3.10k
      for (size_t i = 0; i < m_cellsChunkIndices.size(); ++i)
780
3.07k
      {
781
3.07k
        if (m_contentChunks[m_cellsChunkIndices[i]].seqNum == csn)
782
1.10k
        {
783
1.10k
          index = i;
784
1.10k
          break;
785
1.10k
        }
786
3.07k
      }
787
788
1.13k
      TableInfo ti(nr, nc);
789
1.13k
      ti.m_rowHeightsInEmu = rowHeightsInEmu;
790
1.13k
      ti.m_columnWidthsInEmu = columnWidthsInEmu;
791
792
1.13k
      if (!index)
793
23
      {
794
23
        MSPUB_DEBUG_MSG(("WARNING: Couldn't find cells of seqnum %u corresponding to table of seqnum %u.\n",
795
23
                         csn, chunk.seqNum));
796
23
        return false;
797
23
      }
798
1.11k
      else
799
1.11k
      {
800
1.11k
        const ContentChunkReference &cellsChunk = m_contentChunks[m_cellsChunkIndices[get(index)]];
801
1.11k
        input->seek(cellsChunk.offset, librevenge::RVNG_SEEK_SET);
802
1.11k
        const unsigned cellsLength = readU32(input);
803
1.11k
        boost::optional<unsigned> cellCount;
804
163k
        while (stillReading(input, cellsChunk.offset + cellsLength))
805
162k
        {
806
162k
          MSPUBBlockInfo info = parseBlock(input, true);
807
162k
          switch (info.id)
808
162k
          {
809
3.01k
          case 0x01:
810
3.01k
            cellCount = info.data;
811
3.01k
            break;
812
3.19k
          case 0x02:
813
3.19k
          {
814
3.19k
            input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET);
815
42.3k
            while (stillReading(input, info.dataOffset + info.dataLength))
816
39.1k
            {
817
39.1k
              const MSPUBBlockInfo itemInfo = parseBlock(input, true);
818
39.1k
              if (itemInfo.id == 0)
819
19.9k
              {
820
19.9k
                input->seek(itemInfo.dataOffset + 4, librevenge::RVNG_SEEK_SET);
821
19.9k
                CellInfo currentCell;
822
135k
                while (stillReading(input, itemInfo.dataOffset + itemInfo.dataLength))
823
115k
                {
824
115k
                  const MSPUBBlockInfo subInfo = parseBlock(input, true);
825
115k
                  switch (subInfo.id)
826
115k
                  {
827
8.39k
                  case 0x01:
828
8.39k
                    currentCell.m_startRow = subInfo.data;
829
8.39k
                    break;
830
8.46k
                  case 0x02:
831
8.46k
                    currentCell.m_endRow = subInfo.data;
832
8.46k
                    break;
833
6.68k
                  case 0x03:
834
6.68k
                    currentCell.m_startColumn = subInfo.data;
835
6.68k
                    break;
836
7.68k
                  case 0x04:
837
7.68k
                    currentCell.m_endColumn = subInfo.data;
838
7.68k
                    break;
839
                  // TODO: 0x09 - 0x0e: width/height of content + margins?
840
84.0k
                  default:
841
84.0k
                    break;
842
115k
                  }
843
115k
                }
844
19.9k
                ti.m_cells.push_back(currentCell);
845
19.9k
              }
846
39.1k
            }
847
848
3.19k
            break;
849
3.19k
          }
850
156k
          default:
851
156k
            break;
852
162k
          }
853
162k
        }
854
855
1.10k
        if (bool(cellCount) && (get(cellCount) != ti.m_cells.size()))
856
426
        {
857
426
          MSPUB_DEBUG_MSG(("%u cell records expected, but read %u\n", get(cellCount), unsigned(ti.m_cells.size())));
858
426
        }
859
1.10k
      }
860
861
1.10k
      m_collector->setShapeTableInfo(chunk.seqNum, ti);
862
1.10k
      if (bool(textId))
863
993
        m_collector->addTextShape(get(textId), chunk.seqNum);
864
1.10k
      return true;
865
1.13k
    }
866
66
    return false;
867
1.23k
  }
868
30.3k
  else
869
30.3k
  {
870
30.3k
    bool isText = false;
871
30.3k
    bool shouldStretchBorderArt = true;
872
30.3k
    unsigned textId = 0;
873
30.3k
    unsigned width = 0;
874
30.3k
    unsigned height = 0;
875
5.03M
    while (stillReading(input, pos + length))
876
5.00M
    {
877
5.00M
      MSPUBBlockInfo info = parseBlock(input, true);
878
5.00M
      if (info.id == SHAPE_WIDTH)
879
55.9k
      {
880
55.9k
        width = info.data;
881
55.9k
      }
882
4.95M
      else if (info.id == SHAPE_HEIGHT)
883
55.8k
      {
884
55.8k
        height = info.data;
885
55.8k
      }
886
4.89M
      else if (info.id == SHAPE_BORDER_IMAGE_ID)
887
18.1k
      {
888
18.1k
        m_collector->setShapeBorderImageId(chunk.seqNum, info.data);
889
18.1k
      }
890
4.87M
      else if (info.id == SHAPE_DONT_STRETCH_BA)
891
38.1k
      {
892
38.1k
        shouldStretchBorderArt = false;
893
38.1k
      }
894
4.84M
      else if (info.id == SHAPE_TEXT_ID)
895
15.8k
      {
896
15.8k
        textId = info.data;
897
15.8k
        isText = true;
898
15.8k
      }
899
4.82M
      else if (info.id == SHAPE_VALIGN)
900
7.63k
      {
901
7.63k
        m_collector->setShapeVerticalTextAlign(chunk.seqNum,
902
7.63k
                                               static_cast<VerticalAlign>(info.data));
903
7.63k
      }
904
4.81M
      else if (info.id == SHAPE_CROP && info.data != 0)
905
411
      {
906
411
        m_collector->setShapeCropType(chunk.seqNum,
907
411
                                      static_cast<ShapeType>(info.data));
908
411
      }
909
5.00M
    }
910
30.3k
    if (shouldStretchBorderArt)
911
26.3k
    {
912
26.3k
      m_collector->setShapeStretchBorderArt(chunk.seqNum);
913
26.3k
    }
914
30.3k
    bool parseWithoutDimensions = true; //FIXME: Should we ever ignore if height and width not given?
915
30.3k
    if (isGroup || (height > 0 && width > 0) || parseWithoutDimensions)
916
30.3k
    {
917
30.3k
      if (! isGroup)
918
29.1k
      {
919
29.1k
        if (isText)
920
5.95k
        {
921
5.95k
          m_collector->addTextShape(textId, chunk.seqNum);
922
5.95k
        }
923
29.1k
      }
924
30.3k
    }
925
11
    else
926
11
    {
927
11
      MSPUB_DEBUG_MSG(("Height and width not both specified, ignoring. (Height: 0x%x, Width: 0x%x)\n", height, width));
928
11
    }
929
30.3k
    return true;
930
30.3k
  }
931
31.6k
}
932
933
QuillChunkReference MSPUBParser::parseQuillChunkReference(librevenge::RVNGInputStream *input)
934
82.6k
{
935
82.6k
  QuillChunkReference ret;
936
82.6k
  readU16(input); //FIXME: Can we do something sensible if this is not 0x18 ?
937
82.6k
  char name[5];
938
413k
  for (int i = 0; i < 4; ++i)
939
330k
  {
940
330k
    name[i] = (char)readU8(input);
941
330k
  }
942
82.6k
  name[4] = '\0';
943
82.6k
  ret.name = name;
944
82.6k
  ret.id = readU16(input);
945
82.6k
  input->seek(input->tell() + 4, librevenge::RVNG_SEEK_SET); //Seek past what is normally 0x01000000. We don't know what this represents.
946
82.6k
  char name2[5];
947
413k
  for (int i = 0; i < 4; ++i)
948
330k
  {
949
330k
    name2[i] = (char)readU8(input);
950
330k
  }
951
82.6k
  name2[4] = '\0';
952
82.6k
  ret.name2 = name2;
953
82.6k
  ret.offset = readU32(input);
954
82.6k
  ret.length = readU32(input);
955
82.6k
  return ret;
956
82.6k
}
957
958
std::vector<unsigned> MSPUBParser::parseTableCellDefinitions(
959
  librevenge::RVNGInputStream *input, const QuillChunkReference &chunk)
960
1.52k
{
961
1.52k
  std::vector<unsigned> ret;
962
1.52k
  unsigned numElements = readU32(input) + 1;
963
1.52k
  input->seek(chunk.offset + 0xC, librevenge::RVNG_SEEK_SET);
964
56.7k
  for (unsigned i = 0; i < numElements; ++i)
965
55.2k
  {
966
55.2k
    ret.push_back(readU32(input));
967
    // compensate for all but the last offset not including the terminating 0x0D00
968
55.2k
    if (i != numElements - 1)
969
53.7k
    {
970
53.7k
      ret.back() += 2;
971
53.7k
    }
972
55.2k
  }
973
1.52k
  return ret;
974
1.52k
}
975
976
bool MSPUBParser::parseQuill(librevenge::RVNGInputStream *input)
977
4.24k
{
978
4.24k
  MSPUB_DEBUG_MSG(("MSPUBParser::parseQuill\n"));
979
4.24k
  unsigned chunkReferenceListOffset = 0x18;
980
4.24k
  std::list<QuillChunkReference> chunkReferences;
981
4.24k
  std::set<unsigned> readChunks; // guard against cycle in the chunk list
982
8.87k
  while (chunkReferenceListOffset != 0xffffffff)
983
6.70k
  {
984
6.70k
    input->seek(chunkReferenceListOffset + 2, librevenge::RVNG_SEEK_SET);
985
6.70k
    unsigned short numChunks = readU16(input);
986
6.70k
    chunkReferenceListOffset = readU32(input);
987
6.70k
    if (readChunks.find(chunkReferenceListOffset) != readChunks.end())
988
2.06k
    {
989
2.06k
      MSPUB_DEBUG_MSG(("Found a cycle in chunk reference list: a broken file!\n"));
990
2.06k
      break;
991
2.06k
    }
992
4.63k
    readChunks.insert(chunkReferenceListOffset);
993
87.3k
    for (unsigned i = 0; i < numChunks; ++i)
994
82.6k
    {
995
82.6k
      QuillChunkReference quillChunkReference = parseQuillChunkReference(input);
996
82.6k
      chunkReferences.push_back(quillChunkReference);
997
82.6k
    }
998
4.63k
  }
999
4.24k
  MSPUB_DEBUG_MSG(("Found %u Quill chunks\n", (unsigned)chunkReferences.size()));
1000
  //Make sure we parse the STRS chunk before the TEXT chunk
1001
4.24k
  std::list<QuillChunkReference>::const_iterator textChunkReference = chunkReferences.end();
1002
4.24k
  bool parsedStrs = false;
1003
4.24k
  bool parsedSyid = false;
1004
4.24k
  bool parsedFdpc = false;
1005
4.24k
  bool parsedFdpp = false;
1006
4.24k
  bool parsedStsh = false;
1007
4.24k
  bool parsedFont = false;
1008
4.24k
  std::vector<unsigned> textLengths;
1009
4.24k
  std::vector<unsigned> textIDs;
1010
4.24k
  std::vector<unsigned> textOffsets;
1011
4.24k
  std::map<unsigned, std::vector<unsigned> > tableCellTextEnds;
1012
4.24k
  unsigned textOffsetAccum = 0;
1013
4.24k
  std::vector<TextSpanReference> spans;
1014
4.24k
  std::vector<TextParagraphReference> paras;
1015
4.24k
  unsigned whichStsh = 0;
1016
81.0k
  for (std::list<QuillChunkReference>::const_iterator i = chunkReferences.begin(); i != chunkReferences.end(); ++i)
1017
76.7k
  {
1018
76.7k
    if (i->name == "TEXT")
1019
1.38k
    {
1020
1.38k
      textChunkReference = i;
1021
1.38k
    }
1022
75.4k
    else if (i->name == "STRS")
1023
1.08k
    {
1024
1.08k
      input->seek(i->offset, librevenge::RVNG_SEEK_SET);
1025
1.08k
      unsigned numLengths = readU32(input); //Assuming the first DWORD is the number of children and that the next is the remaining length before children start. We are unsure that this is correct.
1026
1.08k
      input->seek(4 + i->offset + readU32(input), librevenge::RVNG_SEEK_SET);
1027
26.2k
      for (unsigned j = 0; j < numLengths; ++j)
1028
25.1k
      {
1029
25.1k
        unsigned length = readU32(input);
1030
25.1k
        textLengths.push_back(length);
1031
25.1k
        textOffsets.push_back(textOffsetAccum);
1032
25.1k
        textOffsetAccum += length * 2;
1033
25.1k
      }
1034
1.08k
      parsedStrs = true;
1035
1.08k
    }
1036
74.3k
    else if (i->name == "SYID")
1037
1.15k
    {
1038
1.15k
      input->seek(i->offset, librevenge::RVNG_SEEK_SET);
1039
1.15k
      readU32(input); // Don't know what the first DWORD means.
1040
1.15k
      unsigned numIDs = readU32(input);
1041
74.1k
      for (unsigned j = 0; j < numIDs; ++j)
1042
72.9k
      {
1043
72.9k
        textIDs.push_back(readU32(input));
1044
72.9k
      }
1045
1.15k
      parsedSyid = true;
1046
1.15k
    }
1047
73.1k
    else if (i->name == "PL  ")
1048
608
    {
1049
608
      input->seek(i->offset, librevenge::RVNG_SEEK_SET);
1050
608
      parseColors(input, *i);
1051
608
    }
1052
72.5k
    else if (i->name == "FDPC")
1053
2.45k
    {
1054
2.45k
      input->seek(i->offset, librevenge::RVNG_SEEK_SET);
1055
2.45k
      std::vector<TextSpanReference> thisBlockSpans = parseCharacterStyles(input, *i);
1056
2.45k
      spans.insert(spans.end(), thisBlockSpans.begin(), thisBlockSpans.end());
1057
2.45k
      parsedFdpc |= !thisBlockSpans.empty();
1058
2.45k
    }
1059
70.0k
    else if (i->name == "FDPP")
1060
2.08k
    {
1061
2.08k
      input->seek(i->offset, librevenge::RVNG_SEEK_SET);
1062
2.08k
      std::vector<TextParagraphReference> thisBlockParas = parseParagraphStyles(input, *i);
1063
2.08k
      paras.insert(paras.end(), thisBlockParas.begin(), thisBlockParas.end());
1064
2.08k
      parsedFdpp |= !thisBlockParas.empty();
1065
2.08k
    }
1066
68.0k
    else if (i->name == "STSH")
1067
2.83k
    {
1068
2.83k
      if (whichStsh++ == 1)
1069
1.13k
      {
1070
1.13k
        input->seek(i->offset, librevenge::RVNG_SEEK_SET);
1071
1.13k
        parseDefaultStyle(input, *i);
1072
1.13k
        parsedStsh = true;
1073
1.13k
      }
1074
2.83k
    }
1075
65.1k
    else if (i->name == "FONT")
1076
1.02k
    {
1077
1.02k
      input->seek(i->offset, librevenge::RVNG_SEEK_SET);
1078
1.02k
      parseFonts(input, *i);
1079
1.02k
      parsedFont = true;
1080
1.02k
    }
1081
64.1k
    else if (i->name == "TCD ")
1082
1.52k
    {
1083
1.52k
      input->seek(i->offset, librevenge::RVNG_SEEK_SET);
1084
1.52k
      tableCellTextEnds[i->id] = parseTableCellDefinitions(input, *i);
1085
1.52k
    }
1086
76.7k
  }
1087
4.24k
  if (parsedStrs && parsedSyid && parsedFdpc && parsedFdpp && parsedStsh && parsedFont && textChunkReference != chunkReferences.end())
1088
819
  {
1089
819
    input->seek(textChunkReference->offset, librevenge::RVNG_SEEK_SET);
1090
819
    unsigned bytesRead = 0;
1091
819
    auto currentTextSpan = spans.begin();
1092
819
    auto currentTextPara = paras.begin();
1093
11.9k
    for (unsigned j = 0; j < textIDs.size() && j < textLengths.size(); ++j)
1094
11.1k
    {
1095
11.1k
      MSPUB_DEBUG_MSG(("Parsing a text block.\n"));
1096
11.1k
      std::vector<TextParagraph> readParas;
1097
11.1k
      std::vector<TextSpan> readSpans;
1098
11.1k
      std::vector<unsigned char> text;
1099
837k
      for (unsigned k = 0; k < textLengths[j] && currentTextPara != paras.end() && currentTextSpan != spans.end(); ++k)
1100
826k
      {
1101
826k
        text.push_back(readU8(input));
1102
826k
        text.push_back(readU8(input));
1103
826k
        bytesRead += 2;
1104
826k
        if (bytesRead >= currentTextSpan->last - textChunkReference->offset)
1105
16.0k
        {
1106
16.0k
          if (!text.empty())
1107
16.0k
          {
1108
16.0k
            readSpans.push_back(TextSpan(text, currentTextSpan->charStyle));
1109
16.0k
            MSPUB_DEBUG_MSG(("Saw text span %d in the current text paragraph.\n", (unsigned)readSpans.size()));
1110
16.0k
          }
1111
16.0k
          ++currentTextSpan;
1112
16.0k
          text.clear();
1113
16.0k
        }
1114
826k
        if (bytesRead >= currentTextPara->last - textChunkReference->offset)
1115
21.4k
        {
1116
21.4k
          if (!text.empty())
1117
16.9k
          {
1118
16.9k
            readSpans.push_back(TextSpan(text, currentTextSpan->charStyle));
1119
16.9k
            MSPUB_DEBUG_MSG(("Saw text span %d in the current text paragraph.\n", (unsigned)readSpans.size()));
1120
16.9k
          }
1121
21.4k
          text.clear();
1122
21.4k
          if (!readSpans.empty())
1123
21.4k
          {
1124
21.4k
            readParas.push_back(TextParagraph(readSpans, currentTextPara->paraStyle));
1125
21.4k
            MSPUB_DEBUG_MSG(("Saw paragraph %d in the current text block.\n", (unsigned)readParas.size()));
1126
21.4k
          }
1127
21.4k
          ++currentTextPara;
1128
21.4k
          readSpans.clear();
1129
21.4k
        }
1130
826k
      }
1131
11.1k
      if (!text.empty() && currentTextSpan != spans.end())
1132
1.48k
      {
1133
1.48k
        readSpans.push_back(TextSpan(text, currentTextSpan->charStyle));
1134
1.48k
        MSPUB_DEBUG_MSG(("Saw text span %d in the current text paragraph.\n", (unsigned)readSpans.size()));
1135
1.48k
      }
1136
11.1k
      text.clear();
1137
11.1k
      if (!readSpans.empty() && currentTextPara != paras.end())
1138
1.94k
      {
1139
1.94k
        readParas.push_back(TextParagraph(readSpans, currentTextPara->paraStyle));
1140
1.94k
        MSPUB_DEBUG_MSG(("Saw paragraph %d in the current text block.\n", (unsigned)readParas.size()));
1141
1.94k
      }
1142
11.1k
      m_collector->addTextString(readParas, textIDs[j]);
1143
11.1k
      m_collector->setTextStringOffset(textIDs[j], textOffsets[j]);
1144
11.1k
      const std::map<unsigned, std::vector<unsigned> >::const_iterator it = tableCellTextEnds.find(j);
1145
11.1k
      if (it != tableCellTextEnds.end())
1146
868
        m_collector->setTableCellTextEnds(textIDs[j], it->second);
1147
11.1k
    }
1148
819
    textChunkReference = chunkReferences.end();
1149
819
  }
1150
4.24k
  return true;
1151
4.24k
}
1152
1153
void MSPUBParser::parseFonts(librevenge::RVNGInputStream *input, const QuillChunkReference &)
1154
1.02k
{
1155
1.02k
  readU32(input);
1156
1.02k
  unsigned numElements = readU32(input);
1157
1.02k
  input->seek(input->tell() + 12 + 4 * numElements, librevenge::RVNG_SEEK_SET);
1158
7.76k
  for (unsigned i = 0; i < numElements; ++i)
1159
6.73k
  {
1160
6.73k
    unsigned short nameLength = readU16(input);
1161
6.73k
    if (nameLength > 0)
1162
5.59k
    {
1163
5.59k
      std::vector<unsigned char> name;
1164
5.59k
      readNBytes(input, nameLength * 2, name);
1165
5.59k
      m_collector->addFont(name);
1166
5.59k
    }
1167
6.73k
    readU32(input);
1168
6.73k
  }
1169
1.02k
}
1170
1171
void MSPUBParser::parseDefaultStyle(librevenge::RVNGInputStream *input, const QuillChunkReference &chunk)
1172
1.13k
{
1173
1.13k
  readU32(input);
1174
1.13k
  unsigned numElements = std::min(readU32(input), m_length);
1175
1.13k
  input->seek(input->tell() + 12, librevenge::RVNG_SEEK_SET);
1176
1.13k
  std::vector<unsigned> offsets;
1177
1.13k
  offsets.reserve(numElements);
1178
20.2k
  for (unsigned i = 0; i < numElements; ++i)
1179
19.1k
  {
1180
19.1k
    offsets.push_back(readU32(input));
1181
19.1k
  }
1182
8.06k
  for (unsigned i = 0; i < numElements; ++i)
1183
6.92k
  {
1184
6.92k
    input->seek(chunk.offset + 20 + offsets[i], librevenge::RVNG_SEEK_SET);
1185
6.92k
    readU16(input);
1186
6.92k
    if (i % 2 == 0)
1187
3.46k
    {
1188
      //FIXME: Does STSH2 hold information for associating style indices in FDPP to indices in STSH1 ?
1189
3.46k
      m_collector->addDefaultCharacterStyle(getCharacterStyle(input));
1190
3.46k
    }
1191
3.46k
    else
1192
3.46k
    {
1193
3.46k
      m_collector->addDefaultParagraphStyle(getParagraphStyle(input));
1194
3.46k
    }
1195
6.92k
  }
1196
1.13k
}
1197
1198
1199
void MSPUBParser::parseColors(librevenge::RVNGInputStream *input, const QuillChunkReference &)
1200
608
{
1201
608
  unsigned numEntries = readU32(input);
1202
608
  input->seek(input->tell() + 8, librevenge::RVNG_SEEK_SET);
1203
1.52k
  for (unsigned i = 0; i < numEntries; ++i)
1204
917
  {
1205
917
    unsigned blocksOffset = input->tell();
1206
917
    unsigned len = readU32(input);
1207
22.9k
    while (stillReading(input, blocksOffset + len))
1208
22.0k
    {
1209
22.0k
      MSPUBBlockInfo info = parseBlock(input, true);
1210
22.0k
      if (info.id == 0x01)
1211
1.03k
      {
1212
1.03k
        m_collector->addTextColor(ColorReference(info.data));
1213
1.03k
      }
1214
22.0k
    }
1215
917
  }
1216
608
}
1217
1218
std::vector<MSPUBParser::TextParagraphReference> MSPUBParser::parseParagraphStyles(librevenge::RVNGInputStream *input, const QuillChunkReference &chunk)
1219
2.08k
{
1220
2.08k
  std::vector<TextParagraphReference> ret;
1221
2.08k
  unsigned short numEntries = readU16(input);
1222
2.08k
  input->seek(input->tell() + 6, librevenge::RVNG_SEEK_SET);
1223
2.08k
  std::vector<unsigned> textOffsets;
1224
2.08k
  textOffsets.reserve(numEntries);
1225
2.08k
  std::vector<unsigned short> chunkOffsets;
1226
2.08k
  textOffsets.reserve(numEntries);
1227
85.2k
  for (unsigned short i = 0; i < numEntries; ++i)
1228
83.1k
  {
1229
83.1k
    textOffsets.push_back(readU32(input));
1230
83.1k
  }
1231
78.2k
  for (unsigned short i = 0; i < numEntries; ++i)
1232
76.2k
  {
1233
76.2k
    chunkOffsets.push_back(readU16(input));
1234
76.2k
  }
1235
2.08k
  unsigned currentSpanBegin = 0;
1236
74.1k
  for (unsigned short i = 0; i < numEntries; ++i)
1237
72.1k
  {
1238
72.1k
    input->seek(chunk.offset + chunkOffsets[i], librevenge::RVNG_SEEK_SET);
1239
72.1k
    ParagraphStyle style = getParagraphStyle(input);
1240
72.1k
    ret.push_back(TextParagraphReference(currentSpanBegin, textOffsets[i], style));
1241
72.1k
    currentSpanBegin = textOffsets[i] + 1;
1242
72.1k
  }
1243
2.08k
  return ret;
1244
2.08k
}
1245
1246
std::vector<MSPUBParser::TextSpanReference> MSPUBParser::parseCharacterStyles(librevenge::RVNGInputStream *input, const QuillChunkReference &chunk)
1247
2.45k
{
1248
2.45k
  unsigned short numEntries = readU16(input);
1249
2.45k
  input->seek(input->tell() + 6, librevenge::RVNG_SEEK_SET);
1250
2.45k
  std::vector<unsigned> textOffsets;
1251
2.45k
  textOffsets.reserve(numEntries);
1252
2.45k
  std::vector<unsigned short> chunkOffsets;
1253
2.45k
  chunkOffsets.reserve(numEntries);
1254
2.45k
  std::vector<TextSpanReference> ret;
1255
83.6k
  for (unsigned short i = 0; i < numEntries; ++i)
1256
81.2k
  {
1257
81.2k
    textOffsets.push_back(readU32(input));
1258
81.2k
  }
1259
58.8k
  for (unsigned short i = 0; i < numEntries; ++i)
1260
56.4k
  {
1261
56.4k
    chunkOffsets.push_back(readU16(input));
1262
56.4k
  }
1263
2.45k
  unsigned currentSpanBegin = 0;
1264
53.3k
  for (unsigned short i = 0; i < numEntries; ++i)
1265
50.9k
  {
1266
50.9k
    input->seek(chunk.offset + chunkOffsets[i], librevenge::RVNG_SEEK_SET);
1267
50.9k
    CharacterStyle style = getCharacterStyle(input);
1268
50.9k
    currentSpanBegin = textOffsets[i] + 1;
1269
50.9k
    ret.push_back(TextSpanReference(currentSpanBegin, textOffsets[i], style));
1270
50.9k
  }
1271
2.45k
  return ret;
1272
2.45k
}
1273
ParagraphStyle MSPUBParser::getParagraphStyle(librevenge::RVNGInputStream *input)
1274
75.5k
{
1275
75.5k
  ParagraphStyle ret;
1276
1277
75.5k
  bool isList = false;
1278
75.5k
  unsigned bulletChar = 0;
1279
75.5k
  NumberingType numberingType = STANDARD_WESTERN;
1280
75.5k
  NumberingDelimiter numberingDelimiter = NO_DELIMITER;
1281
75.5k
  boost::optional<unsigned> numberIfRestarted;
1282
1283
75.5k
  unsigned offset = input->tell();
1284
75.5k
  unsigned len = readU32(input);
1285
29.5M
  while (stillReading(input, offset + len))
1286
29.4M
  {
1287
29.4M
    MSPUBBlockInfo info = parseBlock(input, true);
1288
29.4M
    switch (info.id)
1289
29.4M
    {
1290
171k
    case PARAGRAPH_ALIGNMENT:
1291
171k
      ret.m_align = (Alignment)(info.data & 0xFF); // Is this correct?
1292
171k
      break;
1293
22.4k
    case PARAGRAPH_DEFAULT_CHAR_STYLE:
1294
22.4k
      ret.m_defaultCharStyleIndex = info.data;
1295
22.4k
      break;
1296
62.8k
    case PARAGRAPH_LINE_SPACING:
1297
62.8k
      if (info.data & 1)
1298
4.08k
      {
1299
        // line spacing expressed in points in the UI,
1300
        // in eighths of an emu in the file format.
1301
        // (WTF??)
1302
4.08k
        ret.m_lineSpacing = LineSpacingInfo(LINE_SPACING_PT,
1303
4.08k
                                            static_cast<double>(info.data - 1) / 8 * 72 / EMUS_IN_INCH);
1304
4.08k
      }
1305
58.7k
      else if (info.data & 2)
1306
4.17k
      {
1307
        // line spacing expressed in SP in the UI,
1308
        // in what would be EMUs if font size were 96pt in the file format
1309
        // (WTF??)
1310
4.17k
        ret.m_lineSpacing = LineSpacingInfo(LINE_SPACING_SP,
1311
4.17k
                                            static_cast<double>(info.data - 2) / EMUS_IN_INCH * 72 / 96);
1312
4.17k
      }
1313
62.8k
      break;
1314
226k
    case PARAGRAPH_SPACE_BEFORE:
1315
226k
      ret.m_spaceBeforeEmu = info.data;
1316
226k
      break;
1317
44.4k
    case PARAGRAPH_SPACE_AFTER:
1318
44.4k
      ret.m_spaceAfterEmu = info.data;
1319
44.4k
      break;
1320
60.6k
    case PARAGRAPH_FIRST_LINE_INDENT:
1321
60.6k
      ret.m_firstLineIndentEmu = (int)info.data;
1322
60.6k
      break;
1323
291k
    case PARAGRAPH_LEFT_INDENT:
1324
291k
      ret.m_leftIndentEmu = info.data;
1325
291k
      break;
1326
29.5k
    case PARAGRAPH_RIGHT_INDENT:
1327
29.5k
      ret.m_rightIndentEmu = info.data;
1328
29.5k
      break;
1329
173k
    case PARAGRAPH_TABS:
1330
173k
      input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET);
1331
630k
      while (stillReading(input, info.dataOffset + info.dataLength))
1332
457k
      {
1333
457k
        MSPUBBlockInfo tabArrayInfo = parseBlock(input, true);
1334
457k
        if (tabArrayInfo.id == TAB_ARRAY)
1335
14.6k
        {
1336
14.6k
          input->seek(tabArrayInfo.dataOffset + 4, librevenge::RVNG_SEEK_SET);
1337
158k
          while (stillReading(input, tabArrayInfo.dataOffset + tabArrayInfo.dataLength))
1338
143k
          {
1339
143k
            MSPUBBlockInfo tabEntryInfo = parseBlock(input, true);
1340
143k
            if (tabEntryInfo.type == GENERAL_CONTAINER)
1341
22.4k
            {
1342
22.4k
              input->seek(tabEntryInfo.dataOffset + 4, librevenge::RVNG_SEEK_SET);
1343
22.4k
              MSPUBBlockInfo tabInfo = parseBlock(input, true);
1344
22.4k
              if (tabInfo.id == TAB_AMOUNT)
1345
21.9k
              {
1346
21.9k
                ret.m_tabStopsInEmu.push_back(tabInfo.data);
1347
21.9k
              }
1348
22.4k
            }
1349
143k
          }
1350
14.6k
        }
1351
457k
      }
1352
173k
      break;
1353
13.9k
    case PARAGRAPH_LIST_INFO:
1354
13.9k
    {
1355
13.9k
      isList = true;
1356
13.9k
      input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET);
1357
34.2k
      while (stillReading(input, info.dataOffset + info.dataLength))
1358
20.3k
      {
1359
20.3k
        MSPUBBlockInfo listSubInfo = parseBlock(input, true);
1360
20.3k
        switch (listSubInfo.id)
1361
20.3k
        {
1362
12.1k
        case PARAGRAPH_LIST_NUMBERING_TYPE:
1363
12.1k
          numberingType = static_cast<NumberingType>(info.data);
1364
12.1k
          break;
1365
947
        case PARAGRAPH_LIST_BULLET_CHAR:
1366
947
          bulletChar = info.data;
1367
947
          break;
1368
7.27k
        default:
1369
7.27k
          break;
1370
20.3k
        }
1371
20.3k
      }
1372
13.9k
      break;
1373
13.9k
    }
1374
35.2k
    case PARAGRAPH_LIST_NUMBER_RESTART:
1375
35.2k
      numberIfRestarted = info.data;
1376
35.2k
      break;
1377
180k
    case PARAGRAPH_DROP_CAP_LINES:
1378
180k
      ret.m_dropCapLines = info.data;
1379
180k
      break;
1380
31.0k
    case PARAGRAPH_DROP_CAP_LETTERS:
1381
31.0k
      ret.m_dropCapLetters = info.data;
1382
31.0k
      break;
1383
28.1M
    default:
1384
28.1M
      break;
1385
29.4M
    }
1386
29.4M
  }
1387
75.5k
  if (isList)
1388
12.2k
  {
1389
12.2k
    if (bulletChar)
1390
0
    {
1391
0
      ret.m_listInfo = ListInfo(bulletChar);
1392
0
    }
1393
12.2k
    else
1394
12.2k
    {
1395
12.2k
      ret.m_listInfo = ListInfo(numberIfRestarted, numberingType,
1396
12.2k
                                numberingDelimiter);
1397
12.2k
    }
1398
12.2k
  }
1399
1400
75.5k
  return ret;
1401
75.5k
}
1402
1403
CharacterStyle MSPUBParser::getCharacterStyle(librevenge::RVNGInputStream *input)
1404
54.3k
{
1405
54.3k
  CharacterStyle style;
1406
1407
54.3k
  bool seenBold1 = false, seenBold2 = false, seenItalic1 = false, seenItalic2 = false;
1408
54.3k
  int textSize1 = -1, /* textSize2 = -1,*/ colorIndex = -1;
1409
54.3k
  boost::optional<unsigned> fontIndex;
1410
54.3k
  unsigned offset = input->tell();
1411
54.3k
  unsigned len = readU32(input);
1412
21.1M
  while (stillReading(input, offset + len))
1413
21.1M
  {
1414
21.1M
    MSPUBBlockInfo info = parseBlock(input, true);
1415
21.1M
    switch (info.id)
1416
21.1M
    {
1417
64.1k
    case BOLD_1_ID:
1418
64.1k
      seenBold1 = true;
1419
64.1k
      break;
1420
15.1k
    case BOLD_2_ID:
1421
15.1k
      seenBold2 = true;
1422
15.1k
      break;
1423
28.7k
    case ITALIC_1_ID:
1424
28.7k
      seenItalic1 = true;
1425
28.7k
      break;
1426
20.8k
    case ITALIC_2_ID:
1427
20.8k
      seenItalic2 = true;
1428
20.8k
      break;
1429
25.7k
    case UNDERLINE_ID:
1430
25.7k
      style.underline = readUnderline(info.data);
1431
25.7k
      break;
1432
26.4k
    case TEXT_SIZE_1_ID:
1433
26.4k
      textSize1 = info.data;
1434
26.4k
      break;
1435
25.8k
    case TEXT_SIZE_2_ID:
1436
      // textSize2 = info.data;
1437
25.8k
      break;
1438
60.0k
    case BARE_COLOR_INDEX_ID:
1439
60.0k
      colorIndex = info.data;
1440
60.0k
      break;
1441
25.7k
    case COLOR_INDEX_CONTAINER_ID:
1442
25.7k
      colorIndex = getColorIndex(input, info);
1443
25.7k
      break;
1444
26.8k
    case FONT_INDEX_CONTAINER_ID:
1445
26.8k
      fontIndex = getFontIndex(input, info);
1446
26.8k
      break;
1447
13.1k
    case SUPER_SUB_TYPE_ID:
1448
13.1k
      style.superSubType = static_cast<SuperSubType>(info.data);
1449
13.1k
      break;
1450
69.4k
    case OUTLINE_ID:
1451
69.4k
      style.outline = true;
1452
69.4k
      break;
1453
31.1k
    case SHADOW_ID:
1454
31.1k
      style.shadow = true;
1455
31.1k
      break;
1456
19.8k
    case SMALL_CAPS_ID:
1457
19.8k
      style.smallCaps = true;
1458
19.8k
      break;
1459
13.4k
    case ALL_CAPS_ID:
1460
13.4k
      style.allCaps = true;
1461
13.4k
      break;
1462
14.6k
    case EMBOSS_ID:
1463
14.6k
      style.emboss = true;
1464
14.6k
      break;
1465
19.6k
    case ENGRAVE_ID:
1466
19.6k
      style.engrave = true;
1467
19.6k
      break;
1468
869k
    case SCALING_ID:
1469
869k
      style.textScale = double(info.data) / 10;
1470
869k
      break;
1471
130k
    case LOCALE_ID:
1472
130k
      style.lcid = info.data;
1473
130k
      break;
1474
19.6M
    default:
1475
19.6M
      break;
1476
21.1M
    }
1477
21.1M
  }
1478
  //FIXME: Figure out what textSize2 is used for. Can we find a document where it differs from textSize1 ?
1479
  // textSize2 = textSize1;
1480
54.3k
  boost::optional<double> dTextSize;
1481
54.3k
  if (textSize1 != -1)
1482
11.1k
  {
1483
11.1k
    dTextSize = textSize1 * (double(POINTS_IN_INCH) / EMUS_IN_INCH);
1484
11.1k
  }
1485
  // FIXME: What's this with foo1 && foo2? I've only seen foo1 in 2k2
1486
  // files and either just foo1 or foo1+foo2 in 2k7 files...
1487
54.3k
  style.italic = seenItalic1; // && seenItalic2;
1488
54.3k
  style.bold = seenBold1; // && seenBold2;
1489
54.3k
  (void) seenItalic2;
1490
54.3k
  (void) seenBold2;
1491
54.3k
  style.textSizeInPt = dTextSize;
1492
54.3k
  style.colorIndex = getColorIndexByQuillEntry(colorIndex);
1493
54.3k
  style.fontIndex = fontIndex;
1494
1495
54.3k
  return style;
1496
54.3k
}
1497
1498
unsigned MSPUBParser::getFontIndex(librevenge::RVNGInputStream *input, const MSPUBBlockInfo &info)
1499
26.8k
{
1500
26.8k
  MSPUB_DEBUG_MSG(("In getFontIndex\n"));
1501
26.8k
  input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET);
1502
254k
  while (stillReading(input, info.dataOffset + info.dataLength))
1503
234k
  {
1504
234k
    MSPUBBlockInfo subInfo = parseBlock(input, true);
1505
234k
    if (subInfo.type == GENERAL_CONTAINER)
1506
7.29k
    {
1507
7.29k
      input->seek(subInfo.dataOffset + 4, librevenge::RVNG_SEEK_SET);
1508
7.29k
      if (stillReading(input, subInfo.dataOffset + subInfo.dataLength))
1509
7.06k
      {
1510
7.06k
        MSPUBBlockInfo subSubInfo = parseBlock(input, true);
1511
7.06k
        skipBlock(input, info);
1512
7.06k
        return subSubInfo.data;
1513
7.06k
      }
1514
7.29k
    }
1515
234k
  }
1516
19.7k
  return 0;
1517
26.8k
}
1518
1519
int MSPUBParser::getColorIndex(librevenge::RVNGInputStream *input, const MSPUBBlockInfo &info)
1520
25.7k
{
1521
25.7k
  input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET);
1522
26.7k
  while (stillReading(input, info.dataOffset + info.dataLength))
1523
4.23k
  {
1524
4.23k
    MSPUBBlockInfo subInfo = parseBlock(input, true);
1525
4.23k
    if (subInfo.id == COLOR_INDEX_ID)
1526
3.24k
    {
1527
3.24k
      skipBlock(input, info);
1528
3.24k
      MSPUB_DEBUG_MSG(("Found color index 0x%x\n", (unsigned)subInfo.data));
1529
3.24k
      return subInfo.data;
1530
3.24k
    }
1531
4.23k
  }
1532
22.5k
  MSPUB_DEBUG_MSG(("Failed to find color index!\n"));
1533
22.5k
  return -1;
1534
25.7k
}
1535
1536
bool MSPUBParser::parseEscher(librevenge::RVNGInputStream *input)
1537
1.85k
{
1538
1.85k
  MSPUB_DEBUG_MSG(("MSPUBParser::parseEscher\n"));
1539
1.85k
  EscherContainerInfo fakeroot;
1540
1.85k
  fakeroot.initial = 0;
1541
1.85k
  fakeroot.type = 0;
1542
1.85k
  fakeroot.contentsOffset = input->tell();
1543
1.85k
  fakeroot.contentsLength = (unsigned long)-1; //FIXME: Get the actual length
1544
1.85k
  EscherContainerInfo dg, dgg;
1545
  //Note: this assumes that dgg comes before any dg with images.
1546
1.85k
  if (findEscherContainer(input, fakeroot, dgg, OFFICE_ART_DGG_CONTAINER))
1547
1.60k
  {
1548
1.60k
    EscherContainerInfo bsc;
1549
1.60k
    if (findEscherContainer(input, fakeroot, bsc, OFFICE_ART_B_STORE_CONTAINER))
1550
1.17k
    {
1551
1.17k
      unsigned short currentDelayIndex = 1;
1552
67.3k
      while (stillReading(input, bsc.contentsOffset + bsc.contentsLength))
1553
66.1k
      {
1554
66.1k
        unsigned begin = input->tell();
1555
66.1k
        input->seek(begin + 10, librevenge::RVNG_SEEK_SET);
1556
66.1k
        if (!(readU32(input) == 0 && readU32(input) == 0 && readU32(input) == 0 && readU32(input) == 0))
1557
64.1k
        {
1558
64.1k
          m_escherDelayIndices.push_back(currentDelayIndex++);
1559
64.1k
        }
1560
1.98k
        else
1561
1.98k
        {
1562
1.98k
          m_escherDelayIndices.push_back(-1);
1563
1.98k
        }
1564
66.1k
        input->seek(begin + 44, librevenge::RVNG_SEEK_SET);
1565
66.1k
      }
1566
1.17k
    }
1567
1.60k
    input->seek(dgg.contentsOffset + dgg.contentsLength + getEscherElementTailLength(OFFICE_ART_DGG_CONTAINER), librevenge::RVNG_SEEK_SET);
1568
1.60k
  }
1569
6.33k
  while (findEscherContainer(input, fakeroot, dg, OFFICE_ART_DG_CONTAINER))
1570
4.47k
  {
1571
4.47k
    EscherContainerInfo spgr;
1572
8.90k
    while (findEscherContainer(input, dg, spgr, OFFICE_ART_SPGR_CONTAINER))
1573
4.43k
    {
1574
4.43k
      Coordinate c1, c2;
1575
4.43k
      parseShapeGroup(input, spgr, c1, c2);
1576
4.43k
    }
1577
4.47k
    input->seek(input->tell() + getEscherElementTailLength(OFFICE_ART_DG_CONTAINER), librevenge::RVNG_SEEK_SET);
1578
4.47k
  }
1579
1.85k
  return true;
1580
1.85k
}
1581
1582
void MSPUBParser::parseShapeGroup(librevenge::RVNGInputStream *input, const EscherContainerInfo &spgr, Coordinate parentCoordinateSystem, Coordinate parentGroupAbsoluteCoord)
1583
5.09k
{
1584
5.09k
  EscherContainerInfo shapeOrGroup;
1585
5.09k
  std::set<unsigned short> types;
1586
5.09k
  types.insert(OFFICE_ART_SPGR_CONTAINER);
1587
5.09k
  types.insert(OFFICE_ART_SP_CONTAINER);
1588
58.4k
  while (findEscherContainerWithTypeInSet(input, spgr, shapeOrGroup, types))
1589
53.5k
  {
1590
53.5k
    switch (shapeOrGroup.type)
1591
53.5k
    {
1592
661
    case OFFICE_ART_SPGR_CONTAINER:
1593
661
      m_collector->beginGroup();
1594
661
      parseShapeGroup(input, shapeOrGroup, parentCoordinateSystem, parentGroupAbsoluteCoord);
1595
661
      m_collector->endGroup();
1596
661
      break;
1597
52.8k
    case OFFICE_ART_SP_CONTAINER:
1598
52.8k
      parseEscherShape(input, shapeOrGroup, parentCoordinateSystem, parentGroupAbsoluteCoord);
1599
52.8k
      break;
1600
53.5k
    }
1601
53.3k
    input->seek(shapeOrGroup.contentsOffset + shapeOrGroup.contentsLength + getEscherElementTailLength(shapeOrGroup.type), librevenge::RVNG_SEEK_SET);
1602
53.3k
  }
1603
5.09k
}
1604
1605
void MSPUBParser::parseEscherShape(librevenge::RVNGInputStream *input, const EscherContainerInfo &sp, Coordinate &parentCoordinateSystem, Coordinate &parentGroupAbsoluteCoord)
1606
52.8k
{
1607
52.8k
  Coordinate thisParentCoordinateSystem = parentCoordinateSystem;
1608
52.8k
  bool definesRelativeCoordinates = false;
1609
52.8k
  EscherContainerInfo cData;
1610
52.8k
  EscherContainerInfo cAnchor;
1611
52.8k
  EscherContainerInfo cFopt;
1612
52.8k
  EscherContainerInfo cTertiaryFopt;
1613
52.8k
  EscherContainerInfo cFsp;
1614
52.8k
  EscherContainerInfo cFspgr;
1615
52.8k
  unsigned shapeFlags = 0;
1616
52.8k
  bool isGroupLeader = false;
1617
52.8k
  ShapeType st = RECTANGLE;
1618
52.8k
  if (findEscherContainer(input, sp, cFspgr, OFFICE_ART_FSPGR))
1619
4.78k
  {
1620
4.78k
    input->seek(cFspgr.contentsOffset, librevenge::RVNG_SEEK_SET);
1621
4.78k
    parentCoordinateSystem.m_xs = readU32(input);
1622
4.78k
    parentCoordinateSystem.m_ys = readU32(input);
1623
4.78k
    parentCoordinateSystem.m_xe = readU32(input);
1624
4.78k
    parentCoordinateSystem.m_ye = readU32(input);
1625
4.78k
    parentCoordinateSystem.arrange();
1626
4.78k
    definesRelativeCoordinates = true;
1627
4.78k
  }
1628
52.8k
  input->seek(sp.contentsOffset, librevenge::RVNG_SEEK_SET);
1629
52.8k
  if (findEscherContainer(input, sp, cFsp, OFFICE_ART_FSP))
1630
50.7k
  {
1631
50.7k
    st = (ShapeType)(cFsp.initial >> 4);
1632
50.7k
    std::map<unsigned short, unsigned> fspData = extractEscherValues(input, cFsp);
1633
50.7k
    input->seek(cFsp.contentsOffset + 4, librevenge::RVNG_SEEK_SET);
1634
50.7k
    shapeFlags = readU32(input);
1635
50.7k
    isGroupLeader = shapeFlags & SF_GROUP;
1636
50.7k
  }
1637
52.8k
  input->seek(sp.contentsOffset, librevenge::RVNG_SEEK_SET);
1638
52.8k
  if (findEscherContainer(input, sp, cData, OFFICE_ART_CLIENT_DATA))
1639
22.0k
  {
1640
22.0k
    std::map<unsigned short, unsigned> dataValues = extractEscherValues(input, cData);
1641
22.0k
    unsigned *shapeSeqNum = getIfExists(dataValues, FIELDID_SHAPE_ID);
1642
22.0k
    if (shapeSeqNum)
1643
21.7k
    {
1644
21.7k
      m_collector->setShapeType(*shapeSeqNum, st);
1645
21.7k
      m_collector->setShapeFlip(*shapeSeqNum, shapeFlags & SF_FLIP_V, shapeFlags & SF_FLIP_H);
1646
21.7k
      input->seek(sp.contentsOffset, librevenge::RVNG_SEEK_SET);
1647
21.7k
      if (isGroupLeader)
1648
869
      {
1649
869
        m_collector->setCurrentGroupSeqNum(*shapeSeqNum);
1650
869
      }
1651
20.8k
      else
1652
20.8k
      {
1653
20.8k
        m_collector->setShapeOrder(*shapeSeqNum);
1654
20.8k
      }
1655
21.7k
      std::set<unsigned short> anchorTypes;
1656
21.7k
      anchorTypes.insert(OFFICE_ART_CLIENT_ANCHOR);
1657
21.7k
      anchorTypes.insert(OFFICE_ART_CHILD_ANCHOR);
1658
21.7k
      bool foundAnchor;
1659
21.7k
      if ((foundAnchor = findEscherContainerWithTypeInSet(input, sp, cAnchor, anchorTypes)) || isGroupLeader)
1660
21.0k
      {
1661
21.0k
        bool rotated90 = false;
1662
21.0k
        MSPUB_DEBUG_MSG(("Found Escher data for %s of seqnum 0x%x\n", isGroupLeader ? "group" : "shape", *shapeSeqNum));
1663
21.0k
        boost::optional<std::map<unsigned short, unsigned> > maybe_tertiaryFoptValues;
1664
21.0k
        input->seek(sp.contentsOffset, librevenge::RVNG_SEEK_SET);
1665
21.0k
        if (findEscherContainer(input, sp, cTertiaryFopt, OFFICE_ART_TERTIARY_FOPT))
1666
20.1k
        {
1667
20.1k
          maybe_tertiaryFoptValues = extractEscherValues(input, cTertiaryFopt);
1668
20.1k
        }
1669
21.0k
        if (bool(maybe_tertiaryFoptValues))
1670
20.1k
        {
1671
20.1k
          const std::map<unsigned short, unsigned> &tertiaryFoptValues =
1672
20.1k
            maybe_tertiaryFoptValues.get();
1673
20.1k
          const unsigned *ptr_pictureRecolor = getIfExists_const(tertiaryFoptValues,
1674
20.1k
                                                                 FIELDID_PICTURE_RECOLOR);
1675
20.1k
          if (ptr_pictureRecolor)
1676
101
          {
1677
101
            m_collector->setShapePictureRecolor(*shapeSeqNum,
1678
101
                                                ColorReference(*ptr_pictureRecolor));
1679
101
          }
1680
20.1k
        }
1681
21.0k
        input->seek(sp.contentsOffset, librevenge::RVNG_SEEK_SET);
1682
21.0k
        if (findEscherContainer(input, sp, cFopt, OFFICE_ART_FOPT))
1683
20.3k
        {
1684
20.3k
          FOPTValues foptValues = extractFOPTValues(input, cFopt);
1685
20.3k
          unsigned *pxId = getIfExists(foptValues.m_scalarValues, FIELDID_PXID);
1686
20.3k
          if (pxId)
1687
2.79k
          {
1688
2.79k
            MSPUB_DEBUG_MSG(("Current Escher shape has pxId %d\n", *pxId));
1689
2.79k
            if (*pxId > 0 && *pxId <= m_escherDelayIndices.size() && m_escherDelayIndices[*pxId - 1] >= 0)
1690
2.34k
            {
1691
2.34k
              m_collector->setShapeImgIndex(*shapeSeqNum, m_escherDelayIndices[*pxId - 1]);
1692
2.34k
            }
1693
454
            else
1694
454
            {
1695
454
              MSPUB_DEBUG_MSG(("Couldn't find corresponding escherDelay index\n"));
1696
454
            }
1697
2.79k
            unsigned *ptr_pictureBrightness = getIfExists(foptValues.m_scalarValues, FIELDID_PICTURE_BRIGHTNESS);
1698
2.79k
            if (ptr_pictureBrightness)
1699
3
            {
1700
3
              m_collector->setShapePictureBrightness(*shapeSeqNum, (int)(*ptr_pictureBrightness));
1701
3
            }
1702
2.79k
            unsigned *ptr_pictureContrast = getIfExists(foptValues.m_scalarValues, FIELDID_PICTURE_CONTRAST);
1703
2.79k
            if (ptr_pictureContrast)
1704
17
            {
1705
17
              m_collector->setShapePictureContrast(*shapeSeqNum, (int)(*ptr_pictureContrast));
1706
17
            }
1707
2.79k
          }
1708
20.3k
          unsigned *ptr_lineBackColor =
1709
20.3k
            getIfExists(foptValues.m_scalarValues, FIELDID_LINE_BACK_COLOR);
1710
20.3k
          if (ptr_lineBackColor &&
1711
13.1k
              static_cast<int>(*ptr_lineBackColor) != -1)
1712
13.1k
          {
1713
13.1k
            m_collector->setShapeLineBackColor(
1714
13.1k
              *shapeSeqNum, ColorReference(*ptr_lineBackColor));
1715
13.1k
          }
1716
20.3k
          unsigned *ptr_lineColor = getIfExists(foptValues.m_scalarValues, FIELDID_LINE_COLOR);
1717
20.3k
          unsigned *ptr_lineFlags = getIfExists(foptValues.m_scalarValues, FIELDID_LINE_STYLE_BOOL_PROPS);
1718
20.3k
          unsigned *ptr_geomFlags = getIfExists(
1719
20.3k
                                      foptValues.m_scalarValues, FIELDID_GEOM_BOOL_PROPS);
1720
20.3k
          bool useLine = lineExistsByFlagPointer(
1721
20.3k
                           ptr_lineFlags, ptr_geomFlags);
1722
20.3k
          bool skipIfNotBg = false;
1723
20.3k
          std::shared_ptr<Fill> ptr_fill = getNewFill(foptValues.m_scalarValues, skipIfNotBg, foptValues.m_complexValues);
1724
20.3k
          unsigned lineWidth = 0;
1725
20.3k
          if (useLine)
1726
11.9k
          {
1727
11.9k
            if (ptr_lineColor)
1728
9.33k
            {
1729
9.33k
              unsigned *ptr_lineWidth = getIfExists(foptValues.m_scalarValues, FIELDID_LINE_WIDTH);
1730
9.33k
              lineWidth = ptr_lineWidth ? *ptr_lineWidth : 9525;
1731
9.33k
              m_collector->addShapeLine(*shapeSeqNum, Line(ColorReference(*ptr_lineColor), lineWidth, true));
1732
9.33k
            }
1733
2.58k
            else
1734
2.58k
            {
1735
2.58k
              if (bool(maybe_tertiaryFoptValues))
1736
2.32k
              {
1737
2.32k
                std::map<unsigned short, unsigned> &tertiaryFoptValues =
1738
2.32k
                  maybe_tertiaryFoptValues.get();
1739
2.32k
                unsigned *ptr_tertiaryLineFlags = getIfExists(tertiaryFoptValues, FIELDID_LINE_STYLE_BOOL_PROPS);
1740
2.32k
                if (lineExistsByFlagPointer(ptr_tertiaryLineFlags))
1741
1.03k
                {
1742
1.03k
                  unsigned *ptr_topColor = getIfExists(tertiaryFoptValues, FIELDID_LINE_TOP_COLOR);
1743
1.03k
                  unsigned *ptr_topWidth = getIfExists(tertiaryFoptValues, FIELDID_LINE_TOP_WIDTH);
1744
1.03k
                  unsigned *ptr_topFlags = getIfExists(tertiaryFoptValues, FIELDID_LINE_TOP_BOOL_PROPS);
1745
1.03k
                  unsigned *ptr_rightColor = getIfExists(tertiaryFoptValues, FIELDID_LINE_RIGHT_COLOR);
1746
1.03k
                  unsigned *ptr_rightWidth = getIfExists(tertiaryFoptValues, FIELDID_LINE_RIGHT_WIDTH);
1747
1.03k
                  unsigned *ptr_rightFlags = getIfExists(tertiaryFoptValues, FIELDID_LINE_RIGHT_BOOL_PROPS);
1748
1.03k
                  unsigned *ptr_bottomColor = getIfExists(tertiaryFoptValues, FIELDID_LINE_BOTTOM_COLOR);
1749
1.03k
                  unsigned *ptr_bottomWidth = getIfExists(tertiaryFoptValues, FIELDID_LINE_BOTTOM_WIDTH);
1750
1.03k
                  unsigned *ptr_bottomFlags = getIfExists(tertiaryFoptValues, FIELDID_LINE_BOTTOM_BOOL_PROPS);
1751
1.03k
                  unsigned *ptr_leftColor = getIfExists(tertiaryFoptValues, FIELDID_LINE_LEFT_COLOR);
1752
1.03k
                  unsigned *ptr_leftWidth = getIfExists(tertiaryFoptValues, FIELDID_LINE_LEFT_WIDTH);
1753
1.03k
                  unsigned *ptr_leftFlags = getIfExists(tertiaryFoptValues, FIELDID_LINE_LEFT_BOOL_PROPS);
1754
1755
1.03k
                  bool topExists = ptr_topColor && lineExistsByFlagPointer(ptr_topFlags);
1756
1.03k
                  bool rightExists = ptr_rightColor && lineExistsByFlagPointer(ptr_rightFlags);
1757
1.03k
                  bool bottomExists = ptr_bottomColor && lineExistsByFlagPointer(ptr_bottomFlags);
1758
1.03k
                  bool leftExists = ptr_leftColor && lineExistsByFlagPointer(ptr_leftFlags);
1759
1.03k
                  if (ptr_topWidth)
1760
79
                  {
1761
79
                    lineWidth = *ptr_topWidth;
1762
79
                  }
1763
1764
1.03k
                  m_collector->addShapeLine(*shapeSeqNum,
1765
1.03k
                                            topExists ? Line(ColorReference(*ptr_topColor), ptr_topWidth ? *ptr_topWidth : 9525, true) :
1766
1.03k
                                            Line(ColorReference(0), 0, false));
1767
1.03k
                  m_collector->addShapeLine(*shapeSeqNum,
1768
1.03k
                                            rightExists ? Line(ColorReference(*ptr_rightColor), ptr_rightWidth ? *ptr_rightWidth : 9525, true) :
1769
1.03k
                                            Line(ColorReference(0), 0, false));
1770
1.03k
                  m_collector->addShapeLine(*shapeSeqNum,
1771
1.03k
                                            bottomExists ? Line(ColorReference(*ptr_bottomColor), ptr_bottomWidth ? *ptr_bottomWidth : 9525, true) :
1772
1.03k
                                            Line(ColorReference(0), 0, false));
1773
1.03k
                  m_collector->addShapeLine(*shapeSeqNum,
1774
1.03k
                                            leftExists ? Line(ColorReference(*ptr_leftColor), ptr_leftWidth ? *ptr_leftWidth : 9525, true) :
1775
1.03k
                                            Line(ColorReference(0), 0, false));
1776
1777
                  // Amazing feat of Microsoft engineering:
1778
                  // The detailed interaction of four flags describes ONE true/false property!
1779
1780
1.03k
                  if (ptr_leftFlags &&
1781
86
                      (*ptr_leftFlags & FLAG_USE_LEFT_INSET_PEN) &&
1782
27
                      (!(*ptr_leftFlags & FLAG_USE_LEFT_INSET_PEN_OK) || (*ptr_leftFlags & FLAG_LEFT_INSET_PEN_OK)) &&
1783
21
                      (*ptr_leftFlags & FLAG_LEFT_INSET_PEN))
1784
11
                  {
1785
11
                    m_collector->setShapeBorderPosition(*shapeSeqNum, INSIDE_SHAPE);
1786
11
                  }
1787
1.02k
                  else
1788
1.02k
                  {
1789
1.02k
                    m_collector->setShapeBorderPosition(*shapeSeqNum, HALF_INSIDE_SHAPE);
1790
1.02k
                  }
1791
1.03k
                }
1792
2.32k
              }
1793
2.58k
            }
1794
11.9k
          }
1795
20.3k
          if (ptr_fill)
1796
6.18k
          {
1797
6.18k
            m_collector->setShapeFill(*shapeSeqNum, ptr_fill, skipIfNotBg);
1798
6.18k
          }
1799
20.3k
          int *ptr_adjust1 = (int *)getIfExists(foptValues.m_scalarValues, FIELDID_ADJUST_VALUE_1);
1800
20.3k
          int *ptr_adjust2 = (int *)getIfExists(foptValues.m_scalarValues, FIELDID_ADJUST_VALUE_2);
1801
20.3k
          int *ptr_adjust3 = (int *)getIfExists(foptValues.m_scalarValues, FIELDID_ADJUST_VALUE_3);
1802
20.3k
          if (ptr_adjust1)
1803
2.30k
          {
1804
2.30k
            m_collector->setAdjustValue(*shapeSeqNum, 0, *ptr_adjust1);
1805
2.30k
          }
1806
20.3k
          if (ptr_adjust2)
1807
591
          {
1808
591
            m_collector->setAdjustValue(*shapeSeqNum, 1, *ptr_adjust2);
1809
591
          }
1810
20.3k
          if (ptr_adjust3)
1811
70
          {
1812
70
            m_collector->setAdjustValue(*shapeSeqNum, 2, *ptr_adjust3);
1813
70
          }
1814
20.3k
          int *ptr_rotation = (int *)getIfExists(foptValues.m_scalarValues, FIELDID_ROTATION);
1815
20.3k
          if (ptr_rotation)
1816
1.26k
          {
1817
1.26k
            double rotation = doubleModulo(toFixedPoint(*ptr_rotation), 360);
1818
1.26k
            m_collector->setShapeRotation(*shapeSeqNum, short(rotation));
1819
            //FIXME : make MSPUBCollector handle double shape rotations
1820
1.26k
            rotated90 = (rotation >= 45 && rotation < 135) || (rotation >= 225 && rotation < 315);
1821
1822
1.26k
          }
1823
20.3k
          unsigned *ptr_left = getIfExists(foptValues.m_scalarValues, FIELDID_DY_TEXT_LEFT);
1824
20.3k
          unsigned *ptr_top = getIfExists(foptValues.m_scalarValues, FIELDID_DY_TEXT_TOP);
1825
20.3k
          unsigned *ptr_right = getIfExists(foptValues.m_scalarValues, FIELDID_DY_TEXT_RIGHT);
1826
20.3k
          unsigned *ptr_bottom = getIfExists(foptValues.m_scalarValues, FIELDID_DY_TEXT_BOTTOM);
1827
20.3k
          m_collector->setShapeMargins(*shapeSeqNum, ptr_left ? *ptr_left : DEFAULT_MARGIN,
1828
20.3k
                                       ptr_top ? *ptr_top : DEFAULT_MARGIN,
1829
20.3k
                                       ptr_right ? *ptr_right : DEFAULT_MARGIN,
1830
20.3k
                                       ptr_bottom ? *ptr_bottom : DEFAULT_MARGIN);
1831
20.3k
          unsigned *ptr_lineDashing = getIfExists(foptValues.m_scalarValues, FIELDID_LINE_DASHING);
1832
20.3k
          unsigned *ptr_lineEndcapStyle = getIfExists(foptValues.m_scalarValues, FIELDID_LINE_ENDCAP_STYLE);
1833
20.3k
          DotStyle dotStyle = RECT_DOT;
1834
20.3k
          if (ptr_lineEndcapStyle)
1835
115
          {
1836
115
            switch (*ptr_lineEndcapStyle)
1837
115
            {
1838
3
            case 0:
1839
3
              dotStyle = ROUND_DOT;
1840
3
              break;
1841
112
            default:
1842
112
              break;
1843
115
            }
1844
115
          }
1845
20.3k
          if (ptr_lineDashing)
1846
312
          {
1847
312
            m_collector->setShapeDash(*shapeSeqNum, getDash(
1848
312
                                        static_cast<MSPUBDashStyle>(*ptr_lineDashing), lineWidth,
1849
312
                                        dotStyle));
1850
312
          }
1851
1852
20.3k
          if (bool(maybe_tertiaryFoptValues))
1853
19.5k
          {
1854
19.5k
            std::map<unsigned short, unsigned> &tertiaryFoptValues = maybe_tertiaryFoptValues.get();
1855
19.5k
            unsigned *ptr_numColumns = getIfExists(tertiaryFoptValues, FIELDID_NUM_COLUMNS);
1856
19.5k
            if (ptr_numColumns)
1857
12
            {
1858
12
              m_collector->setShapeNumColumns(*shapeSeqNum, *ptr_numColumns);
1859
12
            }
1860
19.5k
            unsigned *ptr_columnSpacing = getIfExists(tertiaryFoptValues, FIELDID_COLUMN_SPACING);
1861
19.5k
            if (ptr_columnSpacing)
1862
2.63k
            {
1863
2.63k
              m_collector->setShapeColumnSpacing(*shapeSeqNum, *ptr_columnSpacing);
1864
2.63k
            }
1865
19.5k
          }
1866
20.3k
          unsigned *ptr_beginArrowStyle = getIfExists(foptValues.m_scalarValues,
1867
20.3k
                                                      FIELDID_BEGIN_ARROW_STYLE);
1868
20.3k
          unsigned *ptr_beginArrowWidth = getIfExists(foptValues.m_scalarValues,
1869
20.3k
                                                      FIELDID_BEGIN_ARROW_WIDTH);
1870
20.3k
          unsigned *ptr_beginArrowHeight = getIfExists(foptValues.m_scalarValues,
1871
20.3k
                                                       FIELDID_BEGIN_ARROW_HEIGHT);
1872
20.3k
          m_collector->setShapeBeginArrow(*shapeSeqNum, Arrow(
1873
20.3k
                                            ptr_beginArrowStyle ? (ArrowStyle)(*ptr_beginArrowStyle) :
1874
20.3k
                                            NO_ARROW,
1875
20.3k
                                            ptr_beginArrowWidth ? (ArrowSize)(*ptr_beginArrowWidth) :
1876
20.3k
                                            MEDIUM,
1877
20.3k
                                            ptr_beginArrowHeight ? (ArrowSize)(*ptr_beginArrowHeight) :
1878
20.3k
                                            MEDIUM));
1879
20.3k
          unsigned *ptr_endArrowStyle = getIfExists(foptValues.m_scalarValues,
1880
20.3k
                                                    FIELDID_END_ARROW_STYLE);
1881
20.3k
          unsigned *ptr_endArrowWidth = getIfExists(foptValues.m_scalarValues,
1882
20.3k
                                                    FIELDID_END_ARROW_WIDTH);
1883
20.3k
          unsigned *ptr_endArrowHeight = getIfExists(foptValues.m_scalarValues,
1884
20.3k
                                                     FIELDID_END_ARROW_HEIGHT);
1885
20.3k
          m_collector->setShapeEndArrow(*shapeSeqNum, Arrow(
1886
20.3k
                                          ptr_endArrowStyle ? (ArrowStyle)(*ptr_endArrowStyle) :
1887
20.3k
                                          NO_ARROW,
1888
20.3k
                                          ptr_endArrowWidth ? (ArrowSize)(*ptr_endArrowWidth) :
1889
20.3k
                                          MEDIUM,
1890
20.3k
                                          ptr_endArrowHeight ? (ArrowSize)(*ptr_endArrowHeight) :
1891
20.3k
                                          MEDIUM));
1892
1893
20.3k
          unsigned *shadowBoolProps = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_BOOL_PROPS);
1894
20.3k
          if (shadowBoolProps)
1895
5.25k
          {
1896
5.25k
            unsigned shadowProps = *shadowBoolProps;
1897
5.25k
            if ((shadowProps & FLAG_USE_FSHADOW) && (shadowProps & FLAG_USE_SHADOW))
1898
1.88k
            {
1899
1.88k
              unsigned *ptr_shadowType = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_TYPE);
1900
1.88k
              auto shadowType = static_cast<ShadowType>(ptr_shadowType ? *ptr_shadowType : 0);
1901
1.88k
              unsigned *shadowColor = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_COLOR);
1902
1.88k
              unsigned *shadowHColor = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_HIGHLIGHT);
1903
1.88k
              unsigned *shadowOpacity = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_OPACITY);
1904
1.88k
              unsigned *shadowOffsetX = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_OFFSET_X);
1905
1.88k
              unsigned *shadowOffsetY = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_OFFSET_Y);
1906
1.88k
              unsigned *shadowOffsetX2 = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_SECOND_OFFSET_X);
1907
1.88k
              unsigned *shadowOffsetY2 = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_SECOND_OFFSET_Y);
1908
1.88k
              unsigned *shadowOriginX = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_ORIGIN_X);
1909
1.88k
              unsigned *shadowOriginY = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_ORIGIN_Y);
1910
1.88k
              m_collector->setShapeShadow(*shapeSeqNum, Shadow(shadowType,
1911
1.88k
                                                               shadowOffsetX ? static_cast<int>(*shadowOffsetX) : 0x6338,
1912
1.88k
                                                               shadowOffsetY ? static_cast<int>(*shadowOffsetY) : 0x6338,
1913
1.88k
                                                               shadowOffsetX2 ? static_cast<int>(*shadowOffsetX2) : 0,
1914
1.88k
                                                               shadowOffsetY2 ? static_cast<int>(*shadowOffsetY2) : 0,
1915
1.88k
                                                               shadowOriginX ? toFixedPoint(static_cast<int>(*shadowOriginX)) : 0,
1916
1.88k
                                                               shadowOriginY ? toFixedPoint(static_cast<int>(*shadowOriginY)) : 0,
1917
1.88k
                                                               toFixedPoint(shadowOpacity ? static_cast<int>(*shadowOpacity) : 0x10000),
1918
1.88k
                                                               ColorReference(shadowColor ? *shadowColor : 0x00808080),
1919
1.88k
                                                               ColorReference(shadowHColor ? *shadowHColor : 0x00CBCBCB)
1920
1.88k
                                                              ));
1921
1922
1.88k
            }
1923
5.25k
          }
1924
1925
20.3k
          const std::vector<unsigned char> vertexData = foptValues.m_complexValues[FIELDID_P_VERTICES];
1926
20.3k
          if (!vertexData.empty())
1927
654
          {
1928
654
            unsigned *p_geoRight = getIfExists(foptValues.m_scalarValues,
1929
654
                                               FIELDID_GEO_RIGHT);
1930
654
            unsigned *p_geoBottom = getIfExists(foptValues.m_scalarValues,
1931
654
                                                FIELDID_GEO_BOTTOM);
1932
654
            const std::vector<unsigned char> segmentData = foptValues.m_complexValues[FIELDID_P_SEGMENTS];
1933
654
            const std::vector<unsigned char> guideData = foptValues.m_complexValues[FIELDID_P_GUIDES];
1934
654
            m_collector->setShapeCustomPath(*shapeSeqNum, getDynamicCustomShape(vertexData, segmentData,
1935
654
                                                                                guideData, p_geoRight ? *p_geoRight : 21600,
1936
654
                                                                                p_geoBottom ? *p_geoBottom : 21600));
1937
654
          }
1938
20.3k
          const std::vector<unsigned char> wrapVertexData = foptValues.m_complexValues[FIELDID_P_WRAPPOLYGONVERTICES];
1939
20.3k
          if (!wrapVertexData.empty())
1940
136
          {
1941
136
            std::vector<Vertex> ret = parseVertices(wrapVertexData);
1942
136
            m_collector->setShapeClipPath(*shapeSeqNum, ret);
1943
136
          }
1944
20.3k
        }
1945
21.0k
        if (foundAnchor)
1946
20.9k
        {
1947
20.9k
          Coordinate absolute;
1948
20.9k
          if (cAnchor.type == OFFICE_ART_CLIENT_ANCHOR)
1949
19.9k
          {
1950
19.9k
            std::map<unsigned short, unsigned> anchorData = extractEscherValues(input, cAnchor);
1951
19.9k
            absolute = Coordinate(anchorData[FIELDID_XS],
1952
19.9k
                                  anchorData[FIELDID_YS], anchorData[FIELDID_XE],
1953
19.9k
                                  anchorData[FIELDID_YE]);
1954
19.9k
          }
1955
945
          else if (cAnchor.type == OFFICE_ART_CHILD_ANCHOR)
1956
945
          {
1957
945
            input->seek(cAnchor.contentsOffset, librevenge::RVNG_SEEK_SET);
1958
945
            int coordSystemWidth = int64_t(thisParentCoordinateSystem.m_xe) - thisParentCoordinateSystem.m_xs;
1959
945
            if (coordSystemWidth == 0)
1960
218
              coordSystemWidth = 1;
1961
945
            int coordSystemHeight = thisParentCoordinateSystem.m_ye - thisParentCoordinateSystem.m_ys;
1962
945
            if (coordSystemHeight == 0)
1963
107
              coordSystemHeight = 1;
1964
945
            int groupWidth = int64_t(parentGroupAbsoluteCoord.m_xe) - parentGroupAbsoluteCoord.m_xs;
1965
945
            int groupHeight = int64_t(parentGroupAbsoluteCoord.m_ye) - parentGroupAbsoluteCoord.m_ys;
1966
945
            double widthScale = (double)groupWidth / coordSystemWidth;
1967
945
            double heightScale = (double)groupHeight / coordSystemHeight;
1968
945
            int xs = (readU32(input) - thisParentCoordinateSystem.m_xs) * widthScale + parentGroupAbsoluteCoord.m_xs;
1969
945
            int ys = (readU32(input) - thisParentCoordinateSystem.m_ys) * heightScale + parentGroupAbsoluteCoord.m_ys;
1970
945
            int xe = (readU32(input) - thisParentCoordinateSystem.m_xs) * widthScale + parentGroupAbsoluteCoord.m_xs;
1971
945
            int ye = (readU32(input) - thisParentCoordinateSystem.m_ys) * heightScale + parentGroupAbsoluteCoord.m_ys;
1972
1973
945
            absolute = Coordinate(xs, ys, xe, ye);
1974
945
          }
1975
20.9k
          if (rotated90)
1976
859
          {
1977
859
            int initialWidth = int64_t(absolute.m_xe) - absolute.m_xs;
1978
859
            int initialHeight = int64_t(absolute.m_ye) - absolute.m_ys;
1979
859
            int centerX = int64_t(absolute.m_xs) + initialWidth / 2;
1980
859
            int centerY = int64_t(absolute.m_ys) + initialHeight / 2;
1981
859
            int xs = centerX - initialHeight / 2;
1982
859
            int ys = centerY - initialWidth / 2;
1983
859
            int xe = xs + initialHeight;
1984
859
            int ye = ys + initialWidth;
1985
859
            absolute = Coordinate(xs, ys, xe, ye);
1986
859
          }
1987
20.9k
          m_collector->setShapeCoordinatesInEmu(*shapeSeqNum,
1988
20.9k
                                                absolute.m_xs,
1989
20.9k
                                                absolute.m_ys,
1990
20.9k
                                                absolute.m_xe,
1991
20.9k
                                                absolute.m_ye);
1992
20.9k
          if (definesRelativeCoordinates)
1993
480
          {
1994
480
            parentGroupAbsoluteCoord = absolute;
1995
480
          }
1996
20.9k
        }
1997
21.0k
      }
1998
21.7k
    }
1999
22.0k
  }
2000
52.8k
}
2001
2002
std::shared_ptr<Fill> MSPUBParser::getNewFill(const std::map<unsigned short, unsigned> &foptProperties,
2003
                                              bool &skipIfNotBg, std::map<unsigned short, std::vector<unsigned char> > &foptValues)
2004
20.3k
{
2005
20.3k
  const FillType *ptr_fillType = (FillType *)getIfExists_const(foptProperties, FIELDID_FILL_TYPE);
2006
20.3k
  FillType fillType = ptr_fillType ? *ptr_fillType : SOLID;
2007
20.3k
  switch (fillType)
2008
20.3k
  {
2009
18.6k
  case SOLID:
2010
18.6k
  {
2011
18.6k
    const unsigned *ptr_fillColor = getIfExists_const(foptProperties, FIELDID_FILL_COLOR);
2012
18.6k
    const unsigned *ptr_fieldStyleProps = getIfExists_const(foptProperties, FIELDID_FIELD_STYLE_BOOL_PROPS);
2013
18.6k
    skipIfNotBg = ptr_fieldStyleProps && (*ptr_fieldStyleProps & 0xF0) == 0;
2014
18.6k
    if (ptr_fillColor && !skipIfNotBg)
2015
4.83k
    {
2016
4.83k
      const unsigned *ptr_fillOpacity = getIfExists_const(foptProperties, FIELDID_FILL_OPACITY);
2017
4.83k
      return std::shared_ptr<Fill>(new SolidFill(ColorReference(*ptr_fillColor), ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1, m_collector));
2018
4.83k
    }
2019
13.8k
    return std::shared_ptr<Fill>();
2020
18.6k
  }
2021
11
  case SHADE_SHAPE:
2022
241
  case SHADE_CENTER:
2023
242
  case SHADE:
2024
1.10k
  case SHADE_SCALE:
2025
1.10k
  {
2026
1.10k
    int angle;
2027
1.10k
    const int *ptr_angle = (const int *)getIfExists_const(foptProperties, FIELDID_FILL_ANGLE);
2028
1.10k
    const unsigned *ptr_fillColor = getIfExists_const(foptProperties, FIELDID_FILL_COLOR);
2029
1.10k
    const unsigned *ptr_fillBackColor = getIfExists_const(foptProperties, FIELDID_FILL_BACK_COLOR);
2030
1.10k
    unsigned fill = ptr_fillColor ? *ptr_fillColor : 0x00FFFFFFF;
2031
1.10k
    unsigned fillBack = ptr_fillBackColor ? *ptr_fillBackColor : 0x00FFFFFF;
2032
1.10k
    ColorReference firstColor(fill, fill);
2033
1.10k
    ColorReference secondColor(fill, fillBack);
2034
1.10k
    const unsigned *ptr_fillOpacity = getIfExists_const(foptProperties, FIELDID_FILL_OPACITY);
2035
1.10k
    const unsigned *ptr_fillBackOpacity = getIfExists_const(foptProperties, FIELDID_FILL_BACK_OPACITY);
2036
1.10k
    const unsigned *ptr_fillFocus = getIfExists_const(foptProperties, FIELDID_FILL_FOCUS);
2037
1.10k
    short fillFocus = ptr_fillFocus ? int((*ptr_fillFocus << 16) >> 16) : 0;
2038
1.10k
    angle = ptr_angle ? *ptr_angle : 0;
2039
1.10k
    angle >>= 16; //it's actually only 16 bits
2040
    // Don't try to figure out what sense the following switch statement makes.
2041
    // The angles are just offset by 90 degrees in the file format in some cases.
2042
    // It seems totally arbitrary -- maybe an MS bug ?
2043
1.10k
    switch (angle)
2044
1.10k
    {
2045
207
    case -135:
2046
207
      angle = -45;
2047
207
      break;
2048
0
    case -45:
2049
0
      angle = 225;
2050
0
      break;
2051
893
    default:
2052
893
      break;
2053
1.10k
    }
2054
1.10k
    double fillLeftVal = 0.0;
2055
1.10k
    const unsigned *ptr_fillLeft = getIfExists_const(foptProperties, FIELDID_FILL_TO_LEFT);
2056
1.10k
    if (ptr_fillLeft)
2057
107
      fillLeftVal = toFixedPoint(*ptr_fillLeft);
2058
1.10k
    double fillTopVal = 0.0;
2059
1.10k
    const unsigned *ptr_fillTop = getIfExists_const(foptProperties, FIELDID_FILL_TO_TOP);
2060
1.10k
    if (ptr_fillTop)
2061
172
      fillTopVal = toFixedPoint(*ptr_fillTop);
2062
1.10k
    double fillRightVal = 0.0;
2063
1.10k
    const unsigned *ptr_fillRight = getIfExists_const(foptProperties, FIELDID_FILL_TO_RIGHT);
2064
1.10k
    if (ptr_fillRight)
2065
121
      fillRightVal = toFixedPoint(*ptr_fillRight);
2066
1.10k
    double fillBottomVal = 0.0;
2067
1.10k
    const unsigned *ptr_fillBottom = getIfExists_const(foptProperties, FIELDID_FILL_TO_BOTTOM);
2068
1.10k
    if (ptr_fillBottom)
2069
122
      fillBottomVal = toFixedPoint(*ptr_fillBottom);
2070
2071
1.10k
    std::shared_ptr<GradientFill> ret(new GradientFill(m_collector, angle, (int)fillType));
2072
1.10k
    ret->setFillCenter(fillLeftVal, fillTopVal, fillRightVal, fillBottomVal);
2073
2074
1.10k
    const unsigned *ptr_fillGrad = getIfExists_const(foptProperties, FIELDID_FILL_SHADE_COMPLEX);
2075
1.10k
    if (ptr_fillGrad)
2076
146
    {
2077
146
      const std::vector<unsigned char> gradientData = foptValues[FIELDID_FILL_SHADE_COMPLEX];
2078
146
      if (gradientData.size() > 6)
2079
39
      {
2080
39
        unsigned short numEntries = gradientData[0] | (gradientData[1] << 8);
2081
39
        unsigned offs = 6;
2082
12.6k
        for (unsigned i = 0; i < numEntries; ++i)
2083
12.6k
        {
2084
12.6k
          unsigned color = gradientData[offs] | (unsigned(gradientData[offs + 1]) << 8) | (unsigned(gradientData[offs + 2]) << 16) | (unsigned(gradientData[offs + 3]) << 24);
2085
12.6k
          offs += 4;
2086
12.6k
          auto posi = (int)(toFixedPoint(gradientData[offs] | (unsigned(gradientData[offs + 1]) << 8) | (unsigned(gradientData[offs + 2]) << 16) | (unsigned(gradientData[offs + 3]) << 24)) * 100);
2087
12.6k
          offs += 4;
2088
12.6k
          ColorReference sColor(color, color);
2089
12.6k
          if (fillFocus ==  0)
2090
980
            ret->addColor(sColor, posi, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2091
11.6k
          else if (fillFocus == 100)
2092
772
            ret->addColorReverse(sColor, 100 - posi, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2093
10.9k
          else if (fillFocus > 0)
2094
3.94k
            ret->addColor(sColor, posi / 2, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2095
6.96k
          else if (fillFocus < 0)
2096
6.96k
            ret->addColorReverse(sColor, (100 - posi) / 2, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2097
12.6k
        }
2098
39
        if ((fillFocus < 0) || ((fillFocus > 0) && (fillFocus < 100)))
2099
27
          ret->completeComplexFill();
2100
39
      }
2101
146
    }
2102
954
    else
2103
954
    {
2104
954
      if (fillFocus ==  0)
2105
287
      {
2106
287
        ret->addColor(firstColor, 0, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2107
287
        ret->addColor(secondColor, 100, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1);
2108
287
      }
2109
667
      else if (fillFocus == 100)
2110
486
      {
2111
486
        ret->addColor(secondColor, 0, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1);
2112
486
        ret->addColor(firstColor, 100, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2113
486
      }
2114
181
      else if (fillFocus > 0)
2115
71
      {
2116
71
        ret->addColor(secondColor, 0, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1);
2117
71
        ret->addColor(firstColor, fillFocus, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2118
71
        ret->addColor(secondColor, 100, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1);
2119
2120
//        ret->addColor(firstColor, 0, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2121
//        ret->addColor(secondColor, fillFocus, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1);
2122
//        ret->addColor(firstColor, 100, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2123
71
      }
2124
110
      else if (fillFocus < 0)
2125
110
      {
2126
110
        ret->addColor(firstColor, 0, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2127
110
        ret->addColor(secondColor, 100 + fillFocus, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1);
2128
110
        ret->addColor(firstColor, 100, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2129
2130
//        ret->addColor(secondColor, 0, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1);
2131
//        ret->addColor(firstColor, 100 + fillFocus, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1);
2132
//        ret->addColor(secondColor, 100, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1);
2133
110
      }
2134
954
    }
2135
1.10k
    return ret;
2136
1.10k
  }
2137
245
  case TEXTURE:
2138
247
  case BITMAP:
2139
247
  {
2140
    // in the case the shape is rotated we must rotate the image too
2141
247
    int rotation = 0;
2142
247
    const int *ptr_rotation = (const int *)getIfExists_const(foptProperties, FIELDID_ROTATION);
2143
247
    if (ptr_rotation)
2144
74
      rotation = (int)doubleModulo(toFixedPoint(*ptr_rotation), 360);
2145
247
    const unsigned *ptr_bgPxId = getIfExists_const(foptProperties, FIELDID_BG_PXID);
2146
247
    if (ptr_bgPxId && *ptr_bgPxId > 0 && *ptr_bgPxId <= m_escherDelayIndices.size() && m_escherDelayIndices[*ptr_bgPxId - 1] >= 0)
2147
177
    {
2148
177
      return std::shared_ptr<Fill>(new ImgFill(m_escherDelayIndices[*ptr_bgPxId - 1], m_collector, fillType == TEXTURE, rotation));
2149
177
    }
2150
70
    return std::shared_ptr<Fill>();
2151
247
  }
2152
175
  case PATTERN:
2153
175
  {
2154
175
    const unsigned *ptr_bgPxId = getIfExists_const(foptProperties, FIELDID_BG_PXID);
2155
175
    const unsigned *ptr_fillColor = getIfExists_const(foptProperties, FIELDID_FILL_COLOR);
2156
175
    const unsigned *ptr_fillBackColor = getIfExists_const(foptProperties, FIELDID_FILL_BACK_COLOR);
2157
175
    ColorReference fill = ptr_fillColor ? ColorReference(*ptr_fillColor) : ColorReference(0x00FFFFFF);
2158
//    ColorReference back = ptr_fillBackColor ? ColorReference(*ptr_fillBackColor) : ColorReference(0x08000000);
2159
175
    ColorReference back = ptr_fillBackColor ? ColorReference(*ptr_fillBackColor) : ColorReference(0x00FFFFFF);
2160
175
    if (ptr_bgPxId && *ptr_bgPxId > 0 && *ptr_bgPxId <= m_escherDelayIndices.size() && m_escherDelayIndices[*ptr_bgPxId - 1] >= 0)
2161
74
    {
2162
74
      return std::shared_ptr<Fill>(new PatternFill(m_escherDelayIndices[*ptr_bgPxId - 1], m_collector, fill, back));
2163
74
    }
2164
101
    return std::shared_ptr<Fill>();
2165
175
  }
2166
0
  case BACKGROUND:
2167
166
  default:
2168
166
    return std::shared_ptr<Fill>();
2169
20.3k
  }
2170
20.3k
}
2171
2172
DynamicCustomShape MSPUBParser::getDynamicCustomShape(
2173
  const std::vector<unsigned char> &vertexData, const std::vector<unsigned char> &segmentData,
2174
  const std::vector<unsigned char> &guideData, unsigned geoWidth,
2175
  unsigned geoHeight)
2176
654
{
2177
654
  DynamicCustomShape ret(geoWidth, geoHeight);
2178
654
  ret.m_vertices = parseVertices(vertexData);
2179
654
  ret.m_elements = parseSegments(segmentData);
2180
654
  ret.m_calculations = parseGuides(guideData);
2181
654
  return ret;
2182
654
}
2183
2184
std::vector<unsigned short> MSPUBParser::parseSegments(
2185
  const std::vector<unsigned char> &segmentData)
2186
654
{
2187
654
  std::vector<unsigned short> ret;
2188
654
  if (segmentData.size() < 6)
2189
105
  {
2190
105
    return ret;
2191
105
  }
2192
  // assume that the entry size is 2.
2193
549
  unsigned short numEntries = segmentData[0] | (segmentData[1] << 8);
2194
549
  unsigned offset = 6;
2195
208k
  for (unsigned i = 0; i < numEntries; ++i)
2196
208k
  {
2197
208k
    if (offset + 2 > segmentData.size())
2198
18
    {
2199
18
      break;
2200
18
    }
2201
208k
    ret.push_back(segmentData[offset] | (segmentData[offset + 1] << 8));
2202
208k
    offset += 2;
2203
208k
  }
2204
549
  return ret;
2205
654
}
2206
2207
std::vector<Calculation> MSPUBParser::parseGuides(
2208
  const std::vector<unsigned char> &/* guideData */)
2209
654
{
2210
654
  std::vector<Calculation> ret;
2211
2212
  //FIXME : implement this function.
2213
2214
654
  return ret;
2215
654
}
2216
2217
std::vector<Vertex> MSPUBParser::parseVertices(
2218
  const std::vector<unsigned char> &vertexData)
2219
790
{
2220
790
  std::vector<Vertex> ret;
2221
790
  if (vertexData.size() < 6)
2222
0
  {
2223
0
    return ret;
2224
0
  }
2225
790
  unsigned short numVertices = vertexData[0] | (vertexData[1] << 8);
2226
790
  unsigned short entrySize = vertexData[4] | (vertexData[5] << 8);
2227
790
  if (entrySize == 0xFFF0)
2228
27
  {
2229
27
    entrySize = 4;
2230
27
  }
2231
790
  if (!(entrySize == 2 || entrySize == 4 || entrySize == 8))
2232
56
  {
2233
56
    MSPUB_DEBUG_MSG(("Incomprehensible entry size %u in vertex complex data!\n", entrySize));
2234
56
    return ret;
2235
56
  }
2236
734
  unsigned offset = 6;
2237
734
  ret.reserve(numVertices);
2238
16.8k
  for (unsigned i = 0; i < numVertices; ++i)
2239
16.1k
  {
2240
16.1k
    if (offset + entrySize > vertexData.size())
2241
0
    {
2242
0
      break;
2243
0
    }
2244
16.1k
    int x, y;
2245
16.1k
    switch (entrySize)
2246
16.1k
    {
2247
2.55k
    case 2:
2248
2.55k
      x = vertexData[offset];
2249
2.55k
      y = vertexData[offset + 1];
2250
2.55k
      break;
2251
1.65k
    case 4:
2252
1.65k
      x = vertexData[offset] | (unsigned(vertexData[offset + 1]) << 8);
2253
1.65k
      y = vertexData[offset + 2] | (unsigned(vertexData[offset + 3]) << 8);
2254
1.65k
      break;
2255
11.8k
    case 8:
2256
11.8k
      x = vertexData[offset] | (unsigned(vertexData[offset + 1]) << 8) |
2257
11.8k
          (unsigned(vertexData[offset + 2]) << 16) | (unsigned(vertexData[offset + 3]) << 24);
2258
11.8k
      y = vertexData[offset + 4] | (unsigned(vertexData[offset + 5]) << 8) |
2259
11.8k
          (unsigned(vertexData[offset + 6]) << 16) | (unsigned(vertexData[offset + 7]) << 24);
2260
11.8k
      break;
2261
0
    default: // logically shouldn't be able to get here.
2262
0
      x = 0;
2263
0
      y = 0;
2264
0
      break;
2265
16.1k
    }
2266
16.1k
    Vertex v = {x, y};
2267
16.1k
    ret.push_back(v);
2268
16.1k
    offset += entrySize;
2269
16.1k
  }
2270
734
  return ret;
2271
734
}
2272
2273
unsigned MSPUBParser::getEscherElementTailLength(unsigned short type)
2274
815k
{
2275
815k
  switch (type)
2276
815k
  {
2277
1.94k
  case OFFICE_ART_DGG_CONTAINER:
2278
6.43k
  case OFFICE_ART_DG_CONTAINER:
2279
6.43k
    return 4;
2280
808k
  default:
2281
808k
    return 0;
2282
815k
  }
2283
815k
}
2284
2285
unsigned MSPUBParser::getEscherElementAdditionalHeaderLength(unsigned short type)
2286
112k
{
2287
112k
  switch (type)
2288
112k
  {
2289
19.9k
  case OFFICE_ART_CLIENT_ANCHOR:
2290
42.0k
  case OFFICE_ART_CLIENT_DATA: //account for the fact that the length appears twice, for whatever reason
2291
42.0k
    return 4;
2292
112k
  }
2293
70.8k
  return 0;
2294
112k
}
2295
2296
bool MSPUBParser::findEscherContainerWithTypeInSet(librevenge::RVNGInputStream *input, const EscherContainerInfo &parent, EscherContainerInfo &out, std::set<unsigned short> types)
2297
80.1k
{
2298
155k
  while (stillReading(input, parent.contentsOffset + parent.contentsLength))
2299
149k
  {
2300
149k
    EscherContainerInfo next = parseEscherContainer(input);
2301
149k
    if (types.find(next.type) != types.end())
2302
74.4k
    {
2303
74.4k
      out = next;
2304
74.4k
      return true;
2305
74.4k
    }
2306
75.3k
    input->seek(next.contentsOffset + next.contentsLength + getEscherElementTailLength(next.type), librevenge::RVNG_SEEK_SET);
2307
75.3k
  }
2308
5.75k
  return false;
2309
80.1k
}
2310
2311
bool MSPUBParser::findEscherContainer(librevenge::RVNGInputStream *input, const EscherContainerInfo &parent, EscherContainerInfo &out, unsigned short desiredType)
2312
219k
{
2313
219k
  MSPUB_DEBUG_MSG(("At offset 0x%lx, attempting to find escher container of type 0x%x\n", input->tell(), desiredType));
2314
899k
  while (stillReading(input, parent.contentsOffset + parent.contentsLength))
2315
810k
  {
2316
810k
    EscherContainerInfo next = parseEscherContainer(input);
2317
810k
    if (next.type == desiredType)
2318
129k
    {
2319
129k
      out = next;
2320
129k
      return true;
2321
129k
    }
2322
680k
    input->seek(next.contentsOffset + next.contentsLength + getEscherElementTailLength(next.type), librevenge::RVNG_SEEK_SET);
2323
680k
  }
2324
89.3k
  return false;
2325
219k
}
2326
2327
FOPTValues MSPUBParser::extractFOPTValues(librevenge::RVNGInputStream *input, const EscherContainerInfo &record)
2328
20.3k
{
2329
20.3k
  FOPTValues ret;
2330
20.3k
  input->seek(record.contentsOffset, librevenge::RVNG_SEEK_SET);
2331
20.3k
  unsigned short numValues = record.initial >> 4;
2332
20.3k
  std::vector<unsigned short> complexIds;
2333
392k
  for (unsigned short i = 0; i < numValues; ++i)
2334
372k
  {
2335
372k
    if (!stillReading(input, record.contentsOffset + record.contentsLength))
2336
296
    {
2337
296
      break;
2338
296
    }
2339
372k
    unsigned short id = readU16(input);
2340
372k
    unsigned value  = readU32(input);
2341
372k
    ret.m_scalarValues[id] = value;
2342
372k
    bool complex = id & 0x8000;
2343
372k
    if (complex)
2344
18.7k
    {
2345
18.7k
      complexIds.push_back(id);
2346
18.7k
    }
2347
372k
  }
2348
20.3k
  for (unsigned short id : complexIds)
2349
14.6k
  {
2350
14.6k
    if (!stillReading(input, record.contentsOffset + record.contentsLength))
2351
5.07k
    {
2352
5.07k
      break;
2353
5.07k
    }
2354
9.60k
    unsigned length = ret.m_scalarValues[id];
2355
9.60k
    if (!length)
2356
223
    {
2357
223
      continue;
2358
223
    }
2359
9.37k
    unsigned short numEntries = readU16(input);
2360
9.37k
    input->seek(2, librevenge::RVNG_SEEK_CUR);
2361
9.37k
    unsigned short entryLength = readU16(input);
2362
9.37k
    if (entryLength == 0xFFF0)
2363
28
    {
2364
28
      entryLength = 4;
2365
28
    }
2366
9.37k
    input->seek(-6, librevenge::RVNG_SEEK_CUR);
2367
9.37k
    readNBytes(input, static_cast<unsigned long>(entryLength) * numEntries + 6, ret.m_complexValues[id]);
2368
9.37k
  }
2369
20.3k
  return ret;
2370
20.3k
}
2371
2372
std::map<unsigned short, unsigned> MSPUBParser::extractEscherValues(librevenge::RVNGInputStream *input, const EscherContainerInfo &record)
2373
112k
{
2374
112k
  std::map<unsigned short, unsigned> ret;
2375
112k
  input->seek(record.contentsOffset + getEscherElementAdditionalHeaderLength(record.type), librevenge::RVNG_SEEK_SET);
2376
2.73M
  while (stillReading(input, record.contentsOffset + record.contentsLength))
2377
2.66M
  {
2378
2.66M
    unsigned short id = readU16(input);
2379
2.66M
    if (id == 0)
2380
554k
    {
2381
554k
      if (!stillReading(input, record.contentsOffset + record.contentsLength))
2382
49.4k
        break;
2383
504k
      MSPUB_DEBUG_MSG(("found escher value with ID 0!\n"));
2384
504k
    }
2385
2.62M
    unsigned value = readU32(input);
2386
2.62M
    ret[id] = value;
2387
2.62M
  }
2388
112k
  return ret;
2389
112k
}
2390
2391
2392
bool MSPUBParser::parseContentChunkReference(librevenge::RVNGInputStream *input, const MSPUBBlockInfo block)
2393
109k
{
2394
  //input should be at block.dataOffset + 4 , that is, at the beginning of the list of sub-blocks
2395
109k
  MSPUB_DEBUG_MSG(("Parsing chunk reference 0x%x\n", m_lastSeenSeqNum));
2396
109k
  unsigned type = UNKNOWN_CHUNK;
2397
109k
  unsigned long offset = 0;
2398
109k
  unsigned parentSeqNum = 0;
2399
109k
  bool seenType = false;
2400
109k
  bool seenOffset = false;
2401
109k
  bool seenParentSeqNum = false;
2402
794k
  while (stillReading(input, block.dataOffset + block.dataLength))
2403
685k
  {
2404
685k
    MSPUBBlockInfo subBlock = parseBlock(input, true);
2405
    //FIXME: Warn if multiple of these blocks seen.
2406
685k
    if (subBlock.id == CHUNK_TYPE)
2407
109k
    {
2408
109k
      type = subBlock.data;
2409
109k
      seenType = true;
2410
109k
    }
2411
576k
    else if (subBlock.id == CHUNK_OFFSET)
2412
109k
    {
2413
109k
      offset = subBlock.data;
2414
109k
      seenOffset = true;
2415
109k
    }
2416
466k
    else if (subBlock.id == CHUNK_PARENT_SEQNUM)
2417
100k
    {
2418
100k
      parentSeqNum = subBlock.data;
2419
100k
      seenParentSeqNum = true;
2420
100k
    }
2421
685k
  }
2422
109k
  if (seenType && seenOffset) //FIXME: What if there is an offset, but not a type? Should we still set the end of the preceding chunk to that offset?
2423
104k
  {
2424
104k
    if (type == PAGE)
2425
10.5k
    {
2426
10.5k
      MSPUB_DEBUG_MSG(("page chunk: offset 0x%lx, seqnum 0x%x\n", offset, m_lastSeenSeqNum));
2427
10.5k
      m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0));
2428
10.5k
      m_pageChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
2429
10.5k
      return true;
2430
10.5k
    }
2431
94.1k
    else if (type == DOCUMENT)
2432
3.29k
    {
2433
3.29k
      MSPUB_DEBUG_MSG(("document chunk: offset 0x%lx, seqnum 0x%x\n", offset, m_lastSeenSeqNum));
2434
3.29k
      m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0));
2435
3.29k
      m_documentChunkIndex = unsigned(m_contentChunks.size() - 1);
2436
3.29k
      return true;
2437
3.29k
    }
2438
90.8k
    else if (type == SHAPE || type == ALTSHAPE || type == GROUP || type == TABLE || type == LOGO)
2439
33.0k
    {
2440
33.0k
      MSPUB_DEBUG_MSG(("shape chunk: offset 0x%lx, seqnum 0x%x, parent seqnum: 0x%x\n", offset, m_lastSeenSeqNum, parentSeqNum));
2441
33.0k
      m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0));
2442
33.0k
      m_shapeChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
2443
33.0k
      if (type == ALTSHAPE)
2444
7.58k
      {
2445
7.58k
        m_alternateShapeSeqNums.push_back(m_lastSeenSeqNum);
2446
7.58k
      }
2447
33.0k
      return true;
2448
33.0k
    }
2449
57.7k
    else if (type == CELLS)
2450
1.32k
    {
2451
1.32k
      m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0));
2452
1.32k
      m_cellsChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
2453
1.32k
      return true;
2454
1.32k
    }
2455
56.4k
    else if (type == PALETTE)
2456
2.41k
    {
2457
2.41k
      m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0));
2458
2.41k
      m_paletteChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
2459
2.41k
      return true;
2460
2.41k
    }
2461
54.0k
    else if (type == BORDER_ART)
2462
2.31k
    {
2463
2.31k
      m_contentChunks.push_back(ContentChunkReference(type, offset, 0,
2464
2.31k
                                                      m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0));
2465
2.31k
      m_borderArtChunkIndices.push_back(
2466
2.31k
        unsigned(m_contentChunks.size() - 1));
2467
2.31k
      return true;
2468
2.31k
    }
2469
51.7k
    else if (type == FONT)
2470
1.44k
    {
2471
1.44k
      m_contentChunks.push_back(ContentChunkReference(type, offset, 0,
2472
1.44k
                                                      m_lastSeenSeqNum,
2473
1.44k
                                                      seenParentSeqNum ? parentSeqNum : 0));
2474
1.44k
      m_fontChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
2475
1.44k
      return true;
2476
1.44k
    }
2477
50.2k
    m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0));
2478
50.2k
    m_unknownChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
2479
50.2k
  }
2480
54.8k
  return false;
2481
109k
}
2482
2483
bool MSPUBParser::isBlockDataString(unsigned type)
2484
1.21M
{
2485
1.21M
  return type == STRING_CONTAINER;
2486
1.21M
}
2487
void MSPUBParser::skipBlock(librevenge::RVNGInputStream *input, MSPUBBlockInfo block)
2488
8.89M
{
2489
8.89M
  input->seek(block.dataOffset + block.dataLength, librevenge::RVNG_SEEK_SET);
2490
8.89M
}
2491
2492
EscherContainerInfo MSPUBParser::parseEscherContainer(librevenge::RVNGInputStream *input)
2493
1.02M
{
2494
1.02M
  EscherContainerInfo info;
2495
1.02M
  info.initial = readU16(input);
2496
1.02M
  info.type = readU16(input);
2497
1.02M
  info.contentsLength = readU32(input);
2498
1.02M
  info.contentsOffset = input->tell();
2499
1.02M
  MSPUB_DEBUG_MSG(("Parsed escher container: type 0x%x, contentsOffset 0x%lx, contentsLength 0x%lx\n", info.type, info.contentsOffset, info.contentsLength));
2500
1.02M
  return info;
2501
1.02M
}
2502
2503
MSPUBBlockInfo MSPUBParser::parseBlock(librevenge::RVNGInputStream *input, bool skipHierarchicalData)
2504
70.0M
{
2505
70.0M
  MSPUBBlockInfo info;
2506
70.0M
  info.startPosition = input->tell();
2507
70.0M
  info.id = readU8(input);
2508
70.0M
  info.type = readU8(input);
2509
70.0M
  info.dataOffset = input->tell();
2510
70.0M
  int len = getBlockDataLength(info.type);
2511
70.0M
  bool varLen = len < 0;
2512
70.0M
  if (varLen)
2513
1.21M
  {
2514
1.21M
    info.dataLength = readU32(input);
2515
1.21M
    if (isBlockDataString(info.type))
2516
163k
    {
2517
163k
      info.stringData = std::vector<unsigned char>();
2518
163k
      readNBytes(input, info.dataLength - 4, info.stringData);
2519
163k
    }
2520
1.05M
    else if (skipHierarchicalData)
2521
459k
    {
2522
459k
      skipBlock(input, info);
2523
459k
    }
2524
1.21M
    info.data = 0;
2525
1.21M
  }
2526
68.7M
  else
2527
68.7M
  {
2528
68.7M
    info.dataLength = len;
2529
68.7M
    switch (info.dataLength)
2530
68.7M
    {
2531
0
    case 1:
2532
0
      info.data = readU8(input);
2533
0
      break;
2534
2.86M
    case 2:
2535
2.86M
      info.data = readU16(input);
2536
2.86M
      break;
2537
4.01M
    case 4:
2538
4.01M
      info.data = readU32(input);
2539
4.01M
      break;
2540
330k
    case 8:
2541
382k
    case 16:
2542
417k
    case 24:
2543
      //FIXME: Not doing anything with this data for now.
2544
417k
      skipBlock(input, info);
2545
417k
      MSPUB_FALLTHROUGH;
2546
61.9M
    default:
2547
61.9M
      info.data = 0;
2548
68.7M
    }
2549
68.7M
  }
2550
70.0M
  MSPUB_DEBUG_MSG(("parseBlock dataOffset 0x%lx, id 0x%x, type 0x%x, dataLength 0x%lx, integral data 0x%x\n", info.dataOffset, info.id, info.type, info.dataLength, info.data));
2551
70.0M
  return info;
2552
70.0M
}
2553
2554
PageType MSPUBParser::getPageTypeBySeqNum(unsigned seqNum)
2555
10.3k
{
2556
10.3k
  switch (seqNum)
2557
10.3k
  {
2558
592
  case 0x10d:
2559
610
  case 0x110:
2560
640
  case 0x113:
2561
680
  case 0x117:
2562
680
    return DUMMY_PAGE;
2563
9.70k
  default:
2564
9.70k
    return NORMAL;
2565
10.3k
  }
2566
10.3k
}
2567
2568
bool MSPUBParser::parsePaletteChunk(librevenge::RVNGInputStream *input, const ContentChunkReference &chunk)
2569
2.53k
{
2570
2.53k
  unsigned length = readU32(input);
2571
1.16M
  while (stillReading(input, chunk.offset + length))
2572
1.15M
  {
2573
1.15M
    MSPUBBlockInfo info = parseBlock(input);
2574
1.15M
    if (info.type == 0xA0)
2575
1.73k
    {
2576
2.77M
      while (stillReading(input, info.dataOffset + info.dataLength))
2577
2.76M
      {
2578
2.76M
        MSPUBBlockInfo subInfo = parseBlock(input);
2579
2.76M
        if (subInfo.type == GENERAL_CONTAINER)
2580
361k
        {
2581
361k
          parsePaletteEntry(input, subInfo);
2582
361k
        }
2583
2.40M
        else if (subInfo.type == DUMMY)
2584
26.4k
        {
2585
26.4k
          m_collector->addPaletteColor(Color());
2586
26.4k
        }
2587
2.76M
        skipBlock(input, subInfo);
2588
2.76M
      }
2589
1.73k
    }
2590
1.15M
    skipBlock(input, info);
2591
1.15M
  }
2592
2.53k
  return true;
2593
2.53k
}
2594
2595
void MSPUBParser::parsePaletteEntry(librevenge::RVNGInputStream *input, MSPUBBlockInfo info)
2596
361k
{
2597
2.17M
  while (stillReading(input, info.dataOffset + info.dataLength))
2598
1.81M
  {
2599
1.81M
    MSPUBBlockInfo subInfo = parseBlock(input, true);
2600
1.81M
    if (subInfo.id == 0x01)
2601
5.52k
    {
2602
5.52k
      m_collector->addPaletteColor(Color(subInfo.data & 0xFF, (subInfo.data >> 8) & 0xFF, (subInfo.data >> 16) & 0xFF));
2603
5.52k
    }
2604
1.81M
  }
2605
361k
}
2606
2607
bool MSPUBParser::parseMetaData()
2608
4.35k
{
2609
4.35k
  m_input->seek(0, librevenge::RVNG_SEEK_SET);
2610
4.35k
  MSPUBMetaData metaData;
2611
2612
4.35k
  std::unique_ptr<librevenge::RVNGInputStream> sumaryInfo(m_input->getSubStreamByName("\x05SummaryInformation"));
2613
4.35k
  if (sumaryInfo)
2614
1.59k
  {
2615
1.59k
    metaData.parse(sumaryInfo.get());
2616
1.59k
  }
2617
2618
4.35k
  std::unique_ptr<librevenge::RVNGInputStream> docSumaryInfo(m_input->getSubStreamByName("\005DocumentSummaryInformation"));
2619
4.35k
  if (docSumaryInfo)
2620
1.03k
  {
2621
1.03k
    metaData.parse(docSumaryInfo.get());
2622
1.03k
  }
2623
2624
4.35k
  m_input->seek(0, librevenge::RVNG_SEEK_SET);
2625
4.35k
  metaData.parseTimes(m_input);
2626
4.35k
  m_collector->collectMetaData(metaData.getMetaData());
2627
2628
4.35k
  return true;
2629
4.35k
}
2630
2631
2632
}
2633
2634
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */