Coverage Report

Created: 2026-10-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libvisio/src/lib/VSDParser.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 libvisio 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 "VSDParser.h"
11
12
#include <librevenge-stream/librevenge-stream.h>
13
#include <locale.h>
14
#include <sstream>
15
#include <string>
16
#include <cmath>
17
#include <set>
18
#include "libvisio_utils.h"
19
#include "VSDInternalStream.h"
20
#include "VSDDocumentStructure.h"
21
#include "VSDContentCollector.h"
22
#include "VSDStylesCollector.h"
23
#include "VSDMetaData.h"
24
25
libvisio::VSDParser::VSDParser(librevenge::RVNGInputStream *input, librevenge::RVNGDrawingInterface *painter, librevenge::RVNGInputStream *container)
26
31.8k
  : m_input(input), m_painter(painter), m_container(container), m_header(), m_collector(nullptr), m_shapeList(), m_currentLevel(0),
27
31.8k
    m_stencils(), m_currentStencil(nullptr), m_shape(), m_isStencilStarted(false), m_isInStyles(false),
28
31.8k
    m_currentShapeLevel(0), m_currentShapeID(MINUS_ONE), m_currentLayerListLevel(0), m_extractStencils(false), m_colours(),
29
31.8k
    m_isBackgroundPage(false), m_isShapeStarted(false), m_shadowOffsetX(0.0), m_shadowOffsetY(0.0),
30
31.8k
    m_currentGeometryList(nullptr), m_currentGeomListCount(0), m_fonts(), m_names(), m_namesMapMap(),
31
31.8k
    m_currentPageName(), m_currentTabSet()
32
31.8k
{}
33
34
libvisio::VSDParser::~VSDParser()
35
31.8k
{
36
31.8k
}
37
38
void libvisio::VSDParser::_nameFromId(VSDName &name, unsigned id, unsigned level)
39
162k
{
40
162k
  name = VSDName();
41
162k
  std::map<unsigned, std::map<unsigned, VSDName> >::const_iterator iter1 = m_namesMapMap.find(level);
42
162k
  if (iter1 != m_namesMapMap.end())
43
48.7k
  {
44
48.7k
    auto iter = iter1->second.find(id);
45
48.7k
    if (iter != iter1->second.end())
46
1.19k
      name = iter->second;
47
48.7k
  }
48
162k
}
49
50
bool libvisio::VSDParser::getChunkHeader(librevenge::RVNGInputStream *input)
51
910k
{
52
910k
  unsigned char tmpChar = 0;
53
13.9M
  while (!input->isEnd() && !tmpChar)
54
13.0M
    tmpChar = readU8(input);
55
56
910k
  if (input->isEnd())
57
5.70k
    return false;
58
905k
  else
59
905k
    input->seek(-1, librevenge::RVNG_SEEK_CUR);
60
61
905k
  m_header.chunkType = readU32(input);
62
905k
  m_header.id = readU32(input);
63
905k
  m_header.list = readU32(input);
64
65
  // Certain chunk types seem to always have a trailer
66
905k
  m_header.trailer = 0;
67
905k
  if (m_header.list != 0 || m_header.chunkType == 0x71 || m_header.chunkType == 0x70 ||
68
674k
      m_header.chunkType == 0x6b || m_header.chunkType == 0x6a || m_header.chunkType == 0x69 ||
69
660k
      m_header.chunkType == 0x66 || m_header.chunkType == 0x65 || m_header.chunkType == 0x2c)
70
256k
    m_header.trailer += 8; // 8 byte trailer
71
72
905k
  m_header.dataLength = readU32(input);
73
905k
  m_header.level = readU16(input);
74
905k
  m_header.unknown = readU8(input);
75
76
905k
  unsigned trailerChunks [14] = {0x64, 0x65, 0x66, 0x69, 0x6a, 0x6b, 0x6f, 0x71,
77
905k
                                 0x92, 0xa9, 0xb4, 0xb6, 0xb9, 0xc7
78
905k
                                };
79
  // Add word separator under certain circumstances for v11
80
  // Below are known conditions, may be more or a simpler pattern
81
905k
  if (m_header.list != 0 || (m_header.level == 2 && m_header.unknown == 0x55) ||
82
651k
      (m_header.level == 2 && m_header.unknown == 0x54 && m_header.chunkType == 0xaa)
83
650k
      || (m_header.level == 3 && m_header.unknown != 0x50 && m_header.unknown != 0x54))
84
338k
  {
85
338k
    m_header.trailer += 4;
86
338k
  }
87
88
905k
  for (unsigned int trailerChunk : trailerChunks)
89
12.3M
  {
90
12.3M
    if (m_header.chunkType == trailerChunk && m_header.trailer != 12 && m_header.trailer != 4)
91
38.1k
    {
92
38.1k
      m_header.trailer += 4;
93
38.1k
      break;
94
38.1k
    }
95
12.3M
  }
96
97
  // Some chunks never have a trailer
98
905k
  if (m_header.chunkType == 0x1f || m_header.chunkType == 0xc9 ||
99
881k
      m_header.chunkType == 0x2d || m_header.chunkType == 0xd1)
100
27.8k
  {
101
27.8k
    m_header.trailer = 0;
102
27.8k
  }
103
905k
  return true;
104
910k
}
105
106
bool libvisio::VSDParser::parseMain()
107
31.8k
{
108
31.8k
  if (!m_input)
109
0
  {
110
0
    return false;
111
0
  }
112
  // Seek to trailer stream pointer
113
31.8k
  m_input->seek(0x24, librevenge::RVNG_SEEK_SET);
114
115
31.8k
  Pointer trailerPointer;
116
31.8k
  readPointer(m_input, trailerPointer);
117
31.8k
  bool compressed = ((trailerPointer.Format & 2) == 2);
118
31.8k
  unsigned shift = 0;
119
31.8k
  if (compressed)
120
25.0k
    shift = 4;
121
122
31.8k
  m_input->seek(trailerPointer.Offset, librevenge::RVNG_SEEK_SET);
123
31.8k
  VSDInternalStream trailerStream(m_input, trailerPointer.Length, compressed);
124
125
31.8k
  std::vector<std::map<unsigned, XForm> > groupXFormsSequence;
126
31.8k
  std::vector<std::map<unsigned, unsigned> > groupMembershipsSequence;
127
31.8k
  std::vector<std::list<unsigned> > documentPageShapeOrders;
128
129
31.8k
  VSDStylesCollector stylesCollector(groupXFormsSequence, groupMembershipsSequence, documentPageShapeOrders);
130
31.8k
  m_collector = &stylesCollector;
131
31.8k
  VSD_DEBUG_MSG(("VSDParser::parseMain 1st pass\n"));
132
31.8k
  if (!parseDocument(&trailerStream, shift))
133
4.64k
    return false;
134
135
27.2k
  _handleLevelChange(0);
136
137
27.2k
  VSDStyles styles = stylesCollector.getStyleSheets();
138
27.2k
  const std::optional<unsigned> varColInd = stylesCollector.getvariationColorIndex();
139
27.2k
  const std::optional<unsigned> varStyInd = stylesCollector.getvariationStyleIndex();
140
141
27.2k
  VSDContentCollector contentCollector(m_painter, groupXFormsSequence, groupMembershipsSequence, documentPageShapeOrders, styles, m_stencils, varColInd, varStyInd);
142
27.2k
  m_collector = &contentCollector;
143
27.2k
  if (m_container)
144
5.60k
    parseMetaData();
145
146
27.2k
  VSD_DEBUG_MSG(("VSDParser::parseMain 2nd pass\n"));
147
27.2k
  if (!parseDocument(&trailerStream, shift))
148
19
    return false;
149
150
27.2k
  return true;
151
27.2k
}
152
153
6.18k
void libvisio::VSDParser::parseMetaData() try
154
6.18k
{
155
6.18k
  if (!m_container)
156
0
    return;
157
6.18k
  m_container->seek(0, librevenge::RVNG_SEEK_SET);
158
6.18k
  if (!m_container->isStructured())
159
5.01k
    return;
160
1.17k
  VSDMetaData metaData;
161
162
1.17k
  const RVNGInputStreamPtr_t sumaryInfo(m_container->getSubStreamByName("\x05SummaryInformation"));
163
1.17k
  if (bool(sumaryInfo))
164
496
    metaData.parse(sumaryInfo.get());
165
166
1.17k
  const RVNGInputStreamPtr_t docSumaryInfo(m_container->getSubStreamByName("\005DocumentSummaryInformation"));
167
1.17k
  if (bool(docSumaryInfo))
168
479
    metaData.parse(docSumaryInfo.get());
169
170
1.17k
  m_container->seek(0, librevenge::RVNG_SEEK_SET);
171
1.17k
  metaData.parseTimes(m_container);
172
1.17k
  m_collector->collectMetaData(metaData.getMetaData());
173
1.17k
}
174
6.18k
catch (...)
175
6.18k
{
176
  // Ignore any exceptions in metadata. They are not important enough to stop parsing.
177
176
}
178
179
bool libvisio::VSDParser::parseDocument(librevenge::RVNGInputStream *input, unsigned shift)
180
59.0k
{
181
59.0k
  std::set<unsigned> visited;
182
59.0k
  try
183
59.0k
  {
184
59.0k
    handleStreams(input, VSD_TRAILER_STREAM, shift, 0, visited);
185
59.0k
    return true;
186
59.0k
  }
187
59.0k
  catch (...)
188
59.0k
  {
189
4.66k
    return false;
190
4.66k
  }
191
59.0k
}
192
193
bool libvisio::VSDParser::extractStencils()
194
0
{
195
0
  m_extractStencils = true;
196
0
  return parseMain();
197
0
}
198
199
void libvisio::VSDParser::readPointer(librevenge::RVNGInputStream *input, Pointer &ptr)
200
1.92M
{
201
1.92M
  ptr.Type = readU32(input);
202
1.92M
  input->seek(4, librevenge::RVNG_SEEK_CUR); // Skip dword
203
1.92M
  ptr.Offset = readU32(input);
204
1.92M
  ptr.Length = readU32(input);
205
1.92M
  ptr.Format = readU16(input);
206
1.92M
}
207
208
void libvisio::VSDParser::readPointerInfo(librevenge::RVNGInputStream *input, unsigned /* ptrType */, unsigned shift, unsigned &listSize, int &pointerCount)
209
43.7k
{
210
43.7k
  VSD_DEBUG_MSG(("VSDParser::readPointerInfo\n"));
211
43.7k
  input->seek(shift, librevenge::RVNG_SEEK_SET);
212
43.7k
  unsigned offset = readU32(input);
213
43.7k
  input->seek(offset+shift-4, librevenge::RVNG_SEEK_SET);
214
43.7k
  listSize = readU32(input);
215
43.7k
  pointerCount = readS32(input);
216
43.7k
  input->seek(4, librevenge::RVNG_SEEK_CUR);
217
43.7k
}
218
219
void libvisio::VSDParser::handleStreams(librevenge::RVNGInputStream *input, unsigned ptrType, unsigned shift, unsigned level, std::set<unsigned> &visited)
220
197k
{
221
197k
  VSD_DEBUG_MSG(("VSDParser::HandleStreams\n"));
222
197k
  std::vector<unsigned> pointerOrder;
223
197k
  std::map<unsigned, libvisio::Pointer> PtrList;
224
197k
  std::map<unsigned, libvisio::Pointer> FontFaces;
225
197k
  std::map<unsigned, libvisio::Pointer> NameList;
226
197k
  std::map<unsigned, libvisio::Pointer> NameIDX;
227
228
197k
  try
229
197k
  {
230
    // Parse out pointers to streams
231
197k
    unsigned listSize = 0;
232
197k
    int pointerCount = 0;
233
197k
    readPointerInfo(input, ptrType, shift, listSize, pointerCount);
234
20.3M
    for (int i = 0; i < pointerCount; i++)
235
20.1M
    {
236
20.1M
      Pointer ptr;
237
20.1M
      readPointer(input, ptr);
238
20.1M
      if (ptr.Type == 0)
239
5.88M
        continue;
240
241
14.2M
      if (ptr.Type == VSD_FONTFACES)
242
36.3k
        FontFaces[i] = ptr;
243
14.2M
      else if (ptr.Type == VSD_NAME_LIST2)
244
86.7k
        NameList[i] = ptr;
245
14.1M
      else if (ptr.Type == VSD_NAMEIDX || ptr.Type == VSD_NAMEIDX123)
246
1.04M
        NameIDX[i] = ptr;
247
13.0M
      else if (ptr.Type)
248
13.0M
        PtrList[i] = ptr;
249
14.2M
    }
250
197k
    if (listSize <= 1)
251
62.2k
      listSize = 0;
252
1.72M
    while (listSize--)
253
1.52M
      pointerOrder.push_back(readU32(input));
254
197k
  }
255
197k
  catch (const EndOfStreamException &)
256
197k
  {
257
127k
    pointerOrder.clear();
258
127k
    PtrList.clear();
259
127k
    FontFaces.clear();
260
127k
    NameList.clear();
261
127k
  }
262
263
197k
  std::map<unsigned, libvisio::Pointer>::iterator iter;
264
217k
  for (iter = NameList.begin(); iter != NameList.end(); ++iter)
265
20.0k
    handleStream(iter->second, iter->first, level+1, visited);
266
267
1.23M
  for (iter = NameIDX.begin(); iter != NameIDX.end(); ++iter)
268
1.04M
    handleStream(iter->second, iter->first, level+1, visited);
269
270
208k
  for (iter = FontFaces.begin(); iter != FontFaces.end(); ++iter)
271
11.2k
    handleStream(iter->second, iter->first, level+1, visited);
272
273
197k
  if (!pointerOrder.empty())
274
7.65k
  {
275
7.65k
    for (unsigned int j : pointerOrder)
276
448k
    {
277
448k
      iter = PtrList.find(j);
278
448k
      if (iter != PtrList.end())
279
42.6k
      {
280
42.6k
        handleStream(iter->second, iter->first, level+1, visited);
281
42.6k
        PtrList.erase(iter);
282
42.6k
      }
283
448k
    }
284
7.65k
  }
285
4.33M
  for (iter = PtrList.begin(); iter != PtrList.end(); ++iter)
286
4.13M
    handleStream(iter->second, iter->first, level+1, visited);
287
288
197k
}
289
290
void libvisio::VSDParser::handleStream(const Pointer &ptr, unsigned idx, unsigned level, std::set<unsigned> &visited)
291
5.25M
{
292
5.25M
  VSD_DEBUG_MSG(("VSDParser::HandleStream %u type 0x%x\n", idx, ptr.Type));
293
5.25M
  m_header.level = level;
294
5.25M
  m_header.id = idx;
295
5.25M
  m_header.chunkType = ptr.Type;
296
5.25M
  _handleLevelChange(level);
297
5.25M
  VSDStencil tmpStencil;
298
5.25M
  bool compressed = ((ptr.Format & 2) == 2);
299
5.25M
  m_input->seek(ptr.Offset, librevenge::RVNG_SEEK_SET);
300
5.25M
  VSDInternalStream tmpInput(m_input, ptr.Length, compressed);
301
5.25M
  m_header.dataLength = tmpInput.getSize();
302
5.25M
  unsigned shift = compressed ? 4 : 0;
303
5.25M
  switch (ptr.Type)
304
5.25M
  {
305
18.8k
  case VSD_STYLES:
306
18.8k
    m_isInStyles = true;
307
18.8k
    break;
308
46.3k
  case VSD_PAGES:
309
46.3k
    if (m_extractStencils)
310
0
      return;
311
46.3k
    break;
312
162k
  case VSD_PAGE:
313
162k
    if (m_extractStencils)
314
0
      return;
315
162k
    if (!(ptr.Format&0x1))
316
66.9k
      m_isBackgroundPage = true;
317
95.4k
    else
318
95.4k
      m_isBackgroundPage = false;
319
162k
    _nameFromId(m_currentPageName, idx, level+1);
320
162k
    m_collector->startPage(idx);
321
162k
    break;
322
29.9k
  case VSD_STENCILS:
323
29.9k
    if (m_extractStencils)
324
0
      break;
325
29.9k
    if (m_stencils.count())
326
11.2k
      return;
327
18.7k
    m_isStencilStarted = true;
328
18.7k
    break;
329
71.2k
  case VSD_STENCIL_PAGE:
330
71.2k
    if (m_extractStencils)
331
0
    {
332
0
      m_isBackgroundPage = false;
333
0
      _nameFromId(m_currentPageName, idx, level+1);
334
0
      m_collector->startPage(idx);
335
0
    }
336
71.2k
    else
337
71.2k
      m_currentStencil = &tmpStencil;
338
71.2k
    break;
339
15.6k
  case VSD_SHAPE_GROUP:
340
44.3k
  case VSD_SHAPE_SHAPE:
341
114k
  case VSD_SHAPE_FOREIGN:
342
114k
    m_currentShapeID = idx;
343
114k
    break;
344
28.3k
  case VSD_OLE_LIST:
345
28.3k
    if (!m_shape.m_foreign)
346
15.8k
      m_shape.m_foreign = std::make_unique<ForeignData>();
347
28.3k
    m_shape.m_foreign->dataId = idx;
348
28.3k
    break;
349
4.78M
  default:
350
4.78M
    break;
351
5.25M
  }
352
353
5.24M
  if ((ptr.Format >> 4) == 0x4 || (ptr.Format >> 4) == 0x5 || (ptr.Format >> 4) == 0x0)
354
2.22M
  {
355
2.22M
    handleBlob(&tmpInput, shift, level+1);
356
2.22M
    if ((ptr.Format >> 4) == 0x5 && ptr.Type != VSD_COLORS)
357
326k
    {
358
      // Each stream offset is handled once, however many pointers reach it.
359
326k
      if (visited.insert(ptr.Offset).second)
360
138k
        handleStreams(&tmpInput, ptr.Type, shift, level+1, visited);
361
326k
    }
362
2.22M
  }
363
3.01M
  else if ((ptr.Format >> 4) == 0xd || (ptr.Format >> 4) == 0xc || (ptr.Format >> 4) == 0x8)
364
888k
    handleChunks(&tmpInput, level+1);
365
366
5.24M
  switch (ptr.Type)
367
5.24M
  {
368
18.5k
  case VSD_STYLES:
369
18.5k
    _handleLevelChange(0);
370
18.5k
    m_isInStyles = false;
371
18.5k
    break;
372
161k
  case VSD_PAGE:
373
161k
    _handleLevelChange(0);
374
161k
    m_collector->endPage();
375
161k
    break;
376
46.2k
  case VSD_PAGES:
377
46.2k
    _handleLevelChange(0);
378
46.2k
    m_collector->endPages();
379
46.2k
    break;
380
18.5k
  case VSD_STENCILS:
381
18.5k
    _handleLevelChange(0);
382
18.5k
    if (m_extractStencils)
383
0
      m_collector->endPages();
384
18.5k
    else
385
18.5k
      m_isStencilStarted = false;
386
18.5k
    break;
387
71.1k
  case VSD_STENCIL_PAGE:
388
71.1k
    _handleLevelChange(0);
389
71.1k
    if (m_extractStencils)
390
0
      m_collector->endPage();
391
71.1k
    else if (m_currentStencil)
392
71.0k
    {
393
71.0k
      m_stencils.addStencil(idx, *m_currentStencil);
394
71.0k
      m_currentStencil = nullptr;
395
71.0k
    }
396
71.1k
    break;
397
15.6k
  case VSD_SHAPE_GROUP:
398
44.2k
  case VSD_SHAPE_SHAPE:
399
114k
  case VSD_SHAPE_FOREIGN:
400
114k
    if (m_isStencilStarted)
401
4.91k
    {
402
4.91k
      _handleLevelChange(0);
403
4.91k
      if (m_currentStencil)
404
3.82k
        m_currentStencil->addStencilShape(m_shape.m_shapeId, m_shape);
405
4.91k
    }
406
114k
    break;
407
4.80M
  default:
408
4.80M
    break;
409
5.24M
  }
410
411
5.24M
}
412
413
void libvisio::VSDParser::handleBlob(librevenge::RVNGInputStream *input, unsigned shift, unsigned level)
414
2.22M
{
415
2.22M
  try
416
2.22M
  {
417
2.22M
    m_header.level = level;
418
2.22M
    input->seek(shift, librevenge::RVNG_SEEK_SET);
419
2.22M
    m_header.dataLength -= shift;
420
2.22M
    _handleLevelChange(m_header.level);
421
2.22M
    handleChunk(input);
422
2.22M
  }
423
2.22M
  catch (EndOfStreamException &)
424
2.22M
  {
425
376k
    VSD_DEBUG_MSG(("VSDParser::handleBlob - catching EndOfStreamException\n"));
426
376k
  }
427
2.22M
}
428
429
void libvisio::VSDParser::handleChunks(librevenge::RVNGInputStream *input, unsigned level)
430
888k
{
431
888k
  long endPos = 0;
432
433
9.69M
  while (!input->isEnd())
434
8.83M
  {
435
8.83M
    if (!getChunkHeader(input))
436
20.3k
      return;
437
8.81M
    m_header.level += level;
438
8.81M
    endPos = m_header.dataLength+m_header.trailer+input->tell();
439
440
8.81M
    _handleLevelChange(m_header.level);
441
8.81M
    VSD_DEBUG_MSG(("VSDParser::handleChunks - parsing chunk type 0x%x\n", m_header.chunkType));
442
8.81M
    handleChunk(input);
443
8.81M
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
444
8.81M
  }
445
888k
}
446
447
void libvisio::VSDParser::handleChunk(librevenge::RVNGInputStream *input)
448
11.0M
{
449
11.0M
  switch (m_header.chunkType)
450
11.0M
  {
451
101k
  case VSD_SHAPE_GROUP:
452
316k
  case VSD_SHAPE_SHAPE:
453
725k
  case VSD_SHAPE_FOREIGN:
454
725k
    readShape(input);
455
725k
    break;
456
20.1k
  case VSD_XFORM_DATA:
457
20.1k
    readXFormData(input);
458
20.1k
    break;
459
13.0k
  case VSD_XFORM_1D:
460
13.0k
    readXForm1D(input);
461
13.0k
    break;
462
29.3k
  case VSD_TEXT_XFORM:
463
29.3k
    readTxtXForm(input);
464
29.3k
    break;
465
47.8k
  case VSD_SHAPE_LIST:
466
47.8k
    readShapeList(input);
467
47.8k
    break;
468
282k
  case VSD_SHAPE_ID:
469
282k
    readShapeId(input);
470
282k
    break;
471
131k
  case VSD_LINE:
472
131k
    readLine(input);
473
131k
    break;
474
41.8k
  case VSD_PROP_LIST:
475
41.8k
    readPropList(input);
476
41.8k
    break;
477
139k
  case VSD_FILL_AND_SHADOW:
478
139k
    readFillAndShadow(input);
479
139k
    break;
480
305k
  case VSD_GEOM_LIST:
481
305k
    readGeomList(input);
482
305k
    break;
483
45.8k
  case VSD_GEOMETRY:
484
45.8k
    readGeometry(input);
485
45.8k
    break;
486
202k
  case VSD_MOVE_TO:
487
202k
    readMoveTo(input);
488
202k
    break;
489
183k
  case VSD_LINE_TO:
490
183k
    readLineTo(input);
491
183k
    break;
492
83.7k
  case VSD_ARC_TO:
493
83.7k
    readArcTo(input);
494
83.7k
    break;
495
66.6k
  case VSD_ELLIPSE:
496
66.6k
    readEllipse(input);
497
66.6k
    break;
498
62.7k
  case VSD_ELLIPTICAL_ARC_TO:
499
62.7k
    readEllipticalArcTo(input);
500
62.7k
    break;
501
68.6k
  case VSD_NURBS_TO:
502
68.6k
    readNURBSTo(input);
503
68.6k
    break;
504
59.7k
  case VSD_POLYLINE_TO:
505
59.7k
    readPolylineTo(input);
506
59.7k
    break;
507
100k
  case VSD_INFINITE_LINE:
508
100k
    readInfiniteLine(input);
509
100k
    break;
510
242k
  case VSD_SHAPE_DATA:
511
242k
    readShapeData(input);
512
242k
    break;
513
102k
  case VSD_FOREIGN_DATA_TYPE:
514
102k
    readForeignDataType(input);
515
102k
    break;
516
64.6k
  case VSD_FOREIGN_DATA:
517
64.6k
    readForeignData(input);
518
64.6k
    break;
519
52.3k
  case VSD_OLE_LIST:
520
52.3k
    readOLEList(input);
521
52.3k
    break;
522
23.0k
  case VSD_OLE_DATA:
523
23.0k
    readOLEData(input);
524
23.0k
    break;
525
77.0k
  case VSD_NAMEIDX:
526
77.0k
    readNameIDX(input);
527
77.0k
    break;
528
289k
  case VSD_NAMEIDX123:
529
289k
    readNameIDX123(input);
530
289k
    break;
531
26.0k
  case VSD_PAGE_PROPS:
532
26.0k
    readPageProps(input);
533
26.0k
    break;
534
56.8k
  case VSD_CHAR_LIST:
535
56.8k
    readCharList(input);
536
56.8k
    break;
537
19.9k
  case VSD_PARA_LIST:
538
19.9k
    readParaList(input);
539
19.9k
    break;
540
138k
  case VSD_TEXT:
541
138k
    readText(input);
542
138k
    break;
543
159k
  case VSD_CHAR_IX:
544
159k
    readCharIX(input);
545
159k
    break;
546
133k
  case VSD_PARA_IX:
547
133k
    readParaIX(input);
548
133k
    break;
549
41.2k
  case VSD_TEXT_BLOCK:
550
41.2k
    readTextBlock(input);
551
41.2k
    break;
552
63.7k
  case VSD_FONT_IX: // ver 6 only
553
63.7k
    readFontIX(input);
554
63.7k
    break;
555
101k
  case VSD_PAGE:
556
101k
    readPage(input);
557
101k
    break;
558
35.1k
  case VSD_STENCIL_PAGE:
559
35.1k
    if (m_extractStencils)
560
0
      readPage(input);
561
35.1k
    break;
562
41.6k
  case VSD_SPLINE_START:
563
41.6k
    readSplineStart(input);
564
41.6k
    break;
565
56.4k
  case VSD_SPLINE_KNOT:
566
56.4k
    readSplineKnot(input);
567
56.4k
    break;
568
29.4k
  case VSD_NAME_LIST:
569
29.4k
    readNameList(input);
570
29.4k
    break;
571
53.3k
  case VSD_NAME_LIST2:
572
53.3k
    readNameList2(input);
573
53.3k
    break;
574
33.6k
  case VSD_NAME:
575
33.6k
    readName(input);
576
33.6k
    break;
577
110k
  case VSD_NAME2:
578
110k
    readName2(input);
579
110k
    break;
580
24.7k
  case VSD_FIELD_LIST:
581
24.7k
    readFieldList(input);
582
24.7k
    break;
583
65.2k
  case VSD_TEXT_FIELD:
584
65.2k
    readTextField(input);
585
65.2k
    break;
586
565k
  case VSD_STYLE_SHEET:
587
565k
    readStyleSheet(input);
588
565k
    break;
589
20.6k
  case VSD_PAGE_SHEET:
590
20.6k
    readPageSheet(input);
591
20.6k
    break;
592
9.12k
  case VSD_COLORS:
593
9.12k
    readColours(input);
594
9.12k
    break;
595
51.1k
  case VSD_FONTFACE: // substreams of FONTAFACES stream, ver 11 only
596
51.1k
    readFont(input);
597
51.1k
    break;
598
26.4k
  case VSD_MISC:
599
26.4k
    readMisc(input);
600
26.4k
    break;
601
25.4k
  case VSD_LAYER_LIST:
602
25.4k
    readLayerList(input);
603
25.4k
    break;
604
56.9k
  case VSD_LAYER:
605
56.9k
    readLayer(input);
606
56.9k
    break;
607
59.6k
  case VSD_LAYER_MEMBERSHIP:
608
59.6k
    readLayerMem(input);
609
59.6k
    break;
610
16.3k
  case VSD_TABS_DATA_LIST:
611
16.3k
    readTabsDataList(input);
612
16.3k
    break;
613
11.9k
  case VSD_TABS_DATA_1:
614
53.7k
  case VSD_TABS_DATA_2:
615
64.0k
  case VSD_TABS_DATA_3:
616
64.0k
    readTabsData(input);
617
64.0k
    break;
618
5.54M
  default:
619
5.54M
    m_collector->collectUnhandledChunk(m_header.id, m_header.level);
620
11.0M
  }
621
11.0M
}
622
623
void libvisio::VSDParser::_flushShape()
624
808k
{
625
808k
  if (!m_isShapeStarted)
626
304k
    return;
627
628
503k
  m_collector->collectShape(m_shape.m_shapeId, m_currentShapeLevel, m_shape.m_parent, m_shape.m_masterPage, m_shape.m_masterShape, m_shape.m_lineStyleId, m_shape.m_fillStyleId, m_shape.m_textStyleId, m_shape.m_aName);
629
630
503k
  m_collector->collectShapesOrder(0, m_currentShapeLevel+2, m_shape.m_shapeList.getShapesOrder());
631
632
503k
  m_collector->collectXFormData(m_currentShapeLevel+2, m_shape.m_xform);
633
634
503k
  m_collector->collectLayerMem(m_currentShapeLevel+2, m_shape.m_layerMem);
635
636
503k
  m_collector->collectMisc(m_currentShapeLevel+2, m_shape.m_misc);
637
638
503k
  if (m_shape.m_txtxform)
639
14.9k
    m_collector->collectTxtXForm(m_currentShapeLevel+2, *(m_shape.m_txtxform));
640
641
503k
  m_collector->collectLine(m_currentShapeLevel+2, m_shape.m_lineStyle.width, m_shape.m_lineStyle.colour, m_shape.m_lineStyle.pattern,
642
503k
                           m_shape.m_lineStyle.startMarker, m_shape.m_lineStyle.endMarker, m_shape.m_lineStyle.cap, m_shape.m_lineStyle.rounding,
643
503k
                           m_shape.m_lineStyle.qsLineColour, m_shape.m_lineStyle.qsLineMatrix);
644
645
503k
  m_collector->collectFillAndShadow(m_currentShapeLevel+2, m_shape.m_fillStyle.fgColour, m_shape.m_fillStyle.bgColour, m_shape.m_fillStyle.pattern,
646
503k
                                    m_shape.m_fillStyle.fgTransparency, m_shape.m_fillStyle.bgTransparency, m_shape.m_fillStyle.shadowPattern,
647
503k
                                    m_shape.m_fillStyle.shadowFgColour, m_shape.m_fillStyle.shadowOffsetX, m_shape.m_fillStyle.shadowOffsetY,
648
503k
                                    m_shape.m_fillStyle.qsFillColour, m_shape.m_fillStyle.qsShadowColour, m_shape.m_fillStyle.qsFillMatrix);
649
650
503k
  m_collector->collectTextBlock(m_currentShapeLevel+2, m_shape.m_textBlockStyle.leftMargin, m_shape.m_textBlockStyle.rightMargin,
651
503k
                                m_shape.m_textBlockStyle.topMargin, m_shape.m_textBlockStyle.bottomMargin, m_shape.m_textBlockStyle.verticalAlign,
652
503k
                                m_shape.m_textBlockStyle.isTextBkgndFilled, m_shape.m_textBlockStyle.textBkgndColour,
653
503k
                                m_shape.m_textBlockStyle.defaultTabStop, m_shape.m_textBlockStyle.textDirection);
654
655
503k
  if (m_shape.m_foreign)
656
30.6k
    m_collector->collectForeignDataType(m_currentShapeLevel+2, m_shape.m_foreign->type, m_shape.m_foreign->format,
657
30.6k
                                        m_shape.m_foreign->offsetX, m_shape.m_foreign->offsetY, m_shape.m_foreign->width, m_shape.m_foreign->height);
658
659
515k
  for (std::map<unsigned, NURBSData>::const_iterator iterNurbs = m_shape.m_nurbsData.begin(); iterNurbs != m_shape.m_nurbsData.end(); ++iterNurbs)
660
11.4k
    m_collector->collectShapeData(iterNurbs->first, m_currentShapeLevel+2, iterNurbs->second.xType, iterNurbs->second.yType,
661
11.4k
                                  iterNurbs->second.degree, iterNurbs->second.lastKnot, iterNurbs->second.points,
662
11.4k
                                  iterNurbs->second.knots, iterNurbs->second.weights);
663
664
508k
  for (std::map<unsigned, PolylineData>::const_iterator iterPoly = m_shape.m_polylineData.begin(); iterPoly != m_shape.m_polylineData.end(); ++iterPoly)
665
4.94k
    m_collector->collectShapeData(iterPoly->first, m_currentShapeLevel+2, iterPoly->second.xType, iterPoly->second.yType, iterPoly->second.points);
666
667
508k
  for (std::map<unsigned, VSDName>::const_iterator iterName = m_shape.m_names.begin(); iterName != m_shape.m_names.end(); ++iterName)
668
4.95k
    m_collector->collectName(iterName->first, m_currentShapeLevel+2, iterName->second.m_data, iterName->second.m_format);
669
670
503k
  if (m_shape.m_foreign && m_shape.m_foreign->data.size())
671
16.5k
    m_collector->collectForeignData(m_currentShapeLevel+1, m_shape.m_foreign->data);
672
673
503k
  m_collector->collectTabsDataList(m_currentShapeLevel+1, m_shape.m_tabSets);
674
675
503k
  if (!m_shape.m_fields.empty())
676
15.3k
    m_shape.m_fields.handle(m_collector);
677
678
503k
  if (m_shape.m_text.size())
679
97.2k
    m_collector->collectText(m_currentShapeLevel+1, m_shape.m_text, m_shape.m_textFormat);
680
681
682
634k
  for (std::map<unsigned, VSDGeometryList>::const_iterator iterGeom = m_shape.m_geometries.begin(); iterGeom != m_shape.m_geometries.end(); ++iterGeom)
683
131k
    iterGeom->second.handle(m_collector);
684
685
503k
  m_collector->collectDefaultCharStyle(m_shape.m_charStyle.charCount, m_shape.m_charStyle.font, m_shape.m_charStyle.colour,
686
503k
                                       m_shape.m_charStyle.size, m_shape.m_charStyle.bold, m_shape.m_charStyle.italic, m_shape.m_charStyle.underline,
687
503k
                                       m_shape.m_charStyle.doubleunderline, m_shape.m_charStyle.strikeout, m_shape.m_charStyle.doublestrikeout,
688
503k
                                       m_shape.m_charStyle.allcaps, m_shape.m_charStyle.initcaps, m_shape.m_charStyle.smallcaps,
689
503k
                                       m_shape.m_charStyle.superscript, m_shape.m_charStyle.subscript, m_shape.m_charStyle.scaleWidth);
690
691
503k
  m_shape.m_charList.handle(m_collector);
692
693
503k
  m_collector->collectDefaultParaStyle(m_shape.m_paraStyle.charCount, m_shape.m_paraStyle.indFirst, m_shape.m_paraStyle.indLeft,
694
503k
                                       m_shape.m_paraStyle.indRight, m_shape.m_paraStyle.spLine, m_shape.m_paraStyle.spBefore,
695
503k
                                       m_shape.m_paraStyle.spAfter, m_shape.m_paraStyle.align, m_shape.m_paraStyle.bullet,
696
503k
                                       m_shape.m_paraStyle.bulletStr, m_shape.m_paraStyle.bulletFont, m_shape.m_paraStyle.bulletFontSize,
697
503k
                                       m_shape.m_paraStyle.textPosAfterBullet, m_shape.m_paraStyle.flags);
698
699
503k
  m_shape.m_paraList.handle(m_collector);
700
503k
}
701
702
void libvisio::VSDParser::_handleLevelChange(unsigned level)
703
16.6M
{
704
16.6M
  if (level == m_currentLevel)
705
9.51M
    return;
706
7.11M
  if (level <= m_currentShapeLevel+1)
707
2.70M
  {
708
2.70M
    if (!m_shape.m_geometries.empty() && m_currentGeometryList && m_currentGeometryList->empty())
709
43.2k
    {
710
43.2k
      m_shape.m_geometries.erase(--m_currentGeomListCount);
711
43.2k
      m_currentGeometryList = nullptr;
712
43.2k
    }
713
2.70M
    m_collector->collectShapesOrder(0, m_currentShapeLevel+2, m_shapeList.getShapesOrder());
714
2.70M
    m_shapeList.clear();
715
716
2.70M
  }
717
7.11M
  if (level <= m_currentShapeLevel)
718
836k
  {
719
836k
    if (!m_isStencilStarted)
720
808k
    {
721
808k
      _flushShape();
722
808k
      m_shape.clear();
723
808k
      m_currentGeometryList = nullptr;
724
808k
    }
725
836k
    m_isShapeStarted = false;
726
836k
    m_currentShapeLevel = 0;
727
836k
  }
728
7.11M
  m_currentLevel = level;
729
7.11M
}
730
731
// --- READERS ---
732
733
void libvisio::VSDParser::readEllipticalArcTo(librevenge::RVNGInputStream *input)
734
62.7k
{
735
62.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
736
62.7k
  double x3 = readDouble(input); // End x
737
62.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
738
62.7k
  double y3 = readDouble(input); // End y
739
62.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
740
62.7k
  double x2 = readDouble(input); // Mid x
741
62.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
742
62.7k
  double y2 = readDouble(input); // Mid y
743
62.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
744
62.7k
  double angle = readDouble(input); // Angle
745
62.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
746
62.7k
  double ecc = readDouble(input); // Eccentricity
747
748
62.7k
  if (m_currentGeometryList)
749
44.3k
    m_currentGeometryList->addEllipticalArcTo(m_header.id, m_header.level, x3, y3, x2, y2, angle, ecc);
750
62.7k
}
751
752
753
void libvisio::VSDParser::readForeignData(librevenge::RVNGInputStream *input)
754
64.6k
{
755
64.6k
  unsigned long tmpBytesRead = 0;
756
64.6k
  const unsigned char *buffer = input->read(m_header.dataLength, tmpBytesRead);
757
64.6k
  if (m_header.dataLength != tmpBytesRead)
758
2.88k
    return;
759
61.7k
  librevenge::RVNGBinaryData binaryData(buffer, tmpBytesRead);
760
761
61.7k
  if (!m_shape.m_foreign)
762
15.3k
    m_shape.m_foreign = std::make_unique<ForeignData>();
763
61.7k
  m_shape.m_foreign->dataId = m_header.id;
764
61.7k
  m_shape.m_foreign->data = binaryData;
765
61.7k
}
766
767
void libvisio::VSDParser::readOLEList(librevenge::RVNGInputStream * /* input */)
768
52.3k
{
769
52.3k
}
770
771
void libvisio::VSDParser::readOLEData(librevenge::RVNGInputStream *input)
772
23.0k
{
773
23.0k
  unsigned long tmpBytesRead = 0;
774
23.0k
  const unsigned char *buffer = input->read(m_header.dataLength, tmpBytesRead);
775
23.0k
  if (m_header.dataLength != tmpBytesRead)
776
1.95k
    return;
777
21.0k
  librevenge::RVNGBinaryData oleData(buffer, tmpBytesRead);
778
779
21.0k
  if (!m_shape.m_foreign)
780
6.97k
    m_shape.m_foreign = std::make_unique<ForeignData>();
781
  // Append data instead of setting it - allows multi-stream OLE objects
782
21.0k
  m_shape.m_foreign->data.append(oleData);
783
784
21.0k
}
785
786
void libvisio::VSDParser::readTabsData(librevenge::RVNGInputStream *input)
787
64.0k
{
788
64.0k
  m_shape.m_tabSets[m_header.id].m_numChars = getUInt(input);
789
64.0k
  unsigned char numStops = readU8(input);
790
64.0k
  m_shape.m_tabSets[m_header.id].m_tabStops.clear();
791
761k
  for (unsigned char i = 0; i < numStops; ++i)
792
697k
  {
793
697k
    input->seek(1, librevenge::RVNG_SEEK_CUR);
794
697k
    m_shape.m_tabSets[m_header.id].m_tabStops[i].m_position = readDouble(input);
795
697k
    m_shape.m_tabSets[m_header.id].m_tabStops[i].m_alignment = readU8(input);
796
697k
    m_shape.m_tabSets[m_header.id].m_tabStops[i].m_leader = readU8(input);
797
697k
  }
798
64.0k
}
799
800
void libvisio::VSDParser::readNameIDX(librevenge::RVNGInputStream *input)
801
25.8k
{
802
25.8k
  std::map<unsigned, VSDName> names;
803
25.8k
  unsigned recordCount = readU32(input);
804
25.8k
  if (recordCount > getRemainingLength(input) / 13)
805
6.21k
    recordCount = getRemainingLength(input) / 13;
806
826k
  for (unsigned i = 0; i < recordCount; ++i)
807
800k
  {
808
800k
    unsigned nameId = readU32(input);
809
800k
    if (nameId != readU32(input))
810
407k
    {
811
407k
      VSD_DEBUG_MSG(("VSDParser::readNameIDX --> mismatch of first two dwords\n"));
812
407k
    }
813
800k
    unsigned elementId = readU32(input);
814
800k
    input->seek(1, librevenge::RVNG_SEEK_CUR);
815
800k
    std::map<unsigned, VSDName>::const_iterator iter = m_names.find(nameId);
816
800k
    if (iter != m_names.end())
817
263k
      names[elementId] = iter->second;
818
800k
  }
819
25.8k
  m_namesMapMap[m_header.level] = names;
820
25.8k
}
821
822
void libvisio::VSDParser::readNameIDX123(librevenge::RVNGInputStream *input)
823
289k
{
824
289k
  std::map<unsigned, VSDName> names;
825
289k
  long endPosition = input->tell() + m_header.dataLength;
826
423M
  while (!input->isEnd() && input->tell() < endPosition)
827
423M
  {
828
423M
    unsigned nameId = getUInt(input);
829
423M
    unsigned elementId = getUInt(input);
830
423M
    std::map<unsigned, VSDName>::const_iterator iter = m_names.find(nameId);
831
423M
    if (iter != m_names.end())
832
25.5M
      names[elementId] = iter->second;
833
423M
  }
834
289k
  m_namesMapMap[m_header.level] = names;
835
836
289k
}
837
838
void libvisio::VSDParser::readEllipse(librevenge::RVNGInputStream *input)
839
66.6k
{
840
66.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
841
66.6k
  double cx = readDouble(input);
842
66.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
843
66.6k
  double cy = readDouble(input);
844
66.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
845
66.6k
  double xleft = readDouble(input);
846
66.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
847
66.6k
  double yleft = readDouble(input);
848
66.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
849
66.6k
  double xtop = readDouble(input);
850
66.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
851
66.6k
  double ytop = readDouble(input);
852
853
66.6k
  if (m_currentGeometryList)
854
53.7k
    m_currentGeometryList->addEllipse(m_header.id, m_header.level, cx, cy, xleft, yleft, xtop, ytop);
855
66.6k
}
856
857
void libvisio::VSDParser::readLine(librevenge::RVNGInputStream *input)
858
16.9k
{
859
16.9k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
860
16.9k
  double strokeWidth = readDouble(input);
861
16.9k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
862
16.9k
  Colour c;
863
16.9k
  c.r = readU8(input);
864
16.9k
  c.g = readU8(input);
865
16.9k
  c.b = readU8(input);
866
16.9k
  c.a = readU8(input);
867
16.9k
  unsigned char linePattern = readU8(input);
868
16.9k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
869
16.9k
  double rounding = readDouble(input);
870
16.9k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
871
16.9k
  unsigned char startMarker = readU8(input);
872
16.9k
  unsigned char endMarker = readU8(input);
873
16.9k
  unsigned char lineCap = readU8(input);
874
875
16.9k
  if (m_isInStyles)
876
6.00k
    m_collector->collectLineStyle(m_header.level, strokeWidth, c, linePattern, startMarker, endMarker, lineCap, rounding, -1, -1);
877
10.9k
  else
878
10.9k
    m_shape.m_lineStyle.override(VSDOptionalLineStyle(strokeWidth, c, linePattern, startMarker, endMarker, lineCap, rounding, -1, -1));
879
16.9k
}
880
881
void libvisio::VSDParser::readTextBlock(librevenge::RVNGInputStream *input)
882
14.8k
{
883
14.8k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
884
14.8k
  double leftMargin = readDouble(input);
885
14.8k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
886
14.8k
  double rightMargin = readDouble(input);
887
14.8k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
888
14.8k
  double topMargin = readDouble(input);
889
14.8k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
890
14.8k
  double bottomMargin = readDouble(input);
891
14.8k
  unsigned char verticalAlign = readU8(input);
892
14.8k
  const unsigned char bgColourIdx = readU8(input);
893
  // The TextBkgnd cell can have any value from 0 through 24, or 255.
894
  // The values 0 and 255 (visTxtBlklOpaque) both indicate a transparent text background.
895
14.8k
  const bool isBgFilled = bgColourIdx != 0 && bgColourIdx != 0xff;
896
14.8k
  Colour c;
897
14.8k
  c.r = readU8(input);
898
14.8k
  c.g = readU8(input);
899
14.8k
  c.b = readU8(input);
900
14.8k
  c.a = readU8(input);
901
14.8k
  if (isBgFilled)
902
5.47k
    c = _colourFromIndex(bgColourIdx - 1);
903
14.8k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
904
14.8k
  double defaultTabStop = readDouble(input);
905
14.8k
  input->seek(12, librevenge::RVNG_SEEK_CUR);
906
14.8k
  unsigned char textDirection = readU8(input);
907
908
14.8k
  if (m_isInStyles)
909
4.58k
    m_collector->collectTextBlockStyle(m_header.level, leftMargin, rightMargin, topMargin, bottomMargin,
910
4.58k
                                       verticalAlign, isBgFilled, c, defaultTabStop, textDirection);
911
10.2k
  else
912
10.2k
    m_shape.m_textBlockStyle.override(VSDOptionalTextBlockStyle(leftMargin, rightMargin, topMargin, bottomMargin,
913
10.2k
                                                                verticalAlign, isBgFilled, c, defaultTabStop, textDirection));
914
14.8k
}
915
916
void libvisio::VSDParser::readGeomList(librevenge::RVNGInputStream *input)
917
29.1k
{
918
29.1k
  if (!m_shape.m_geometries.empty() && m_currentGeometryList && m_currentGeometryList->empty())
919
6.40k
    m_shape.m_geometries.erase(--m_currentGeomListCount);
920
  // Since this is a map, this will default construct an element and then
921
  // the m_currentGeometryList pointer takes its address and we will work
922
  // on it over that pointer.
923
29.1k
  m_currentGeometryList = &m_shape.m_geometries[m_currentGeomListCount++];
924
925
29.1k
  if (m_header.trailer)
926
22.9k
  {
927
22.9k
    uint32_t subHeaderLength = readU32(input);
928
22.9k
    uint32_t childrenListLength = readU32(input);
929
22.9k
    input->seek(subHeaderLength, librevenge::RVNG_SEEK_CUR);
930
22.9k
    std::vector<unsigned> geometryOrder;
931
22.9k
    if (childrenListLength > getRemainingLength(input))
932
5.08k
      childrenListLength = getRemainingLength(input);
933
22.9k
    geometryOrder.reserve(childrenListLength / sizeof(uint32_t));
934
2.36M
    for (size_t i = 0; i < (childrenListLength / sizeof(uint32_t)); i++)
935
2.34M
      geometryOrder.push_back(readU32(input));
936
937
22.9k
    if (m_currentGeometryList)
938
22.6k
      m_currentGeometryList->setElementsOrder(geometryOrder);
939
22.9k
  }
940
941
  // We want the collectors to still get the level information
942
29.1k
  if (!m_isStencilStarted)
943
24.8k
    m_collector->collectUnhandledChunk(m_header.id, m_header.level);
944
29.1k
}
945
946
void libvisio::VSDParser::readCharList(librevenge::RVNGInputStream *input)
947
24.6k
{
948
  // We want the collectors to still get the level information
949
24.6k
  if (!m_isStencilStarted)
950
23.3k
    m_collector->collectUnhandledChunk(m_header.id, m_header.level);
951
952
24.6k
  if (m_header.trailer)
953
23.3k
  {
954
23.3k
    uint32_t subHeaderLength = readU32(input);
955
23.3k
    uint32_t childrenListLength = readU32(input);
956
23.3k
    input->seek(subHeaderLength, librevenge::RVNG_SEEK_CUR);
957
23.3k
    if (childrenListLength > getRemainingLength(input))
958
8.47k
      childrenListLength = getRemainingLength(input);
959
23.3k
    std::vector<unsigned> characterOrder;
960
23.3k
    characterOrder.reserve(childrenListLength / sizeof(uint32_t));
961
6.04M
    for (size_t i = 0; i < (childrenListLength / sizeof(uint32_t)); i++)
962
6.01M
      characterOrder.push_back(readU32(input));
963
964
23.3k
    m_shape.m_charList.setElementsOrder(characterOrder);
965
23.3k
  }
966
24.6k
}
967
968
void libvisio::VSDParser::readParaList(librevenge::RVNGInputStream *input)
969
12.9k
{
970
  // We want the collectors to still get the level information
971
12.9k
  if (!m_isStencilStarted)
972
11.9k
    m_collector->collectUnhandledChunk(m_header.id, m_header.level);
973
974
12.9k
  if (m_header.trailer)
975
12.0k
  {
976
12.0k
    uint32_t subHeaderLength = readU32(input);
977
12.0k
    uint32_t childrenListLength = readU32(input);
978
12.0k
    input->seek(subHeaderLength, librevenge::RVNG_SEEK_CUR);
979
12.0k
    if (childrenListLength > getRemainingLength(input))
980
4.36k
      childrenListLength = getRemainingLength(input);
981
12.0k
    std::vector<unsigned> paragraphOrder;
982
12.0k
    paragraphOrder.reserve(childrenListLength / sizeof(uint32_t));
983
4.44M
    for (size_t i = 0; i < (childrenListLength / sizeof(uint32_t)); i++)
984
4.43M
      paragraphOrder.push_back(readU32(input));
985
986
12.0k
    m_shape.m_paraList.setElementsOrder(paragraphOrder);
987
12.0k
  }
988
12.9k
}
989
990
void libvisio::VSDParser::readPropList(librevenge::RVNGInputStream * /* input */)
991
26.7k
{
992
26.7k
}
993
994
void libvisio::VSDParser::readTabsDataList(librevenge::RVNGInputStream *input)
995
7.78k
{
996
  // We want the collectors to still get the level information
997
7.78k
  if (!m_isStencilStarted)
998
6.55k
    m_collector->collectUnhandledChunk(m_header.id, m_header.level);
999
1000
7.78k
  if (m_header.trailer)
1001
7.10k
  {
1002
7.10k
    uint32_t subHeaderLength = readU32(input);
1003
7.10k
    uint32_t childrenListLength = readU32(input);
1004
7.10k
    input->seek(subHeaderLength, librevenge::RVNG_SEEK_CUR);
1005
7.10k
    if (childrenListLength > getRemainingLength(input))
1006
2.08k
      childrenListLength = getRemainingLength(input);
1007
7.10k
    std::vector<unsigned> tabsOrder;
1008
7.10k
    tabsOrder.reserve(childrenListLength / sizeof(uint32_t));
1009
525k
    for (size_t i = 0; i < (childrenListLength / sizeof(uint32_t)); i++)
1010
518k
      tabsOrder.push_back(readU32(input));
1011
7.10k
  }
1012
7.78k
}
1013
1014
void libvisio::VSDParser::readLayerList(librevenge::RVNGInputStream *input)
1015
25.4k
{
1016
  // We want the collectors to still get the level information
1017
25.4k
  if (!m_isStencilStarted)
1018
23.2k
    m_collector->collectUnhandledChunk(m_header.id, m_header.level);
1019
1020
25.4k
  if (m_header.trailer)
1021
8.37k
  {
1022
8.37k
    uint32_t subHeaderLength = readU32(input);
1023
8.37k
    uint32_t childrenListLength = readU32(input);
1024
8.37k
    input->seek(subHeaderLength, librevenge::RVNG_SEEK_CUR);
1025
8.37k
    if (childrenListLength > getRemainingLength(input))
1026
2.31k
      childrenListLength = getRemainingLength(input);
1027
8.37k
    std::vector<unsigned> layerOrder;
1028
8.37k
    layerOrder.reserve(childrenListLength / sizeof(uint32_t));
1029
695k
    for (size_t i = 0; i < (childrenListLength / sizeof(uint32_t)); i++)
1030
687k
      layerOrder.push_back(readU32(input));
1031
8.37k
  }
1032
25.4k
}
1033
1034
void libvisio::VSDParser::readLayer(librevenge::RVNGInputStream *input)
1035
56.9k
{
1036
56.9k
  libvisio::VSDLayer layer;
1037
56.9k
  input->seek(8, librevenge::RVNG_SEEK_CUR);
1038
56.9k
  unsigned char colourId = readU8(input);
1039
56.9k
  if (colourId == 0xff)
1040
2.53k
    input->seek(4, librevenge::RVNG_SEEK_CUR);
1041
54.4k
  else
1042
54.4k
  {
1043
54.4k
    libvisio::Colour colour;
1044
54.4k
    colour.r = readU8(input);
1045
54.4k
    colour.g = readU8(input);
1046
54.4k
    colour.b = readU8(input);
1047
54.4k
    colour.a = readU8(input);
1048
54.4k
    layer.m_colour = colour;
1049
54.4k
  }
1050
56.9k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1051
56.9k
  layer.m_visible = !!readU8(input);
1052
56.9k
  layer.m_printable = !!readU8(input);
1053
1054
56.9k
  m_collector->collectLayer(m_header.id, m_header.level, layer);
1055
56.9k
}
1056
1057
void libvisio::VSDParser::readLayerMem(librevenge::RVNGInputStream *input)
1058
5.04k
{
1059
5.04k
  input->seek(13, librevenge::RVNG_SEEK_CUR);
1060
5.04k
  unsigned textLength = readU8(input);
1061
1062
5.04k
  librevenge::RVNGBinaryData  textStream;
1063
5.04k
  unsigned long numBytesRead = 0;
1064
5.04k
  const unsigned char *tmpBuffer = input->read(textLength*2, numBytesRead);
1065
5.04k
  if (numBytesRead)
1066
3.39k
  {
1067
3.39k
    textStream.append(tmpBuffer, numBytesRead);
1068
3.39k
    m_shape.m_layerMem.m_data = textStream;
1069
3.39k
    m_shape.m_layerMem.m_format = libvisio::VSD_TEXT_UTF16;
1070
3.39k
  }
1071
1072
5.04k
}
1073
1074
void libvisio::VSDParser::readPage(librevenge::RVNGInputStream *input)
1075
14.7k
{
1076
14.7k
  input->seek(8, librevenge::RVNG_SEEK_CUR); //sub header length and children list length
1077
14.7k
  unsigned backgroundPageID = readU32(input);
1078
14.7k
  m_collector->collectPage(m_header.id, m_header.level, backgroundPageID, m_isBackgroundPage, m_currentPageName);
1079
14.7k
}
1080
1081
void libvisio::VSDParser::readGeometry(librevenge::RVNGInputStream *input)
1082
45.8k
{
1083
45.8k
  unsigned char geomFlags = readU8(input);
1084
45.8k
  bool noFill = (!!(geomFlags & 1));
1085
45.8k
  bool noLine = (!!(geomFlags & 2));
1086
45.8k
  bool noShow = (!!(geomFlags & 4));
1087
1088
45.8k
  if (m_currentGeometryList)
1089
35.1k
    m_currentGeometryList->addGeometry(m_header.id, m_header.level, noFill, noLine, noShow);
1090
45.8k
}
1091
1092
void libvisio::VSDParser::readMoveTo(librevenge::RVNGInputStream *input)
1093
202k
{
1094
202k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1095
202k
  double x = readDouble(input);
1096
202k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1097
202k
  double y = readDouble(input);
1098
1099
202k
  if (m_currentGeometryList)
1100
129k
    m_currentGeometryList->addMoveTo(m_header.id, m_header.level, x, y);
1101
202k
}
1102
1103
void libvisio::VSDParser::readLineTo(librevenge::RVNGInputStream *input)
1104
183k
{
1105
183k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1106
183k
  double x = readDouble(input);
1107
183k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1108
183k
  double y = readDouble(input);
1109
1110
183k
  if (m_currentGeometryList)
1111
116k
    m_currentGeometryList->addLineTo(m_header.id, m_header.level, x, y);
1112
183k
}
1113
1114
void libvisio::VSDParser::readArcTo(librevenge::RVNGInputStream *input)
1115
83.7k
{
1116
83.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1117
83.7k
  double x2 = readDouble(input);
1118
83.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1119
83.7k
  double y2 = readDouble(input);
1120
83.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1121
83.7k
  double bow = readDouble(input);
1122
1123
83.7k
  if (m_currentGeometryList)
1124
63.4k
    m_currentGeometryList->addArcTo(m_header.id, m_header.level, x2, y2, bow);
1125
83.7k
}
1126
1127
void libvisio::VSDParser::readXFormData(librevenge::RVNGInputStream *input)
1128
20.1k
{
1129
20.1k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1130
20.1k
  m_shape.m_xform.pinX = readDouble(input);
1131
20.1k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1132
20.1k
  m_shape.m_xform.pinY = readDouble(input);
1133
20.1k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1134
20.1k
  m_shape.m_xform.width = readDouble(input);
1135
20.1k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1136
20.1k
  m_shape.m_xform.height = readDouble(input);
1137
20.1k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1138
20.1k
  m_shape.m_xform.pinLocX = readDouble(input);
1139
20.1k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1140
20.1k
  m_shape.m_xform.pinLocY = readDouble(input);
1141
20.1k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1142
20.1k
  m_shape.m_xform.angle = readDouble(input);
1143
20.1k
  m_shape.m_xform.flipX = !!readU8(input);
1144
20.1k
  m_shape.m_xform.flipY = !!readU8(input);
1145
20.1k
}
1146
1147
void libvisio::VSDParser::readXForm1D(librevenge::RVNGInputStream *input)
1148
5.41k
{
1149
5.41k
  if (!m_shape.m_xform1d)
1150
4.43k
    m_shape.m_xform1d = std::make_unique<XForm1D>();
1151
5.41k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1152
5.41k
  m_shape.m_xform1d->beginX = readDouble(input);
1153
5.41k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1154
5.41k
  m_shape.m_xform1d->beginY = readDouble(input);
1155
5.41k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1156
5.41k
  m_shape.m_xform1d->endX = readDouble(input);
1157
5.41k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1158
5.41k
  m_shape.m_xform1d->endY = readDouble(input);
1159
5.41k
}
1160
1161
void libvisio::VSDParser::readTxtXForm(librevenge::RVNGInputStream *input)
1162
29.3k
{
1163
29.3k
  m_shape.m_txtxform = std::make_unique<XForm>();
1164
29.3k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1165
29.3k
  m_shape.m_txtxform->pinX = readDouble(input);
1166
29.3k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1167
29.3k
  m_shape.m_txtxform->pinY = readDouble(input);
1168
29.3k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1169
29.3k
  m_shape.m_txtxform->width = readDouble(input);
1170
29.3k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1171
29.3k
  m_shape.m_txtxform->height = readDouble(input);
1172
29.3k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1173
29.3k
  m_shape.m_txtxform->pinLocX = readDouble(input);
1174
29.3k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1175
29.3k
  m_shape.m_txtxform->pinLocY = readDouble(input);
1176
29.3k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1177
29.3k
  m_shape.m_txtxform->angle = readDouble(input);
1178
29.3k
}
1179
1180
void libvisio::VSDParser::readShapeId(librevenge::RVNGInputStream *input)
1181
282k
{
1182
282k
  if (!m_isShapeStarted)
1183
191k
    m_shapeList.addShapeId(m_header.id, getUInt(input));
1184
90.7k
  else
1185
90.7k
    m_shape.m_shapeList.addShapeId(m_header.id, getUInt(input));
1186
282k
}
1187
1188
void libvisio::VSDParser::readShapeList(librevenge::RVNGInputStream *input)
1189
21.7k
{
1190
  // We want the collectors to still get the level information
1191
21.7k
  m_collector->collectUnhandledChunk(m_header.id, m_header.level);
1192
1193
21.7k
  if (m_header.trailer)
1194
20.6k
  {
1195
20.6k
    uint32_t subHeaderLength = readU32(input);
1196
20.6k
    uint32_t childrenListLength = readU32(input);
1197
20.6k
    input->seek(subHeaderLength, librevenge::RVNG_SEEK_CUR);
1198
20.6k
    if (childrenListLength > getRemainingLength(input))
1199
7.17k
      childrenListLength = getRemainingLength(input);
1200
20.6k
    std::vector<unsigned> shapeOrder;
1201
20.6k
    shapeOrder.reserve(childrenListLength / sizeof(uint32_t));
1202
4.23M
    for (size_t i = 0; i < (childrenListLength / sizeof(uint32_t)); i++)
1203
4.21M
      shapeOrder.push_back(readU32(input));
1204
1205
20.6k
    if (!m_isShapeStarted)
1206
15.1k
      m_shapeList.setElementsOrder(shapeOrder);
1207
5.55k
    else
1208
5.55k
      m_shape.m_shapeList.setElementsOrder(shapeOrder);
1209
20.6k
  }
1210
21.7k
}
1211
1212
void libvisio::VSDParser::readForeignDataType(librevenge::RVNGInputStream *input)
1213
102k
{
1214
102k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1215
102k
  double imgOffsetX = readDouble(input);
1216
102k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1217
102k
  double imgOffsetY = readDouble(input);
1218
102k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1219
102k
  double imgWidth = readDouble(input);
1220
102k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1221
102k
  double imgHeight = readDouble(input);
1222
102k
  unsigned foreignType = readU16(input);
1223
102k
  unsigned foreignMapMode = readU16(input);
1224
102k
  if (foreignMapMode == 0x8)
1225
902
    foreignType = 0x4;
1226
102k
  input->seek(0x9, librevenge::RVNG_SEEK_CUR);
1227
102k
  unsigned foreignFormat = readU32(input);
1228
1229
102k
  if (!m_shape.m_foreign)
1230
49.0k
    m_shape.m_foreign = std::make_unique<ForeignData>();
1231
102k
  m_shape.m_foreign->typeId = m_header.id;
1232
102k
  m_shape.m_foreign->type = foreignType;
1233
102k
  m_shape.m_foreign->format = foreignFormat;
1234
102k
  m_shape.m_foreign->offsetX = imgOffsetX;
1235
102k
  m_shape.m_foreign->offsetY = imgOffsetY;
1236
102k
  m_shape.m_foreign->width = imgWidth;
1237
102k
  m_shape.m_foreign->height = imgHeight;
1238
102k
}
1239
1240
void libvisio::VSDParser::readPageProps(librevenge::RVNGInputStream *input)
1241
26.0k
{
1242
  // Skip bytes representing unit to *display* (value is always inches)
1243
26.0k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1244
26.0k
  const double pageWidth = std::max<double>(readDouble(input), 0);
1245
26.0k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1246
26.0k
  const double pageHeight = std::max<double>(readDouble(input), 0);
1247
26.0k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1248
26.0k
  m_shadowOffsetX = readDouble(input);
1249
26.0k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1250
26.0k
  m_shadowOffsetY = readDouble(input);
1251
26.0k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1252
26.0k
  const double pageScale = readDouble(input);
1253
1254
26.0k
  const unsigned char drawingScaleUnit = readU8(input);
1255
26.0k
  double drawingScale = readDouble(input);
1256
26.0k
  if (VSD_ALMOST_ZERO(drawingScale))
1257
20.0k
    drawingScale = 1;
1258
1259
26.0k
  const double scale = std::abs(pageScale / drawingScale);
1260
1261
26.0k
  if (m_isStencilStarted && m_currentStencil)
1262
1.40k
  {
1263
1.40k
    m_currentStencil->m_shadowOffsetX = m_shadowOffsetX;
1264
1.40k
    m_currentStencil->m_shadowOffsetY = m_shadowOffsetY;
1265
1.40k
  }
1266
26.0k
  m_collector->collectPageProps(m_header.id, m_header.level, pageWidth, pageHeight, m_shadowOffsetX, m_shadowOffsetY, scale, drawingScaleUnit, 0, 0);
1267
26.0k
}
1268
1269
void libvisio::VSDParser::readShape(librevenge::RVNGInputStream *input)
1270
74.9k
{
1271
74.9k
  m_currentGeomListCount = 0;
1272
74.9k
  m_isShapeStarted = true;
1273
74.9k
  m_shapeList.clear();
1274
74.9k
  if (m_header.id != MINUS_ONE)
1275
72.8k
    m_currentShapeID = m_header.id;
1276
74.9k
  m_currentShapeLevel = m_header.level;
1277
74.9k
  unsigned parent = 0;
1278
74.9k
  auto masterPage = MINUS_ONE;
1279
74.9k
  auto masterShape = MINUS_ONE;
1280
74.9k
  auto lineStyle = MINUS_ONE;
1281
74.9k
  auto fillStyle = MINUS_ONE;
1282
74.9k
  auto textStyle = MINUS_ONE;
1283
1284
74.9k
  try
1285
74.9k
  {
1286
74.9k
    input->seek(10, librevenge::RVNG_SEEK_CUR);
1287
74.9k
    parent = readU32(input);
1288
74.9k
    input->seek(4, librevenge::RVNG_SEEK_CUR);
1289
74.9k
    masterPage = readU32(input);
1290
74.9k
    input->seek(4, librevenge::RVNG_SEEK_CUR);
1291
74.9k
    masterShape = readU32(input);
1292
74.9k
    input->seek(0x4, librevenge::RVNG_SEEK_CUR);
1293
74.9k
    fillStyle = readU32(input);
1294
74.9k
    input->seek(4, librevenge::RVNG_SEEK_CUR);
1295
74.9k
    lineStyle = readU32(input);
1296
74.9k
    input->seek(4, librevenge::RVNG_SEEK_CUR);
1297
74.9k
    textStyle = readU32(input);
1298
74.9k
  }
1299
74.9k
  catch (const EndOfStreamException &)
1300
74.9k
  {
1301
2.39k
  }
1302
1303
74.9k
  m_shape.clear();
1304
74.9k
  m_currentGeometryList = nullptr;
1305
74.9k
  const VSDShape *tmpShape = m_stencils.getStencilShape(masterPage, masterShape);
1306
74.9k
  if (tmpShape)
1307
2.63k
  {
1308
2.63k
    if (tmpShape->m_foreign)
1309
238
      m_shape.m_foreign = std::make_unique<ForeignData>(*(tmpShape->m_foreign));
1310
2.63k
    m_shape.m_xform = tmpShape->m_xform;
1311
2.63k
    if (tmpShape->m_txtxform)
1312
1.53k
      m_shape.m_txtxform = std::make_unique<XForm>(*(tmpShape->m_txtxform));
1313
2.63k
    m_shape.m_tabSets = tmpShape->m_tabSets;
1314
2.63k
    m_shape.m_text = tmpShape->m_text;
1315
2.63k
    m_shape.m_textFormat = tmpShape->m_textFormat;
1316
2.63k
    m_shape.m_misc = tmpShape->m_misc;
1317
2.63k
  }
1318
1319
74.9k
  m_shape.m_lineStyleId = lineStyle;
1320
74.9k
  m_shape.m_fillStyleId = fillStyle;
1321
74.9k
  m_shape.m_textStyleId = textStyle;
1322
74.9k
  m_shape.m_parent = parent;
1323
74.9k
  m_shape.m_masterPage = masterPage;
1324
74.9k
  m_shape.m_masterShape = masterShape;
1325
74.9k
  m_shape.m_shapeId = m_currentShapeID;
1326
74.9k
  m_currentShapeID = MINUS_ONE;
1327
74.9k
}
1328
1329
void libvisio::VSDParser::readNURBSTo(librevenge::RVNGInputStream *input)
1330
68.6k
{
1331
68.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1332
68.6k
  double x = readDouble(input);
1333
68.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1334
68.6k
  double y = readDouble(input);
1335
68.6k
  double knot = readDouble(input); // Second last knot
1336
68.6k
  double weight = readDouble(input); // Last weight
1337
68.6k
  double knotPrev = readDouble(input); // First knot
1338
68.6k
  double weightPrev = readDouble(input); // First weight
1339
1340
  // Detect whether to use Shape Data block
1341
68.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1342
68.6k
  unsigned char useData = readU8(input);
1343
68.6k
  if (useData == 0x8a)
1344
3.81k
  {
1345
3.81k
    input->seek(3, librevenge::RVNG_SEEK_CUR);
1346
3.81k
    unsigned dataId = readU32(input);
1347
1348
3.81k
    if (m_currentGeometryList)
1349
3.16k
      m_currentGeometryList->addNURBSTo(m_header.id, m_header.level, x, y, knot, knotPrev, weight, weightPrev, dataId);
1350
3.81k
    return;
1351
3.81k
  }
1352
1353
64.8k
  std::vector<double> knotVector;
1354
64.8k
  knotVector.push_back(knotPrev);
1355
64.8k
  std::vector<std::pair<double, double> > controlPoints;
1356
64.8k
  std::vector<double> weights;
1357
64.8k
  weights.push_back(weightPrev);
1358
1359
64.8k
  input->seek(9, librevenge::RVNG_SEEK_CUR); // Seek to blocks at offset 0x50 (80)
1360
64.8k
  unsigned long chunkBytesRead = 0x50;
1361
1362
  // Find formula block referring to cell E (cell 6)
1363
64.8k
  unsigned cellRef = 0;
1364
64.8k
  unsigned length = 0;
1365
64.8k
  unsigned long inputPos = input->tell();
1366
1.51M
  while (cellRef != 6 && !input->isEnd() &&
1367
1.44M
         m_header.dataLength - chunkBytesRead > 4)
1368
1.44M
  {
1369
1.44M
    length = readU32(input);
1370
1.44M
    input->seek(1, librevenge::RVNG_SEEK_CUR);
1371
1.44M
    cellRef = readU8(input);
1372
1.44M
    if (cellRef < 6)
1373
44.7k
      input->seek(length - 6, librevenge::RVNG_SEEK_CUR);
1374
1.44M
    chunkBytesRead += input->tell() - inputPos;
1375
1.44M
    inputPos = input->tell();
1376
1.44M
  }
1377
1378
64.8k
  if (input->isEnd())
1379
39.1k
    return;
1380
1381
  // Only read formula if block is found
1382
25.6k
  if (cellRef == 6)
1383
17.6k
  {
1384
17.6k
    unsigned char xType = 1;
1385
17.6k
    unsigned char yType = 1;
1386
17.6k
    unsigned degree = 3;
1387
    // Indicates whether it's a "simple" NURBS block with a static format
1388
    // or a complex block where parameters each have a type
1389
17.6k
    unsigned char paramType = readU8(input);
1390
17.6k
    unsigned char valueType = 0;
1391
1392
17.6k
    double lastKnot = 0;
1393
17.6k
    unsigned repetitions = 0;
1394
1395
    // Read formula's static first four parameters
1396
17.6k
    if (paramType == 0x8a)
1397
646
    {
1398
646
      lastKnot = readDouble(input);
1399
646
      degree = readU16(input);
1400
646
      xType = readU8(input);
1401
646
      yType = readU8(input);
1402
646
      repetitions = readU32(input);
1403
646
    }
1404
17.0k
    else
1405
17.0k
    {
1406
17.0k
      valueType = paramType;
1407
17.0k
      if (valueType == 0x20)
1408
2.44k
        lastKnot = readDouble(input);
1409
14.5k
      else
1410
14.5k
        lastKnot = readU16(input);
1411
1412
17.0k
      input->seek(1, librevenge::RVNG_SEEK_CUR);
1413
17.0k
      degree = readU16(input);
1414
17.0k
      input->seek(1, librevenge::RVNG_SEEK_CUR);
1415
17.0k
      xType = readU16(input);
1416
17.0k
      input->seek(1, librevenge::RVNG_SEEK_CUR);
1417
17.0k
      yType = readU16(input);
1418
17.0k
    }
1419
1420
    // Read sequences of (x, y, knot, weight) until finished
1421
17.6k
    unsigned long bytesRead = input->tell() - inputPos;
1422
17.6k
    unsigned char flag = 0;
1423
17.6k
    if (paramType != 0x8a) flag = readU8(input);
1424
1.92M
    while ((paramType == 0x8a ? repetitions > 0 : flag != 0x81) && bytesRead < length)
1425
1.90M
    {
1426
1.90M
      inputPos = input->tell();
1427
1.90M
      double knot_ = 0;
1428
1.90M
      double weight_ = 0;
1429
1.90M
      double controlX = 0;
1430
1.90M
      double controlY = 0;
1431
1432
1.90M
      if (paramType == 0x8a) // Parameters have static format
1433
22.3k
      {
1434
22.3k
        controlX = readDouble(input);
1435
22.3k
        controlY = readDouble(input);
1436
22.3k
        knot_ = readDouble(input);
1437
22.3k
        weight_ = readDouble(input);
1438
22.3k
      }
1439
1.88M
      else // Parameters have types
1440
1.88M
      {
1441
1.88M
        valueType = flag;
1442
1.88M
        if (valueType == 0x20)
1443
18.9k
          controlX = readDouble(input);
1444
1.86M
        else
1445
1.86M
          controlX = readU16(input);
1446
1447
1.88M
        valueType = readU8(input);
1448
1.88M
        if (valueType == 0x20)
1449
21.5k
          controlY = readDouble(input);
1450
1.86M
        else
1451
1.86M
          controlY = readU16(input);
1452
1453
1.88M
        valueType = readU8(input);
1454
1.88M
        if (valueType == 0x20)
1455
22.3k
          knot_ = readDouble(input);
1456
1.86M
        else if (valueType == 0x62)
1457
2.59k
          knot_ = readU16(input);
1458
1459
1.88M
        valueType = readU8(input);
1460
1.88M
        if (valueType == 0x20)
1461
18.8k
          weight_ = readDouble(input);
1462
1.86M
        else if (valueType == 0x62)
1463
2.05k
          weight_ = readU16(input);
1464
1.88M
      }
1465
1.90M
      controlPoints.push_back(std::pair<double, double>(controlX, controlY));
1466
1.90M
      knotVector.push_back(knot_);
1467
1.90M
      weights.push_back(weight_);
1468
1469
1.90M
      if (paramType != 0x8a) flag = readU8(input);
1470
23.5k
      else repetitions--;
1471
1.90M
      bytesRead += input->tell() - inputPos;
1472
1.90M
    }
1473
17.6k
    knotVector.push_back(knot);
1474
17.6k
    knotVector.push_back(lastKnot);
1475
17.6k
    weights.push_back(weight);
1476
1477
17.6k
    if (m_currentGeometryList)
1478
11.8k
      m_currentGeometryList->addNURBSTo(m_header.id, m_header.level, x, y, xType,
1479
11.8k
                                        yType, degree, controlPoints, knotVector, weights);
1480
17.6k
  }
1481
8.00k
  else // No formula found, use line
1482
8.00k
  {
1483
8.00k
    if (m_currentGeometryList)
1484
854
      m_currentGeometryList->addLineTo(m_header.id, m_header.level, x,  y);
1485
8.00k
  }
1486
25.6k
}
1487
1488
void libvisio::VSDParser::readPolylineTo(librevenge::RVNGInputStream *input)
1489
59.7k
{
1490
59.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1491
59.7k
  double x = readDouble(input);
1492
59.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1493
59.7k
  double y = readDouble(input);
1494
1495
  // Detect whether to use Shape Data block
1496
59.7k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1497
59.7k
  unsigned useData = readU8(input);
1498
59.7k
  if (useData == 0x8b)
1499
3.46k
  {
1500
3.46k
    input->seek(3, librevenge::RVNG_SEEK_CUR);
1501
3.46k
    unsigned dataId = readU32(input);
1502
1503
3.46k
    if (m_currentGeometryList)
1504
2.72k
      m_currentGeometryList->addPolylineTo(m_header.id, m_header.level, x, y, dataId);
1505
3.46k
    return;
1506
3.46k
  }
1507
1508
  // Blocks start at 0x30
1509
56.2k
  input->seek(0x9, librevenge::RVNG_SEEK_CUR);
1510
56.2k
  unsigned long chunkBytesRead = 0x30;
1511
1512
  // Find formula block referring to cell A (cell 2)
1513
56.2k
  unsigned cellRef = 0;
1514
56.2k
  unsigned length = 0;
1515
56.2k
  unsigned long inputPos = input->tell();
1516
738k
  while (cellRef != 2 && !input->isEnd() &&
1517
704k
         m_header.dataLength - chunkBytesRead > 4)
1518
704k
  {
1519
704k
    length = readU32(input);
1520
704k
    if (!length)
1521
21.7k
      break;
1522
682k
    input->seek(1, librevenge::RVNG_SEEK_CUR);
1523
682k
    cellRef = readU8(input);
1524
682k
    if (cellRef < 2)
1525
11.3k
      input->seek(length - 6, librevenge::RVNG_SEEK_CUR);
1526
682k
    chunkBytesRead += input->tell() - inputPos;
1527
682k
    inputPos = input->tell();
1528
682k
  }
1529
1530
56.2k
  if (input->isEnd())
1531
6.42k
    return;
1532
1533
  // Default to local co-ordinates if unspecified
1534
49.8k
  std::vector<std::pair<double, double> > points;
1535
1536
  // Only formula if block is found
1537
49.8k
  if (cellRef == 2)
1538
4.93k
  {
1539
4.93k
    unsigned long blockBytesRead = 0;
1540
4.93k
    inputPos = input->tell();
1541
4.93k
    blockBytesRead += 6;
1542
1543
    // Parse static first two parameters to function
1544
4.93k
    input->seek(1, librevenge::RVNG_SEEK_CUR);
1545
4.93k
    unsigned char xType = readU16(input);
1546
4.93k
    input->seek(1, librevenge::RVNG_SEEK_CUR);
1547
4.93k
    unsigned char yType = readU16(input);
1548
1549
    // Parse pairs of x,y co-ordinates
1550
4.93k
    unsigned flag = readU8(input);
1551
4.93k
    unsigned valueType = 0; // Holds parameter type indicator
1552
4.93k
    blockBytesRead += input->tell() - inputPos;
1553
933k
    while (flag != 0x81 && blockBytesRead < length)
1554
928k
    {
1555
928k
      inputPos = input->tell();
1556
928k
      double x2 = 0;
1557
928k
      double y2 = 0;
1558
1559
928k
      valueType = flag;
1560
928k
      if (valueType == 0x20)
1561
9.37k
        x2 = readDouble(input);
1562
919k
      else
1563
919k
        x2 = readU16(input);
1564
1565
928k
      valueType = readU8(input);
1566
928k
      if (valueType == 0x20)
1567
10.3k
        y2 = readDouble(input);
1568
918k
      else
1569
918k
        y2 = readU16(input);
1570
1571
928k
      points.push_back(std::pair<double, double>(x2, y2));
1572
928k
      flag = readU8(input);
1573
928k
      blockBytesRead += input->tell() - inputPos;
1574
928k
    }
1575
1576
4.93k
    if (m_currentGeometryList)
1577
2.33k
      m_currentGeometryList->addPolylineTo(m_header.id, m_header.level, x, y, xType,
1578
2.33k
                                           yType, points);
1579
4.93k
  }
1580
44.8k
  else
1581
44.8k
  {
1582
44.8k
    if (m_currentGeometryList)
1583
16.7k
      m_currentGeometryList->addLineTo(m_header.id, m_header.level, x, y);
1584
44.8k
  }
1585
49.8k
}
1586
1587
void libvisio::VSDParser::readInfiniteLine(librevenge::RVNGInputStream *input)
1588
100k
{
1589
100k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1590
100k
  double x1 = readDouble(input);
1591
100k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1592
100k
  double y1 = readDouble(input);
1593
100k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1594
100k
  double x2 = readDouble(input);
1595
100k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1596
100k
  double y2 = readDouble(input);
1597
100k
  if (m_currentGeometryList)
1598
61.6k
    m_currentGeometryList->addInfiniteLine(m_header.id, m_header.level, x1, y1, x2, y2);
1599
100k
}
1600
1601
void libvisio::VSDParser::readShapeData(librevenge::RVNGInputStream *input)
1602
242k
{
1603
242k
  unsigned char dataType = readU8(input);
1604
1605
242k
  input->seek(15, librevenge::RVNG_SEEK_CUR);
1606
  // Polyline data
1607
242k
  if (dataType == 0x80)
1608
9.14k
  {
1609
9.14k
    std::vector<std::pair<double, double> > points;
1610
9.14k
    unsigned char xType = readU8(input);
1611
9.14k
    unsigned char yType = readU8(input);
1612
9.14k
    unsigned pointCount = readU32(input);
1613
9.14k
    if (pointCount > getRemainingLength(input) / 16)
1614
5.94k
      pointCount = getRemainingLength(input) / 16;
1615
1616
11.7M
    for (unsigned i = 0; i < pointCount; i++)
1617
11.7M
    {
1618
11.7M
      double x = readDouble(input);
1619
11.7M
      double y = readDouble(input);
1620
11.7M
      points.push_back(std::pair<double, double>(x, y));
1621
11.7M
    }
1622
1623
9.14k
    PolylineData data;
1624
9.14k
    data.xType = xType;
1625
9.14k
    data.yType = yType;
1626
9.14k
    data.points = points;
1627
9.14k
    m_shape.m_polylineData[m_header.id] = data;
1628
9.14k
  }
1629
1630
  // NURBS data
1631
233k
  else if (dataType == 0x82)
1632
34.6k
  {
1633
34.6k
    double lastKnot = readDouble(input);
1634
1635
34.6k
    unsigned degree = readU16(input);
1636
34.6k
    unsigned char xType = readU8(input);
1637
34.6k
    unsigned char yType = readU8(input);
1638
34.6k
    unsigned pointCount = readU32(input);
1639
34.6k
    if (pointCount > getRemainingLength(input) / 32)
1640
7.44k
      pointCount = getRemainingLength(input) / 32;
1641
1642
34.6k
    std::vector<double> knotVector;
1643
34.6k
    std::vector<std::pair<double, double> > controlPoints;
1644
34.6k
    std::vector<double> weights;
1645
1646
14.0M
    for (unsigned i = 0; i < pointCount; i++)
1647
14.0M
    {
1648
14.0M
      double controlX = readDouble(input);
1649
14.0M
      double controlY = readDouble(input);
1650
14.0M
      double knot = readDouble(input);
1651
14.0M
      double weight = readDouble(input);
1652
1653
14.0M
      knotVector.push_back(knot);
1654
14.0M
      weights.push_back(weight);
1655
14.0M
      controlPoints.push_back(std::pair<double, double>(controlX, controlY));
1656
14.0M
    }
1657
1658
34.6k
    NURBSData data;
1659
34.6k
    data.lastKnot = lastKnot;
1660
34.6k
    data.degree = degree;
1661
34.6k
    data.xType = xType;
1662
34.6k
    data.yType = yType;
1663
34.6k
    data.knots = knotVector;
1664
34.6k
    data.weights = weights;
1665
34.6k
    data.points = controlPoints;
1666
34.6k
    m_shape.m_nurbsData[m_header.id] = data;
1667
34.6k
  }
1668
242k
}
1669
1670
void libvisio::VSDParser::readSplineStart(librevenge::RVNGInputStream *input)
1671
41.6k
{
1672
41.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1673
41.6k
  double x = readDouble(input);
1674
41.6k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1675
41.6k
  double y = readDouble(input);
1676
41.6k
  double secondKnot = readDouble(input);
1677
41.6k
  double firstKnot = readDouble(input);
1678
41.6k
  double lastKnot = readDouble(input);
1679
41.6k
  unsigned degree = readU8(input);
1680
1681
41.6k
  if (m_currentGeometryList)
1682
30.7k
    m_currentGeometryList->addSplineStart(m_header.id, m_header.level, x, y, secondKnot, firstKnot, lastKnot, degree);
1683
41.6k
}
1684
1685
void libvisio::VSDParser::readSplineKnot(librevenge::RVNGInputStream *input)
1686
56.4k
{
1687
56.4k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1688
56.4k
  double x = readDouble(input);
1689
56.4k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1690
56.4k
  double y = readDouble(input);
1691
56.4k
  double knot = readDouble(input);
1692
1693
56.4k
  if (m_currentGeometryList)
1694
37.2k
    m_currentGeometryList->addSplineKnot(m_header.id, m_header.level, x, y, knot);
1695
56.4k
}
1696
1697
void libvisio::VSDParser::readNameList(librevenge::RVNGInputStream * /* input */)
1698
29.4k
{
1699
29.4k
  m_shape.m_names.clear();
1700
29.4k
}
1701
1702
void libvisio::VSDParser::readNameList2(librevenge::RVNGInputStream * /* input */)
1703
4.06k
{
1704
4.06k
  m_names.clear();
1705
4.06k
}
1706
1707
void libvisio::VSDParser::readFieldList(librevenge::RVNGInputStream *input)
1708
12.9k
{
1709
12.9k
  if (m_header.trailer)
1710
12.2k
  {
1711
12.2k
    uint32_t subHeaderLength = readU32(input);
1712
12.2k
    uint32_t childrenListLength = readU32(input);
1713
12.2k
    input->seek(subHeaderLength, librevenge::RVNG_SEEK_CUR);
1714
12.2k
    if (childrenListLength > getRemainingLength(input))
1715
4.69k
      childrenListLength = getRemainingLength(input);
1716
12.2k
    std::vector<unsigned> fieldOrder;
1717
12.2k
    fieldOrder.reserve(childrenListLength / sizeof(uint32_t));
1718
1.71M
    for (size_t i = 0; i < (childrenListLength / sizeof(uint32_t)); i++)
1719
1.70M
      fieldOrder.push_back(readU32(input));
1720
1721
12.2k
    m_shape.m_fields.setElementsOrder(fieldOrder);
1722
12.2k
    m_shape.m_fields.addFieldList(m_header.id, m_header.level);
1723
12.2k
  }
1724
12.9k
}
1725
1726
void libvisio::VSDParser::readColours(librevenge::RVNGInputStream *input)
1727
9.12k
{
1728
9.12k
  input->seek(2, librevenge::RVNG_SEEK_CUR);
1729
9.12k
  unsigned numColours = readU8(input);
1730
9.12k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1731
9.12k
  m_colours.clear();
1732
1733
177k
  for (unsigned i = 0; i < numColours; i++)
1734
168k
  {
1735
168k
    Colour tmpColour;
1736
168k
    tmpColour.r = readU8(input);
1737
168k
    tmpColour.g = readU8(input);
1738
168k
    tmpColour.b = readU8(input);
1739
168k
    tmpColour.a = readU8(input);
1740
1741
168k
    m_colours.push_back(tmpColour);
1742
168k
  }
1743
9.12k
}
1744
1745
void libvisio::VSDParser::readFont(librevenge::RVNGInputStream *input)
1746
51.1k
{
1747
51.1k
  input->seek(4, librevenge::RVNG_SEEK_CUR);
1748
51.1k
  librevenge::RVNGBinaryData textStream;
1749
1750
270k
  for (unsigned i = 0; i < 32; i++)
1751
250k
  {
1752
250k
    unsigned char curchar = readU8(input);
1753
250k
    unsigned char nextchar = readU8(input);
1754
250k
    if (curchar == 0 && nextchar == 0)
1755
30.9k
      break;
1756
219k
    textStream.append(curchar);
1757
219k
    textStream.append(nextchar);
1758
219k
  }
1759
51.1k
  m_fonts[m_header.id] = VSDName(textStream, libvisio::VSD_TEXT_UTF16);
1760
51.1k
}
1761
1762
void libvisio::VSDParser::readFontIX(librevenge::RVNGInputStream *input)
1763
63.7k
{
1764
63.7k
  long tmpAdjust = input->tell();
1765
63.7k
  input->seek(2, librevenge::RVNG_SEEK_CUR);
1766
63.7k
  auto codePage = (unsigned char)(getUInt(input) & 0xff);
1767
63.7k
  tmpAdjust -= input->tell();
1768
1769
63.7k
  std::string fontName;
1770
1771
8.30M
  for (long i = 0; i < (long)(m_header.dataLength + tmpAdjust); i++)
1772
8.25M
  {
1773
8.25M
    auto curchar = (char)readU8(input);
1774
8.25M
    if (curchar == 0)
1775
22.9k
      break;
1776
8.23M
    fontName.append(1, curchar);
1777
8.23M
  }
1778
1779
63.7k
  if (!codePage)
1780
23.1k
  {
1781
    // Try to parse the font name for codePage
1782
23.1k
    size_t length = fontName.length();
1783
23.1k
    size_t found = std::string::npos;
1784
1785
23.1k
    if (length > 3 && (found=fontName.find(" CE", length - 3)) != std::string::npos)
1786
163
      codePage = 0xee;
1787
22.9k
    else if (length > 9 && (found=fontName.rfind(" Cyrillic", length - 9)) != std::string::npos)
1788
181
      codePage = 0xcc;
1789
22.8k
    else if (length > 4 && (found=fontName.rfind(" Cyr", length - 4)) != std::string::npos)
1790
405
      codePage = 0xcc;
1791
22.3k
    else if (length > 4 && (found=fontName.rfind(" CYR", length - 4)) != std::string::npos)
1792
6
      codePage = 0xcc;
1793
22.3k
    else if (length > 7 && (found=fontName.rfind(" Baltic", length - 7)) != std::string::npos)
1794
210
      codePage = 0xba;
1795
22.1k
    else if (length > 6 && (found=fontName.rfind(" Greek", length - 6)) != std::string::npos)
1796
346
      codePage = 0xa1;
1797
21.8k
    else if (length > 4 && (found=fontName.rfind(" Tur", length - 4)) != std::string::npos)
1798
187
      codePage = 0xa2;
1799
21.6k
    else if (length > 4 && (found=fontName.rfind(" TUR", length - 4)) != std::string::npos)
1800
56
      codePage = 0xa2;
1801
21.5k
    else if (length > 7 && (found=fontName.rfind(" Hebrew", length - 7)) != std::string::npos)
1802
0
      codePage = 0xb1;
1803
21.5k
    else if (length > 7 && (found=fontName.rfind(" Arabic", length - 7)) != std::string::npos)
1804
0
      codePage = 0xb2;
1805
21.5k
    else if (length > 5 && (found=fontName.rfind(" Thai", length - 5)) != std::string::npos)
1806
0
      codePage = 0xde;
1807
21.5k
    else if (length >= 4 && (found=fontName.find("GOST", 0, 4)) != std::string::npos)
1808
263
    {
1809
263
      codePage = 0xcc;
1810
263
      found = std::string::npos;
1811
263
    }
1812
1813
23.1k
    if (found != std::string::npos)
1814
1.55k
    {
1815
1.55k
      fontName.erase(found, std::string::npos);
1816
1.55k
    }
1817
23.1k
  }
1818
1819
63.7k
  TextFormat format = libvisio::VSD_TEXT_ANSI;
1820
63.7k
  switch (codePage)
1821
63.7k
  {
1822
21.3k
  case 0: // ANSI
1823
21.3k
    format = libvisio::VSD_TEXT_ANSI;
1824
21.3k
    break;
1825
1.51k
  case 0x02:
1826
1.51k
    format = libvisio::VSD_TEXT_SYMBOL;
1827
1.51k
    break;
1828
1.00k
  case 0xa1: // GREEK
1829
1.00k
    format = libvisio::VSD_TEXT_GREEK;
1830
1.00k
    break;
1831
1.07k
  case 0xa2: // TURKISH
1832
1.07k
    format = libvisio::VSD_TEXT_TURKISH;
1833
1.07k
    break;
1834
794
  case 0xa3: // VIETNAMESE
1835
794
    format = libvisio::VSD_TEXT_VIETNAMESE;
1836
794
    break;
1837
883
  case 0xb1: // HEBREW
1838
883
    format = libvisio::VSD_TEXT_HEBREW;
1839
883
    break;
1840
810
  case 0xb2: // ARABIC
1841
810
    format = libvisio::VSD_TEXT_ARABIC;
1842
810
    break;
1843
923
  case 0xba: // BALTIC
1844
923
    format = libvisio::VSD_TEXT_BALTIC;
1845
923
    break;
1846
1.58k
  case 0xcc: // RUSSIAN
1847
1.58k
    format = libvisio::VSD_TEXT_RUSSIAN;
1848
1.58k
    break;
1849
831
  case 0xde: // THAI
1850
831
    format = libvisio::VSD_TEXT_THAI;
1851
831
    break;
1852
815
  case 0xee: // CENTRAL EUROPE
1853
815
    format = libvisio::VSD_TEXT_CENTRAL_EUROPE;
1854
815
    break;
1855
3.00k
  case 0x80: // SHIFTJIS
1856
3.00k
    format = libvisio::VSD_TEXT_JAPANESE;
1857
3.00k
    break;
1858
1.02k
  case 0x81: // HANGUL
1859
1.02k
    format = libvisio::VSD_TEXT_KOREAN;
1860
1.02k
    break;
1861
874
  case 0x86: // GB2312
1862
874
    format = libvisio::VSD_TEXT_CHINESE_SIMPLIFIED;
1863
874
    break;
1864
2.24k
  case 0x88: // CHINESEBIG5
1865
2.24k
    format = libvisio::VSD_TEXT_CHINESE_TRADITIONAL;
1866
2.24k
    break;
1867
6.58k
  default:
1868
6.58k
    break;
1869
63.7k
  }
1870
1871
45.2k
  librevenge::RVNGBinaryData textStream((const unsigned char *)fontName.c_str(), fontName.length());
1872
45.2k
  m_fonts[m_header.id] = VSDName(textStream, format);
1873
45.2k
}
1874
1875
/* StyleSheet readers */
1876
1877
void libvisio::VSDParser::readStyleSheet(librevenge::RVNGInputStream *input)
1878
7.84k
{
1879
7.84k
  input->seek(0x22, librevenge::RVNG_SEEK_CUR);
1880
7.84k
  unsigned lineStyle = readU32(input);
1881
7.84k
  input->seek(4, librevenge::RVNG_SEEK_CUR);
1882
7.84k
  unsigned fillStyle = readU32(input);
1883
7.84k
  input->seek(4, librevenge::RVNG_SEEK_CUR);
1884
7.84k
  unsigned textStyle = readU32(input);
1885
1886
7.84k
  m_collector->collectStyleSheet(m_header.id, m_header.level, lineStyle, fillStyle, textStyle);
1887
7.84k
}
1888
1889
void libvisio::VSDParser::readPageSheet(librevenge::RVNGInputStream * /* input */)
1890
20.6k
{
1891
20.6k
  m_currentShapeLevel = m_header.level;
1892
20.6k
  m_collector->collectPageSheet(m_header.id, m_header.level);
1893
20.6k
}
1894
1895
void libvisio::VSDParser::readText(librevenge::RVNGInputStream *input)
1896
23.9k
{
1897
23.9k
  input->seek(8, librevenge::RVNG_SEEK_CUR);
1898
23.9k
  librevenge::RVNGBinaryData textStream;
1899
1900
  // Read up to end of chunk in byte pairs (except from last 2 bytes)
1901
23.9k
  unsigned long numBytesRead = 0;
1902
23.9k
  const unsigned char *tmpBuffer = input->read(m_header.dataLength - 8, numBytesRead);
1903
23.9k
  if (numBytesRead)
1904
22.9k
  {
1905
22.9k
    if (m_isStencilStarted)
1906
407
    {
1907
407
      VSD_DEBUG_MSG(("Found stencil text\n"));
1908
407
    }
1909
22.9k
    textStream.append(tmpBuffer, numBytesRead);
1910
22.9k
    m_shape.m_text = textStream;
1911
22.9k
  }
1912
988
  else
1913
988
    m_shape.m_text.clear();
1914
23.9k
  m_shape.m_textFormat = libvisio::VSD_TEXT_UTF16;
1915
23.9k
}
1916
1917
void libvisio::VSDParser::readCharIX(librevenge::RVNGInputStream *input)
1918
31.5k
{
1919
31.5k
  VSDFont fontFace;
1920
31.5k
  unsigned charCount = readU32(input);
1921
31.5k
  unsigned fontID = readU16(input);
1922
31.5k
  VSDName font;
1923
31.5k
  std::map<unsigned, VSDName>::const_iterator iter = m_fonts.find(fontID);
1924
31.5k
  if (iter != m_fonts.end())
1925
8.07k
    font = iter->second;
1926
31.5k
  input->seek(1, librevenge::RVNG_SEEK_CUR);  // Color ID
1927
31.5k
  Colour fontColour;            // Font Colour
1928
31.5k
  fontColour.r = readU8(input);
1929
31.5k
  fontColour.g = readU8(input);
1930
31.5k
  fontColour.b = readU8(input);
1931
31.5k
  fontColour.a = readU8(input);
1932
1933
31.5k
  bool bold(false);
1934
31.5k
  bool italic(false);
1935
31.5k
  bool underline(false);
1936
31.5k
  bool doubleunderline(false);
1937
31.5k
  bool strikeout(false);
1938
31.5k
  bool doublestrikeout(false);
1939
31.5k
  bool allcaps(false);
1940
31.5k
  bool initcaps(false);
1941
31.5k
  bool smallcaps(false);
1942
31.5k
  bool superscript(false);
1943
31.5k
  bool subscript(false);
1944
31.5k
  unsigned char fontMod = readU8(input);
1945
31.5k
  if (fontMod & 1) bold = true;
1946
31.5k
  if (fontMod & 2) italic = true;
1947
31.5k
  if (fontMod & 4) underline = true;
1948
31.5k
  if (fontMod & 8) smallcaps = true;
1949
31.5k
  fontMod = readU8(input);
1950
31.5k
  if (fontMod & 1) allcaps = true;
1951
31.5k
  if (fontMod & 2) initcaps = true;
1952
31.5k
  fontMod = readU8(input);
1953
31.5k
  if (fontMod & 1) superscript = true;
1954
31.5k
  if (fontMod & 2) subscript = true;
1955
1956
31.5k
  double scaleWidth = (double)(readU16(input)) / 10000.0;
1957
31.5k
  input->seek(2, librevenge::RVNG_SEEK_CUR);
1958
31.5k
  double fontSize = readDouble(input);
1959
1960
31.5k
  fontMod = readU8(input);
1961
31.5k
  if (fontMod & 1) doubleunderline = true;
1962
31.5k
  if (fontMod & 4) strikeout = true;
1963
31.5k
  if (fontMod & 0x20) doublestrikeout = true;
1964
1965
31.5k
  if (m_isInStyles)
1966
2.74k
    m_collector->collectCharIXStyle(m_header.id, m_header.level, charCount, font, fontColour, fontSize,
1967
2.74k
                                    bold, italic, underline, doubleunderline, strikeout, doublestrikeout,
1968
2.74k
                                    allcaps, initcaps, smallcaps, superscript, subscript, scaleWidth);
1969
28.7k
  else
1970
28.7k
  {
1971
28.7k
    if (m_isStencilStarted)
1972
759
    {
1973
759
      VSD_DEBUG_MSG(("Found stencil character style\n"));
1974
759
    }
1975
1976
28.7k
    m_shape.m_charStyle.override(VSDOptionalCharStyle(charCount, font, fontColour, fontSize,
1977
28.7k
                                                      bold, italic, underline, doubleunderline, strikeout, doublestrikeout,
1978
28.7k
                                                      allcaps, initcaps, smallcaps, superscript, subscript, scaleWidth));
1979
28.7k
    m_shape.m_charList.addCharIX(m_header.id, m_header.level, charCount, font, fontColour, fontSize,
1980
28.7k
                                 bold, italic, underline, doubleunderline, strikeout, doublestrikeout,
1981
28.7k
                                 allcaps, initcaps, smallcaps, superscript, subscript, scaleWidth);
1982
28.7k
  }
1983
31.5k
}
1984
1985
void libvisio::VSDParser::readParaIX(librevenge::RVNGInputStream *input)
1986
30.2k
{
1987
30.2k
  long startPosition = input->tell();
1988
30.2k
  unsigned charCount = readU32(input);
1989
30.2k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1990
30.2k
  double indFirst = readDouble(input);
1991
30.2k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1992
30.2k
  double indLeft = readDouble(input);
1993
30.2k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1994
30.2k
  double indRight = readDouble(input);
1995
30.2k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1996
30.2k
  double spLine = readDouble(input);
1997
30.2k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
1998
30.2k
  double spBefore = readDouble(input);
1999
30.2k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
2000
30.2k
  double spAfter = readDouble(input);
2001
30.2k
  unsigned char align = readU8(input);
2002
30.2k
  unsigned char bullet = readU8(input);
2003
30.2k
  input->seek(4, librevenge::RVNG_SEEK_CUR);
2004
30.2k
  unsigned fontID = readU16(input);
2005
30.2k
  VSDName bulletFont;
2006
30.2k
  if (fontID)
2007
10.6k
  {
2008
10.6k
    std::map<unsigned, VSDName>::const_iterator iter = m_fonts.find(fontID);
2009
10.6k
    if (iter != m_fonts.end())
2010
589
      bulletFont = iter->second;
2011
10.6k
  }
2012
30.2k
  input->seek(2, librevenge::RVNG_SEEK_CUR);
2013
30.2k
  double bulletFontSize = readDouble(input);
2014
30.2k
  input->seek(1, librevenge::RVNG_SEEK_CUR);
2015
30.2k
  double textPosAfterBullet = readDouble(input);
2016
30.2k
  unsigned flags = readU32(input);
2017
30.2k
  input->seek(34, librevenge::RVNG_SEEK_CUR);
2018
30.2k
  long remainingData = m_header.dataLength - input->tell() + startPosition;
2019
30.2k
  unsigned blockLength = 0;
2020
30.2k
  VSDName bulletStr;
2021
2022
39.5k
  while (remainingData >= 4 && (blockLength = readU32(input)))
2023
9.32k
  {
2024
9.32k
    long blockEnd = blockLength-4 + input->tell();
2025
9.32k
    unsigned char blockType = readU8(input);
2026
9.32k
    unsigned char blockIdx = readU8(input);
2027
9.32k
    if (blockType == 2 && blockIdx == 8)
2028
742
    {
2029
742
      input->seek(1, librevenge::RVNG_SEEK_CUR);
2030
742
      unsigned long numBytes = 2*readU8(input);
2031
742
      unsigned long numBytesRead = 0;
2032
742
      const unsigned char *tmpBuffer = input->read(numBytes, numBytesRead);
2033
742
      if (tmpBuffer && numBytesRead)
2034
196
      {
2035
196
        librevenge::RVNGBinaryData tmpBulletString(tmpBuffer, numBytesRead);
2036
196
        bulletStr = VSDName(tmpBulletString, libvisio::VSD_TEXT_UTF16);
2037
196
      }
2038
742
    }
2039
8.58k
    else if (blockType == 2 && blockIdx == 3)
2040
450
    {
2041
450
    };
2042
9.32k
    input->seek(blockEnd, librevenge::RVNG_SEEK_SET);
2043
9.32k
    remainingData -= blockLength;
2044
9.32k
  }
2045
2046
2047
30.2k
  if (m_isInStyles)
2048
3.15k
    m_collector->collectParaIXStyle(m_header.id, m_header.level, charCount, indFirst, indLeft, indRight,
2049
3.15k
                                    spLine, spBefore, spAfter, align, bullet, bulletStr, bulletFont,
2050
3.15k
                                    bulletFontSize, textPosAfterBullet, flags);
2051
27.1k
  else
2052
27.1k
  {
2053
27.1k
    if (m_isStencilStarted)
2054
207
    {
2055
207
      VSD_DEBUG_MSG(("Found stencil paragraph style\n"));
2056
207
    }
2057
2058
27.1k
    m_shape.m_paraStyle.override(VSDOptionalParaStyle(charCount, indFirst, indLeft, indRight,
2059
27.1k
                                                      spLine, spBefore, spAfter, align, bullet,
2060
27.1k
                                                      bulletStr, bulletFont, bulletFontSize,
2061
27.1k
                                                      textPosAfterBullet, flags));
2062
27.1k
    m_shape.m_paraList.addParaIX(m_header.id, m_header.level, charCount, indFirst,
2063
27.1k
                                 indLeft, indRight, spLine, spBefore, spAfter, align,
2064
27.1k
                                 bullet, bulletStr, bulletFont, bulletFontSize,
2065
27.1k
                                 textPosAfterBullet, flags);
2066
27.1k
  }
2067
30.2k
}
2068
2069
2070
void libvisio::VSDParser::readFillAndShadow(librevenge::RVNGInputStream *input)
2071
44.8k
{
2072
44.8k
  unsigned char colourFGIndex = readU8(input);
2073
44.8k
  Colour colourFG;
2074
44.8k
  colourFG.r = readU8(input);
2075
44.8k
  colourFG.g = readU8(input);
2076
44.8k
  colourFG.b = readU8(input);
2077
44.8k
  colourFG.a = readU8(input);
2078
44.8k
  unsigned char colourBGIndex = readU8(input);
2079
44.8k
  Colour colourBG;
2080
44.8k
  colourBG.r = readU8(input);
2081
44.8k
  colourBG.g = readU8(input);
2082
44.8k
  colourBG.b = readU8(input);
2083
44.8k
  colourBG.a = readU8(input);
2084
44.8k
  if (!colourFG && !colourBG)
2085
17.3k
  {
2086
17.3k
    colourFG = _colourFromIndex(colourFGIndex);
2087
17.3k
    colourBG = _colourFromIndex(colourBGIndex);
2088
17.3k
  }
2089
44.8k
  double fillFGTransparency = (double)colourFG.a / 255.0;
2090
44.8k
  double fillBGTransparency = (double)colourBG.a / 255.0;
2091
2092
44.8k
  unsigned char fillPattern = readU8(input);
2093
2094
44.8k
  unsigned char shadowFGIndex = readU8(input);
2095
44.8k
  Colour shadowFG;
2096
44.8k
  shadowFG.r = readU8(input);
2097
44.8k
  shadowFG.g = readU8(input);
2098
44.8k
  shadowFG.b = readU8(input);
2099
44.8k
  shadowFG.a = readU8(input);
2100
44.8k
  unsigned char shadowBGIndex = readU8(input);
2101
44.8k
  Colour shadowBG;
2102
44.8k
  shadowBG.r = readU8(input);
2103
44.8k
  shadowBG.g = readU8(input);
2104
44.8k
  shadowBG.b = readU8(input);
2105
44.8k
  shadowBG.a = readU8(input);
2106
44.8k
  if (!shadowFG && !shadowBG)
2107
18.4k
  {
2108
18.4k
    shadowFG = _colourFromIndex(shadowFGIndex);
2109
18.4k
    shadowBG = _colourFromIndex(shadowBGIndex);
2110
18.4k
  }
2111
2112
44.8k
  unsigned char shadowPattern = readU8(input);
2113
2114
// only version 11 after that point
2115
44.8k
  input->seek(2, librevenge::RVNG_SEEK_CUR); // Shadow Type and Value format byte
2116
44.8k
  double shadowOffsetX = readDouble(input);
2117
44.8k
  input->seek(1, librevenge::RVNG_SEEK_CUR); // Value format byte
2118
44.8k
  double shadowOffsetY = readDouble(input);
2119
2120
2121
2122
44.8k
  if (m_isInStyles)
2123
8.53k
    m_collector->collectFillStyle(m_header.level, colourFG, colourBG, fillPattern,
2124
8.53k
                                  fillFGTransparency, fillBGTransparency, shadowPattern, shadowFG,
2125
8.53k
                                  shadowOffsetX, shadowOffsetY, -1, -1, -1);
2126
36.3k
  else
2127
36.3k
  {
2128
36.3k
    if (m_isStencilStarted)
2129
287
    {
2130
287
      VSD_DEBUG_MSG(("Found stencil fill\n"));
2131
287
    }
2132
36.3k
    m_shape.m_fillStyle.override(VSDOptionalFillStyle(colourFG, colourBG, fillPattern, fillFGTransparency,
2133
36.3k
                                                      fillBGTransparency, shadowFG, shadowPattern,
2134
36.3k
                                                      shadowOffsetX, shadowOffsetY, -1, -1, -1, 0, 0));
2135
36.3k
  }
2136
44.8k
}
2137
2138
void libvisio::VSDParser::readName(librevenge::RVNGInputStream *input)
2139
4.46k
{
2140
4.46k
  unsigned long numBytesRead = 0;
2141
4.46k
  const unsigned char *tmpBuffer = input->read(m_header.dataLength, numBytesRead);
2142
4.46k
  if (numBytesRead)
2143
1.64k
  {
2144
1.64k
    librevenge::RVNGBinaryData name(tmpBuffer, numBytesRead);
2145
1.64k
    m_shape.m_names[m_header.id] = VSDName(name, libvisio::VSD_TEXT_UTF16);
2146
1.64k
  }
2147
4.46k
}
2148
2149
void libvisio::VSDParser::readName2(librevenge::RVNGInputStream *input)
2150
12.8k
{
2151
12.8k
  unsigned short unicharacter = 0;
2152
12.8k
  librevenge::RVNGBinaryData name;
2153
12.8k
  input->seek(4, librevenge::RVNG_SEEK_CUR); // skip a dword that seems to be always 1
2154
87.1k
  while ((unicharacter = readU16(input)))
2155
74.3k
  {
2156
74.3k
    name.append(unicharacter & 0xff);
2157
74.3k
    name.append((unicharacter & 0xff00) >> 8);
2158
74.3k
  }
2159
12.8k
  name.append(unicharacter & 0xff);
2160
12.8k
  name.append((unicharacter & 0xff00) >> 8);
2161
12.8k
  m_names[m_header.id] = VSDName(name, libvisio::VSD_TEXT_UTF16);
2162
12.8k
}
2163
2164
void libvisio::VSDParser::readTextField(librevenge::RVNGInputStream *input)
2165
15.0k
{
2166
15.0k
  unsigned long initialPosition = input->tell();
2167
15.0k
  input->seek(7, librevenge::RVNG_SEEK_CUR);
2168
15.0k
  unsigned char cellType = readU8(input);
2169
15.0k
  if (cellType == CELL_TYPE_StringWithoutUnit)
2170
274
  {
2171
274
    int nameId = readS32(input);
2172
274
    input->seek(6, librevenge::RVNG_SEEK_CUR);
2173
274
    int formatStringId = readS32(input);
2174
274
    m_shape.m_fields.addTextField(m_header.id, m_header.level, nameId, formatStringId);
2175
274
  }
2176
14.8k
  else
2177
14.8k
  {
2178
14.8k
    double numericValue = readDouble(input);
2179
14.8k
    input->seek(2, librevenge::RVNG_SEEK_CUR);
2180
14.8k
    int formatStringId = readS32(input);
2181
2182
14.8k
    unsigned blockIdx = 0;
2183
14.8k
    unsigned length = 0;
2184
14.8k
    unsigned short formatNumber = 0;
2185
14.8k
    input->seek(initialPosition+0x36, librevenge::RVNG_SEEK_SET);
2186
16.6k
    while (blockIdx != 2 && !input->isEnd() && (unsigned long) input->tell() < (unsigned long)(initialPosition+m_header.dataLength+m_header.trailer))
2187
3.25k
    {
2188
3.25k
      unsigned long inputPos = input->tell();
2189
3.25k
      length = readU32(input);
2190
3.25k
      if (!length)
2191
1.45k
        break;
2192
1.80k
      input->seek(1, librevenge::RVNG_SEEK_CUR);
2193
1.80k
      blockIdx = readU8(input);
2194
1.80k
      if (blockIdx != 2)
2195
1.29k
        input->seek(inputPos + length, librevenge::RVNG_SEEK_SET);
2196
509
      else
2197
509
      {
2198
509
        input->seek(1, librevenge::RVNG_SEEK_CUR);
2199
509
        formatNumber = readU16(input);
2200
509
        if (0x80 != readU8(input))
2201
252
        {
2202
252
          input->seek(inputPos + length, librevenge::RVNG_SEEK_SET);
2203
252
          blockIdx = 0;
2204
252
        }
2205
257
        else
2206
257
        {
2207
257
          if (0xc2 != readU8(input))
2208
255
          {
2209
255
            input->seek(inputPos + length, librevenge::RVNG_SEEK_SET);
2210
255
            blockIdx = 0;
2211
255
          }
2212
2
          else
2213
2
            break;
2214
257
        }
2215
509
      }
2216
1.80k
    }
2217
2218
14.8k
    if (input->isEnd())
2219
1.79k
      return;
2220
2221
13.0k
    if (blockIdx != 2)
2222
12.4k
    {
2223
12.4k
      if (cellType == CELL_TYPE_Date)
2224
365
        formatNumber = VSD_FIELD_FORMAT_MsoDateShort;
2225
12.0k
      else
2226
12.0k
        formatNumber = VSD_FIELD_FORMAT_Unknown;
2227
12.4k
    }
2228
2229
13.0k
    m_shape.m_fields.addNumericField(m_header.id, m_header.level, formatNumber, cellType, numericValue, formatStringId);
2230
13.0k
  }
2231
15.0k
}
2232
2233
void libvisio::VSDParser::readMisc(librevenge::RVNGInputStream *input)
2234
10.7k
{
2235
10.7k
  unsigned long initialPosition = input->tell();
2236
10.7k
  unsigned char flags = readU8(input);
2237
10.7k
  if (flags & 0x20)
2238
830
    m_shape.m_misc.m_hideText = true;
2239
9.93k
  else
2240
9.93k
    m_shape.m_misc.m_hideText = false;
2241
2242
10.7k
  input->seek(initialPosition+45, librevenge::RVNG_SEEK_SET);
2243
29.6k
  while (!input->isEnd() && (unsigned long) input->tell() < (unsigned long)(initialPosition+m_header.dataLength+m_header.trailer))
2244
24.6k
  {
2245
24.6k
    unsigned long inputPos = input->tell();
2246
24.6k
    unsigned length = readU32(input);
2247
24.6k
    if (!length)
2248
5.75k
      break;
2249
18.8k
    unsigned blockType = readU8(input);
2250
18.8k
    input->seek(1, librevenge::RVNG_SEEK_CUR);
2251
18.8k
    if (blockType == 2)
2252
14.6k
    {
2253
14.6k
      if (0x74 == readU8(input))
2254
627
      {
2255
627
        if (0x6000004e == readU32(input))
2256
376
        {
2257
376
          unsigned shapeId = readU32(input);
2258
376
          if (0x7a == readU8(input))
2259
361
          {
2260
361
            if (0x40000073 == readU32(input))
2261
340
            {
2262
340
              if (!m_shape.m_xform1d)
2263
7
                m_shape.m_xform1d = std::make_unique<XForm1D>();
2264
340
              if (m_shape.m_xform1d->beginId == MINUS_ONE)
2265
173
                m_shape.m_xform1d->beginId = shapeId;
2266
167
              else if (m_shape.m_xform1d->endId == MINUS_ONE)
2267
167
                m_shape.m_xform1d->endId = shapeId;
2268
340
            }
2269
361
          }
2270
376
        }
2271
627
      }
2272
14.6k
    }
2273
18.8k
    input->seek(inputPos + length, librevenge::RVNG_SEEK_SET);
2274
18.8k
  }
2275
10.7k
}
2276
2277
libvisio::Colour libvisio::VSDParser::_colourFromIndex(unsigned idx)
2278
547k
{
2279
547k
  if (idx < m_colours.size())
2280
30.9k
    return m_colours[idx];
2281
516k
  return libvisio::Colour();
2282
547k
}
2283
2284
unsigned libvisio::VSDParser::getUInt(librevenge::RVNGInputStream *input)
2285
842k
{
2286
842k
  return readU32(input);
2287
842k
}
2288
2289
int libvisio::VSDParser::getInt(librevenge::RVNGInputStream *input)
2290
13.9k
{
2291
13.9k
  return readS32(input);
2292
13.9k
}
2293
2294
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */