Coverage Report

Created: 2026-04-29 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/Quattro.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* libwps
3
 * Version: MPL 2.0 / LGPLv2.1+
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
 * Major Contributor(s):
10
 * Copyright (C) 2006, 2007 Andrew Ziem
11
 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
12
 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 *
14
 * For minor contributions see the git repository.
15
 *
16
 * Alternatively, the contents of this file may be used under the terms
17
 * of the GNU Lesser General Public License Version 2.1 or later
18
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
19
 * applicable instead of those above.
20
 */
21
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include <algorithm>
26
#include <cmath>
27
#include <sstream>
28
#include <stack>
29
#include <utility>
30
31
#include <librevenge-stream/librevenge-stream.h>
32
33
#include "libwps_internal.h"
34
#include "libwps_tools_win.h"
35
36
#include "WKSContentListener.h"
37
#include "WKSSubDocument.h"
38
39
#include "WPSCell.h"
40
#include "WPSEntry.h"
41
#include "WPSFont.h"
42
#include "WPSHeader.h"
43
#include "WPSOLEParser.h"
44
#include "WPSOLEStream.h"
45
#include "WPSPageSpan.h"
46
#include "WPSStream.h"
47
#include "WPSStringStream.h"
48
49
//#include "QuattroChart.h"
50
#include "QuattroFormula.h"
51
#include "QuattroGraph.h"
52
#include "QuattroSpreadsheet.h"
53
54
#include "Quattro.h"
55
56
using namespace libwps;
57
58
//! Internal: namespace to define internal class of QuattroParser
59
namespace QuattroParserInternal
60
{
61
//! the font of a QuattroParser
62
struct Font final : public WPSFont
63
{
64
  //! constructor
65
207k
  explicit Font(libwps_tools_win::Font::Type type) : WPSFont(), m_type(type)
66
207k
  {
67
207k
  }
68
476k
  Font(Font const &)=default;
69
  //! destructor
70
  ~Font() final;
71
  //! font encoding type
72
  libwps_tools_win::Font::Type m_type;
73
};
74
75
Font::~Font()
76
684k
{
77
684k
}
78
79
//! Internal: the subdocument of a WPS4Parser
80
class SubDocument final : public WKSSubDocument
81
{
82
public:
83
  //! constructor for a text entry
84
  SubDocument(RVNGInputStreamPtr const &input, QuattroParser &pars, bool header) :
85
3.84k
    WKSSubDocument(input, &pars), m_header(header) {}
86
  //! destructor
87
0
  ~SubDocument() final {}
88
89
  //! operator==
90
  bool operator==(std::shared_ptr<WPSSubDocument> const &doc) const final
91
0
  {
92
0
    if (!doc || !WKSSubDocument::operator==(doc))
93
0
      return false;
94
0
    auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get());
95
0
    if (!sDoc) return false;
96
0
    return m_header == sDoc->m_header;
97
0
  }
98
99
  //! the parser function
100
  void parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType subDocumentType) final;
101
  //! a flag to known if we need to send the header or the footer
102
  bool m_header;
103
};
104
105
void SubDocument::parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType)
106
3.84k
{
107
3.84k
  if (!listener.get())
108
0
  {
109
0
    WPS_DEBUG_MSG(("QuattroParserInternal::SubDocument::parse: no listener\n"));
110
0
    return;
111
0
  }
112
3.84k
  if (!dynamic_cast<WKSContentListener *>(listener.get()))
113
0
  {
114
0
    WPS_DEBUG_MSG(("QuattroParserInternal::SubDocument::parse: bad listener\n"));
115
0
    return;
116
0
  }
117
118
3.84k
  QuattroParser *pser = m_parser ? dynamic_cast<QuattroParser *>(m_parser) : nullptr;
119
3.84k
  if (!pser)
120
0
  {
121
0
    listener->insertCharacter(' ');
122
0
    WPS_DEBUG_MSG(("QuattroParserInternal::SubDocument::parse: bad parser\n"));
123
0
    return;
124
0
  }
125
3.84k
  pser->sendHeaderFooter(m_header);
126
3.84k
}
127
128
//! a zone name
129
struct ZoneName
130
{
131
  //! constructor
132
  explicit ZoneName(char const *name, char const *extra=nullptr)
133
4.64M
    : m_name(name)
134
4.64M
    , m_extra(extra==nullptr ? "" : extra)
135
4.64M
  {
136
4.64M
  }
137
  //! the zone name
138
  std::string m_name;
139
  //! the extra data
140
  std::string m_extra;
141
};
142
143
//! the state of QuattroParser
144
struct State
145
{
146
  //! constructor
147
  explicit State(libwps_tools_win::Font::Type fontType, char const *password)
148
71.8k
    : m_fontType(fontType)
149
71.8k
    , m_version(-1)
150
71.8k
    , m_metaData()
151
71.8k
    , m_actualSheet(-1)
152
71.8k
    , m_fontsList()
153
71.8k
    , m_colorsList()
154
71.8k
    , m_idToExternalFileMap()
155
71.8k
    , m_idToExternalNameMap()
156
71.8k
    , m_idToFieldMap()
157
71.8k
    , m_pageSpan()
158
71.8k
    , m_actPage(0)
159
71.8k
    , m_numPages(0)
160
71.8k
    , m_headerString()
161
71.8k
    , m_footerString()
162
71.8k
    , m_password(password)
163
71.8k
    , m_isEncrypted(false)
164
71.8k
    , m_isDecoded(false)
165
71.8k
    , m_idToZoneNameMap()
166
71.8k
    , m_readingZone341(false)
167
71.8k
  {
168
71.8k
  }
169
  //! return the default font style
170
  libwps_tools_win::Font::Type getDefaultFontType() const
171
2.29M
  {
172
2.29M
    if (m_fontType != libwps_tools_win::Font::UNKNOWN)
173
0
      return m_fontType;
174
2.29M
    return libwps_tools_win::Font::WIN3_WEUROPE;
175
2.29M
  }
176
177
  //! returns a color corresponding to an id
178
  bool getColor(int id, WPSColor &color) const;
179
  //! returns a default font (Courier12) with file's version to define the default encoding */
180
  static WPSFont getDefaultFont()
181
3.84k
  {
182
3.84k
    WPSFont res;
183
3.84k
    res.m_name="Times New Roman";
184
3.84k
    res.m_size=12;
185
3.84k
    return res;
186
3.84k
  }
187
  //! init the zone name map
188
  void initZoneNameMap();
189
  //! the user font type
190
  libwps_tools_win::Font::Type m_fontType;
191
  //! the file version
192
  int m_version;
193
  //! the meta data
194
  librevenge::RVNGPropertyList m_metaData;
195
  //! the actual sheet
196
  int m_actualSheet;
197
  //! the font list
198
  std::vector<Font> m_fontsList;
199
  //! the color list
200
  std::vector<WPSColor> m_colorsList;
201
  //! map id to filename
202
  std::map<int, librevenge::RVNGString> m_idToExternalFileMap;
203
  //! map id to external name
204
  std::map<int, librevenge::RVNGString> m_idToExternalNameMap;
205
  //! map id to field
206
  std::map<int, std::pair<librevenge::RVNGString,QuattroFormulaInternal::CellReference> >m_idToFieldMap;
207
  //! the actual document size
208
  WPSPageSpan m_pageSpan;
209
  int m_actPage /** the actual page*/, m_numPages /* the number of pages */;
210
  //! the header string
211
  librevenge::RVNGString m_headerString;
212
  //! the footer string
213
  librevenge::RVNGString m_footerString;
214
  //! the password (if known)
215
  char const *m_password;
216
  //! true if the file is encrypted
217
  bool m_isEncrypted;
218
  //! true if the main stream has been decoded
219
  bool m_isDecoded;
220
  //! map zone id to zone name
221
  std::map<int, ZoneName> m_idToZoneNameMap;
222
  //! true if zone 341 is being read. Avoids recursion.
223
  bool m_readingZone341;
224
private:
225
  State(State const &)=delete;
226
  State &operator=(State const &)=delete;
227
};
228
229
bool State::getColor(int id, WPSColor &color) const
230
1.90M
{
231
1.90M
  if (m_colorsList.empty())
232
1.70k
  {
233
1.70k
    auto *THIS=const_cast<State *>(this);
234
1.70k
    static const uint32_t quattroColorMap[]=
235
1.70k
    {
236
1.70k
      0xFFFFFF, 0xC0C0C0, 0x808080, 0x000000,
237
1.70k
      0xFF0000, 0x00FF00, 0x0000FF, 0x00FFFF,
238
1.70k
      0xFF00FF, 0xFFFF00, 0x800080, 0x000080,
239
1.70k
      0x808000, 0x008000, 0x800000, 0x008080
240
1.70k
    };
241
1.70k
    for (uint32_t i : quattroColorMap)
242
27.3k
      THIS->m_colorsList.push_back(WPSColor(i));
243
1.70k
  }
244
1.90M
  if (id < 0 || id >= int(m_colorsList.size()))
245
16.4k
  {
246
16.4k
    WPS_DEBUG_MSG(("QuattroParserInternal::State::getColor(): unknown Quattro Pro color id: %d\n",id));
247
16.4k
    return false;
248
16.4k
  }
249
1.88M
  color = m_colorsList[size_t(id)];
250
1.88M
  return true;
251
1.90M
}
252
253
void State::initZoneNameMap()
254
23.9k
{
255
23.9k
  if (!m_idToZoneNameMap.empty())
256
0
    return;
257
23.9k
  m_idToZoneNameMap=std::map<int,ZoneName>
258
23.9k
  {
259
23.9k
    { 0x0, ZoneName("File", "begin") },
260
23.9k
    { 0x1, ZoneName("File", "end") },
261
23.9k
    { 0x2, ZoneName("Recalculation", "mode") },
262
23.9k
    { 0x3, ZoneName("Recalculation", "order") },
263
23.9k
    { 0x6, ZoneName("Sheet", "size") },
264
23.9k
    { 0xb, ZoneName("FldName") },
265
23.9k
    { 0xc, ZoneName("Cell", "blank") },
266
23.9k
    { 0xd, ZoneName("Cell", "int") },
267
23.9k
    { 0xe, ZoneName("Cell", "float") },
268
23.9k
    { 0xf, ZoneName("Cell", "label") },
269
23.9k
    { 0x10, ZoneName("Cell", "formula") },
270
23.9k
    { 0x18, ZoneName("Range", "table") },
271
23.9k
    { 0x19, ZoneName("Range", "query") },
272
23.9k
    { 0x1a, ZoneName("Print", "block") },
273
23.9k
    { 0x1b, ZoneName("Range", "sort,block") },
274
23.9k
    { 0x1c, ZoneName("Range", "fill") },
275
23.9k
    { 0x1d, ZoneName("Range", "sort,firstKey") },
276
23.9k
    { 0x20, ZoneName("Range", "frequency") },
277
23.9k
    { 0x23, ZoneName("Range", "sort,secondKey") },
278
23.9k
    { 0x24, ZoneName("Protection") },
279
23.9k
    { 0x25, ZoneName("Print", "footer") },
280
23.9k
    { 0x26, ZoneName("Print", "header") },
281
23.9k
    { 0x27, ZoneName("Print", "setup") },
282
23.9k
    { 0x28, ZoneName("Print", "margins") },
283
23.9k
    { 0x2f, ZoneName("Recalculation", "iteration,count") },
284
23.9k
    { 0x30, ZoneName("Print", "pagebreak") },
285
23.9k
    { 0x33, ZoneName("Cell", "string,value") },
286
23.9k
    { 0x4b, ZoneName("Password", "data") },
287
23.9k
    { 0x4c, ZoneName("Password", "level") },
288
23.9k
    { 0x4d, ZoneName("System", "property") },
289
23.9k
    { 0x66, ZoneName("Range", "parse") },
290
23.9k
    { 0x67, ZoneName("Range", "regression") },
291
23.9k
    { 0x69, ZoneName("Range", "matrix") },
292
23.9k
    { 0x96, ZoneName("Column", "size") },
293
23.9k
    { 0x97, ZoneName("External", "link") },
294
23.9k
    { 0x98, ZoneName("External", "name") },
295
23.9k
    { 0x99, ZoneName("Macro", "library") },
296
    // 0x9d Cell style (never seen)
297
    // 0x9e Conditional border color (never seen)
298
23.9k
    { 0x9f, ZoneName("Range", "sort,thirdKey") },
299
23.9k
    { 0xa0, ZoneName("Range", "sort,fourstKey") },
300
23.9k
    { 0xa1, ZoneName("Range", "sort,fifthKey") },
301
23.9k
    { 0xb7, ZoneName("Range", "solve for") },
302
23.9k
    { 0xc9, ZoneName("Version") },
303
23.9k
    { 0xca, ZoneName("Sheet", "begin") },
304
23.9k
    { 0xcb, ZoneName("Sheet", "end") },
305
23.9k
    { 0xcc, ZoneName("Sheet", "name") },
306
23.9k
    { 0xce, ZoneName("Cell", "style") },
307
23.9k
    { 0xcf, ZoneName("FontDef") },
308
23.9k
    { 0xd0, ZoneName("StyleName") },
309
23.9k
    { 0xd1, ZoneName("Sheet", "attribute") },
310
23.9k
    { 0xd2, ZoneName("Pane", "row,default") },
311
23.9k
    { 0xd3, ZoneName("Pane", "row2,default") },
312
23.9k
    { 0xd4, ZoneName("Pane", "col,default") },
313
23.9k
    { 0xd5, ZoneName("Pane", "col2,default") },
314
23.9k
    { 0xd6, ZoneName("Pane", "row") },
315
23.9k
    { 0xd7, ZoneName("Pane", "row2") },
316
23.9k
    { 0xd8, ZoneName("Pane", "col") },
317
23.9k
    { 0xd9, ZoneName("Pane", "col2") },
318
23.9k
    { 0xda, ZoneName("Pane", "font,max") },
319
23.9k
    { 0xdb, ZoneName("Pane", "font2,max") },
320
23.9k
    { 0xdc, ZoneName("Pane", "row,hidden") },
321
23.9k
    { 0xdd, ZoneName("Pane", "row2,hidden") },
322
23.9k
    { 0xde, ZoneName("Pane", "col,hidden") },
323
23.9k
    { 0xdf, ZoneName("Pane", "col2,hidden") },
324
23.9k
    { 0xe0, ZoneName("Pane", "style") },
325
23.9k
    { 0xe1, ZoneName("Pane", "style2") },
326
23.9k
    { 0xe2, ZoneName("PageGroup", "on") },
327
23.9k
    { 0xe3, ZoneName("PageGroup") },
328
23.9k
    { 0xe4, ZoneName("DLLIdFunct", "e5") },
329
23.9k
    { 0xe5, ZoneName("DLLIdFunct", "e6") },
330
23.9k
    { 0xe6, ZoneName("UserFormat") },
331
23.9k
    { 0xe7, ZoneName("Column", "def,attr") },
332
23.9k
    { 0xe8, ZoneName("ColorList") },
333
23.9k
    { 0xe9, ZoneName("Collection") }, // list of cell reference data, never seens
334
23.9k
    { 0xed, ZoneName("Print", "beg,names") },
335
23.9k
    { 0xee, ZoneName("Print", "formula") },
336
23.9k
    { 0xef, ZoneName("Print", "block,delimiter") },
337
23.9k
    { 0xf0, ZoneName("Print", "page,delimiter") },
338
23.9k
    { 0xf1, ZoneName("Print", "copies") },
339
23.9k
    { 0xf2, ZoneName("Print", "pages") },
340
23.9k
    { 0xf3, ZoneName("Print", "density") },
341
23.9k
    { 0xf4, ZoneName("Print", "tofit") },
342
23.9k
    { 0xf5, ZoneName("Print", "scaling") },
343
23.9k
    { 0xf6, ZoneName("Print", "paper,type") },
344
23.9k
    { 0xf7, ZoneName("Print", "orientation") },
345
23.9k
    { 0xf8, ZoneName("Print", "left,border") },
346
23.9k
    { 0xf9, ZoneName("Print", "top,border") },
347
23.9k
    { 0xfa, ZoneName("Print", "center,blocks") },
348
23.9k
    { 0xfb, ZoneName("Print", "end") },
349
23.9k
    { 0xfc, ZoneName("Print", "header,font") },
350
351
23.9k
    { 0x101, ZoneName("Print", "headings") },
352
23.9k
    { 0x102, ZoneName("Print", "gridlines") },
353
23.9k
    { 0x103, ZoneName("Optimizer") },
354
23.9k
    { 0x104, ZoneName("Optimizer", "constraint") },
355
23.9k
    { 0x105, ZoneName("Pane", "row,range") },
356
23.9k
    { 0x106, ZoneName("Pane", "row2,range") },
357
23.9k
    { 0x107, ZoneName("Pane", "font,max,range") },
358
23.9k
    { 0x108, ZoneName("Pane", "font2,max,range") },
359
23.9k
    { 0x109, ZoneName("Print", "beg,record") },
360
23.9k
    { 0x10a, ZoneName("Print", "beg,graph") },
361
23.9k
    { 0x10c, ZoneName("Print", "draft,margins") },
362
23.9k
    { 0x10d, ZoneName("Show", "compatible") },
363
23.9k
    { 0x110, ZoneName("Print", "footer,font") },
364
23.9k
    { 0x111, ZoneName("Print", "area") },
365
366
23.9k
    { 0x12e, ZoneName("Object", "number") },
367
23.9k
    { 0x12f, ZoneName("Query", "table,command") },
368
23.9k
    { 0x132, ZoneName("Formula", "compile") },
369
23.9k
    { 0x133, ZoneName("Formula", "audit") },
370
23.9k
    { 0x134, ZoneName("Sheet", "tab,color") },
371
23.9k
    { 0x135, ZoneName("Sheet", "zoom") },
372
23.9k
    { 0x136, ZoneName("Show", "notebook,object") },
373
23.9k
    { 0x137, ZoneName("Sheet", "protection") },
374
375
23.9k
    { 0x154, ZoneName("UserFormat", "complete") },
376
377
23.9k
    { 0x191, ZoneName("View", "begin") },
378
23.9k
    { 0x192, ZoneName("View", "end") },
379
23.9k
    { 0x193, ZoneName("View", "window") },
380
23.9k
    { 0x194, ZoneName("View", "location") },
381
23.9k
    { 0x195, ZoneName("View", "split") },
382
23.9k
    { 0x196, ZoneName("View", "synchronize") },
383
23.9k
    { 0x197, ZoneName("View", "pane,info") },
384
23.9k
    { 0x198, ZoneName("View", "pane2,info") },
385
23.9k
    { 0x199, ZoneName("View", "page") },
386
23.9k
    { 0x19a, ZoneName("View", "page2") },
387
23.9k
    { 0x19b, ZoneName("View", "current") },
388
23.9k
    { 0x19c, ZoneName("View", "display,settings") },
389
23.9k
    { 0x19e, ZoneName("View", "zoom") },
390
391
23.9k
    { 0x259, ZoneName("Graph", "begin,name") },
392
23.9k
    { 0x25a, ZoneName("Graph", "end") },
393
23.9k
    { 0x25d, ZoneName("Graph", "icon,coord") },
394
23.9k
    { 0x25e, ZoneName("Slide", "begin") },
395
23.9k
    { 0x25f, ZoneName("Slide", "end") },
396
23.9k
    { 0x260, ZoneName("Slide", "icon,coord") },
397
23.9k
    { 0x262, ZoneName("Slide", "time") },
398
23.9k
    { 0x263, ZoneName("Slide", "effect0") },
399
23.9k
    { 0x264, ZoneName("Graph", "version") },
400
23.9k
    { 0x265, ZoneName("Slide", "speed") },
401
23.9k
    { 0x266, ZoneName("Slide", "effect1") },
402
23.9k
    { 0x267, ZoneName("Slide", "level") },
403
23.9k
    { 0x26a, ZoneName("Slide", "type") },
404
23.9k
    { 0x26b, ZoneName("Slide", "comment") },
405
23.9k
    { 0x26c, ZoneName("Slide", "master,name") },
406
23.9k
    { 0x2bc, ZoneName("Graph", "beg,record") },
407
23.9k
    { 0x2bd, ZoneName("Chart", "beg,serie") },
408
23.9k
    { 0x2be, ZoneName("Chart", "end,serie") },
409
23.9k
    { 0x2bf, ZoneName("Serie", "Xlabel") },
410
23.9k
    { 0x2c0, ZoneName("Serie", "Zlabel") },
411
23.9k
    { 0x2c1, ZoneName("Serie", "legend") },
412
23.9k
    { 0x2c2, ZoneName("Serie", "number") },
413
23.9k
    { 0x2c3, ZoneName("Serie", "beg,data") },
414
23.9k
    { 0x2c4, ZoneName("Serie", "end,data") },
415
23.9k
    { 0x2c6, ZoneName("Serie", "data") },
416
23.9k
    { 0x2c7, ZoneName("Serie", "label") },
417
23.9k
    { 0x2c8, ZoneName("Serie", "legend") },
418
23.9k
    { 0x2c9, ZoneName("Chart", "beg,record") },
419
23.9k
    { 0x2ca, ZoneName("Chart", "end,record") },
420
23.9k
    { 0x2cb, ZoneName("Graph", "extension") },
421
23.9k
    { 0x2cd, ZoneName("Chart", "beg,save") },
422
23.9k
    { 0x2ce, ZoneName("Chart", "end,save") },
423
23.9k
    { 0x2db, ZoneName("Graph", "display,order") },
424
23.9k
    { 0x2dc, ZoneName("Serie", "extension") },
425
426
23.9k
    { 0x31f, ZoneName("Graph", "end,record") },
427
23.9k
    { 0x321, ZoneName("Object", "begin") },
428
23.9k
    { 0x322, ZoneName("Object", "end") },
429
430
23.9k
    { 0x335, ZoneName("GrDialog","textbox")},
431
23.9k
    { 0x337, ZoneName("GrDialog","37")},
432
23.9k
    { 0x33e, ZoneName("GrRect", "circle")},
433
23.9k
    { 0x33f, ZoneName("GrDialog","3f")},
434
23.9k
    { 0x342, ZoneName("GrDialog","42")},
435
23.9k
    { 0x343, ZoneName("GrDialog","button")},
436
23.9k
    { 0x345, ZoneName("GrDialog","bitmap")},
437
23.9k
    { 0x349, ZoneName("GrDialog","49")},
438
23.9k
    { 0x34a, ZoneName("GrDialog","4a")},
439
23.9k
    { 0x34e, ZoneName("GrDialog","4e")},
440
23.9k
    { 0x34f, ZoneName("GrDialog","4f")},
441
23.9k
    { 0x351, ZoneName("GrDialog","51")},
442
23.9k
    { 0x35a, ZoneName("GrLine")},
443
23.9k
    { 0x35b, ZoneName("GrPolygon")},
444
23.9k
    { 0x35c, ZoneName("GrPolygon","line")},
445
23.9k
    { 0x35d, ZoneName("GrDialog","5d")},
446
23.9k
    { 0x35e, ZoneName("GrDialog","main")},
447
23.9k
    { 0x364, ZoneName("GrRect")},
448
23.9k
    { 0x36d, ZoneName("GrDialog","6d")},
449
23.9k
    { 0x36f, ZoneName("GrTextBox")},
450
23.9k
    { 0x379, ZoneName("GrRect", "round")},
451
23.9k
    { 0x37b, ZoneName("GrLine", "arrow")},
452
23.9k
    { 0x37c, ZoneName("GrPolygon","line,bezier")},
453
454
23.9k
    { 0x381, ZoneName("Object", "frame,ole") },
455
23.9k
    { 0x382, ZoneName("Object", "image") },
456
23.9k
    { 0x383, ZoneName("Object", "bitmap") },
457
23.9k
    { 0x384, ZoneName("Object", "chart") },
458
23.9k
    { 0x385, ZoneName("Object", "frame") },
459
23.9k
    { 0x386, ZoneName("Object", "button") },
460
23.9k
    { 0x388, ZoneName("GrPolygon","bezier")},
461
23.9k
    { 0x38b, ZoneName("Object", "ole") },
462
463
23.9k
    { 0x4d3, ZoneName("Object", "shape") }
464
23.9k
  };
465
23.9k
}
466
467
}
468
469
// constructor, destructor
470
QuattroParser::QuattroParser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
471
                             libwps_tools_win::Font::Type encoding, char const *password)
472
23.9k
  : WKSParser(input, header)
473
23.9k
  , m_listener()
474
23.9k
  , m_state(new QuattroParserInternal::State(encoding, password))
475
    //, m_chartParser(new QuattroChart(*this))
476
23.9k
  , m_graphParser(new QuattroGraph(*this))
477
23.9k
  , m_spreadsheetParser(new QuattroSpreadsheet(*this))
478
23.9k
{
479
23.9k
}
480
481
QuattroParser::~QuattroParser()
482
23.9k
{
483
23.9k
}
484
485
int QuattroParser::version() const
486
37.9k
{
487
37.9k
  return m_state->m_version;
488
37.9k
}
489
490
libwps_tools_win::Font::Type QuattroParser::getDefaultFontType() const
491
2.29M
{
492
2.29M
  return m_state->getDefaultFontType();
493
2.29M
}
494
495
bool QuattroParser::getExternalFileName(int fId, librevenge::RVNGString &fName) const
496
18.6k
{
497
18.6k
  auto it = m_state->m_idToExternalFileMap.find(fId);
498
18.6k
  if (it!=m_state->m_idToExternalFileMap.end())
499
5.73k
  {
500
5.73k
    fName=it->second;
501
5.73k
    return true;
502
5.73k
  }
503
12.9k
  WPS_DEBUG_MSG(("QuattroParser::getExternalFileName: can not find %d name\n", fId));
504
12.9k
  return false;
505
18.6k
}
506
507
bool QuattroParser::getField(int fId, librevenge::RVNGString &text,
508
                             QuattroFormulaInternal::CellReference &ref,
509
                             librevenge::RVNGString const &fileName) const
510
44.2k
{
511
44.2k
  ref.m_cells.clear();
512
44.2k
  if (fId&0x4000)
513
1.32k
  {
514
1.32k
    auto it=m_state->m_idToExternalNameMap.find(fId & 0xbfff);
515
1.32k
    if (it!=m_state->m_idToExternalNameMap.end() && !it->second.empty())
516
6
    {
517
6
      text=it->second;
518
6
      WKSContentListener::FormulaInstruction instr;
519
6
      instr.m_type=instr.F_Text;
520
6
      if (!fileName.empty())
521
5
      {
522
5
        instr.m_content="[";
523
5
        instr.m_content+=fileName.cstr();
524
5
        instr.m_content+="]";
525
5
      }
526
6
      instr.m_content+=it->second.cstr();
527
6
      ref.addInstruction(instr);
528
6
      return true;
529
6
    }
530
1.32k
    WPS_DEBUG_MSG(("QuattroParser::getField: can not find %d name\n", fId&0xbfff));
531
1.32k
    return false;
532
1.32k
  }
533
42.8k
  auto it = m_state->m_idToFieldMap.find(fId);
534
42.8k
  if (it!=m_state->m_idToFieldMap.end())
535
28.4k
  {
536
28.4k
    text=it->second.first;
537
28.4k
    ref=it->second.second;
538
28.4k
    if (!fileName.empty())   // unsure
539
1
    {
540
1
      for (auto &r : ref.m_cells)
541
1
      {
542
1
        if (r.m_type==r.F_Cell || r.m_type==r.F_CellList)
543
1
          r.m_fileName=fileName;
544
1
      }
545
1
    }
546
28.4k
    return true;
547
28.4k
  }
548
14.3k
  WPS_DEBUG_MSG(("QuattroParser::getField: can not find %d field\n", fId));
549
14.3k
  return false;
550
42.8k
}
551
552
//////////////////////////////////////////////////////////////////////
553
// interface with QuattroGraph
554
//////////////////////////////////////////////////////////////////////
555
556
bool QuattroParser::sendPageGraphics(int sheetId) const
557
641k
{
558
641k
  return m_graphParser->sendPageGraphics(sheetId);
559
641k
}
560
561
bool QuattroParser::sendGraphics(int sheetId, Vec2i const &cell) const
562
7.07k
{
563
7.07k
  return m_graphParser->sendGraphics(sheetId, cell);
564
7.07k
}
565
566
567
//////////////////////////////////////////////////////////////////////
568
// interface with QuattroSpreadsheet
569
//////////////////////////////////////////////////////////////////////
570
571
Vec2f QuattroParser::getCellPosition(int sheetId, Vec2i const &cell) const
572
702
{
573
702
  return m_spreadsheetParser->getPosition(sheetId, cell);
574
702
}
575
576
bool QuattroParser::getColor(int id, WPSColor &color) const
577
1.90M
{
578
1.90M
  return m_state->getColor(id, color);
579
1.90M
}
580
581
bool QuattroParser::getFont(int id, WPSFont &font, libwps_tools_win::Font::Type &type) const
582
357k
{
583
357k
  if (id < 0 || id>=int(m_state->m_fontsList.size()))
584
91.0k
  {
585
91.0k
    WPS_DEBUG_MSG(("QuattroParser::getFont: can not find font %d\n", id));
586
91.0k
    return false;
587
91.0k
  }
588
266k
  auto const &ft=m_state->m_fontsList[size_t(id)];
589
266k
  font=ft;
590
266k
  type=ft.m_type;
591
266k
  return true;
592
357k
}
593
594
// main function to parse the document
595
void QuattroParser::parse(librevenge::RVNGSpreadsheetInterface *documentInterface)
596
23.9k
{
597
23.9k
  RVNGInputStreamPtr input=getInput();
598
23.9k
  if (!input)
599
0
  {
600
0
    WPS_DEBUG_MSG(("QuattroParser::parse: does not find main ole\n"));
601
0
    throw (libwps::ParseException());
602
0
  }
603
604
23.9k
  if (!checkHeader(nullptr)) throw(libwps::ParseException());
605
606
23.9k
  bool ok=false;
607
23.9k
  try
608
23.9k
  {
609
23.9k
    ascii().setStream(input);
610
23.9k
    ascii().open("MN0");
611
23.9k
    if (checkHeader(nullptr) && readZones())
612
23.8k
      m_listener=createListener(documentInterface);
613
23.9k
    if (m_listener)
614
23.8k
    {
615
      //m_chartParser->setListener(m_listener);
616
23.8k
      m_graphParser->setListener(m_listener);
617
23.8k
      m_spreadsheetParser->setListener(m_listener);
618
619
23.8k
      m_graphParser->updateState();
620
23.8k
      m_spreadsheetParser->updateState();
621
622
23.8k
      m_listener->startDocument();
623
23.8k
      int numSheet=m_spreadsheetParser->getNumSpreadsheets();
624
23.8k
      if (numSheet==0) ++numSheet;
625
665k
      for (int i=0; i<numSheet; ++i)
626
641k
        m_spreadsheetParser->sendSpreadsheet(i, m_graphParser->getGraphicCellsInSheet(i));
627
23.8k
      m_listener->endDocument();
628
23.8k
      m_listener.reset();
629
23.8k
      ok = true;
630
23.8k
    }
631
23.9k
  }
632
23.9k
  catch (...)
633
23.9k
  {
634
28
    WPS_DEBUG_MSG(("QuattroParser::parse: exception catched when parsing MN0\n"));
635
28
    throw (libwps::ParseException());
636
28
  }
637
638
23.8k
  ascii().reset();
639
23.8k
  if (!ok)
640
0
    throw(libwps::ParseException());
641
23.8k
}
642
643
std::shared_ptr<WKSContentListener> QuattroParser::createListener(librevenge::RVNGSpreadsheetInterface *interface)
644
23.8k
{
645
23.8k
  std::vector<WPSPageSpan> pageList;
646
23.8k
  WPSPageSpan ps(m_state->m_pageSpan);
647
23.8k
  int numSheet=m_spreadsheetParser->getNumSpreadsheets();
648
23.8k
  if (numSheet<=0) numSheet=1;
649
23.8k
  if (!m_state->m_headerString.empty())
650
2.17k
  {
651
2.17k
    WPSSubDocumentPtr subdoc(new QuattroParserInternal::SubDocument
652
2.17k
                             (getInput(), *this, true));
653
2.17k
    ps.setHeaderFooter(WPSPageSpan::HEADER, WPSPageSpan::ALL, subdoc);
654
2.17k
  }
655
23.8k
  if (!m_state->m_footerString.empty())
656
1.67k
  {
657
1.67k
    WPSSubDocumentPtr subdoc(new QuattroParserInternal::SubDocument
658
1.67k
                             (getInput(), *this, false));
659
1.67k
    ps.setHeaderFooter(WPSPageSpan::FOOTER, WPSPageSpan::ALL, subdoc);
660
1.67k
  }
661
23.8k
  ps.setPageSpan(numSheet);
662
23.8k
  pageList.push_back(ps);
663
23.8k
  auto listener=std::make_shared<WKSContentListener>(pageList, interface);
664
23.8k
  listener->setMetaData(m_state->m_metaData);
665
23.8k
  return listener;
666
23.8k
}
667
668
////////////////////////////////////////////////////////////
669
// low level
670
////////////////////////////////////////////////////////////
671
// read the header
672
////////////////////////////////////////////////////////////
673
bool QuattroParser::checkHeader(WPSHeader *header, bool strict)
674
47.8k
{
675
47.8k
  m_state.reset(new QuattroParserInternal::State(m_state->m_fontType, m_state->m_password));
676
47.8k
  auto input=getInput();
677
47.8k
  auto mainStream=std::make_shared<WPSStream>(input, ascii());
678
47.8k
  if (!mainStream || !checkHeader(mainStream, strict))
679
17
    return false;
680
47.8k
  if (header)
681
0
  {
682
0
    header->setMajorVersion(m_state->m_version);
683
0
    header->setCreator(libwps::WPS_QUATTRO_PRO);
684
0
    header->setKind(libwps::WPS_SPREADSHEET);
685
0
    header->setIsEncrypted(m_state->m_isEncrypted);
686
0
    header->setNeedEncoding(true);
687
0
  }
688
47.8k
  return true;
689
47.8k
}
690
691
bool QuattroParser::checkHeader(std::shared_ptr<WPSStream> stream, bool strict)
692
47.8k
{
693
47.8k
  if (!stream)
694
0
  {
695
0
    WPS_DEBUG_MSG(("QuattroParser::checkHeader: called without stream\n"));
696
0
    return false;
697
0
  }
698
699
47.8k
  RVNGInputStreamPtr input = stream->m_input;
700
47.8k
  libwps::DebugFile &ascFile=stream->m_ascii;
701
47.8k
  libwps::DebugStream f;
702
703
47.8k
  if (!input || !stream->checkFilePosition(12))
704
17
  {
705
17
    WPS_DEBUG_MSG(("QuattroParser::checkHeader: file is too short\n"));
706
17
    return false;
707
17
  }
708
709
47.8k
  input->seek(0,librevenge::RVNG_SEEK_SET);
710
47.8k
  auto firstOffset = int(libwps::readU8(input));
711
47.8k
  auto type = int(libwps::read8(input));
712
47.8k
  f << "FileHeader:";
713
47.8k
  if (firstOffset == 0 && type == 0)
714
47.8k
    m_state->m_version=1000;
715
0
  else
716
0
  {
717
0
    WPS_DEBUG_MSG(("QuattroParser::checkHeader: find unexpected first data\n"));
718
0
    return false;
719
0
  }
720
47.8k
  auto val=int(libwps::read16(input));
721
47.8k
  if (val==2)
722
47.8k
  {
723
    // version
724
47.8k
    val=int(libwps::readU16(input));
725
47.8k
    if (val==0x1001)
726
32.3k
    {
727
32.3k
      m_state->m_version=1001;
728
32.3k
      f << "quattropro[wb1],";
729
32.3k
    }
730
15.5k
    else if (val==0x1002)
731
15.4k
    {
732
15.4k
      m_state->m_version=1002;
733
15.4k
      f << "quattropro[wb2],";
734
15.4k
    }
735
102
    else if (val==0x1007)
736
102
    {
737
102
      m_state->m_version=1003;
738
102
      f << "quattropro[wb3],";
739
102
    }
740
0
    else
741
0
    {
742
0
      WPS_DEBUG_MSG(("QuattroParser::checkHeader: find unknown file version\n"));
743
0
      return false;
744
0
    }
745
47.8k
  }
746
0
  else
747
0
  {
748
0
    WPS_DEBUG_MSG(("QuattroParser::checkHeader: header contain unexpected size field data\n"));
749
0
    return false;
750
0
  }
751
47.8k
  input->seek(0, librevenge::RVNG_SEEK_SET);
752
47.8k
  if (strict)
753
0
  {
754
0
    for (int i=0; i < 6; ++i)
755
0
    {
756
0
      if (!readZone(stream)) return false;
757
0
      if (m_state->m_isEncrypted) break;
758
0
    }
759
0
  }
760
47.8k
  ascFile.addPos(0);
761
47.8k
  ascFile.addNote(f.str().c_str());
762
763
47.8k
  return true;
764
47.8k
}
765
766
bool QuattroParser::readZones()
767
23.9k
{
768
23.9k
  int const vers=version();
769
23.9k
  m_graphParser->cleanState();
770
23.9k
  m_spreadsheetParser->cleanState();
771
23.9k
  m_state->initZoneNameMap();
772
773
23.9k
  std::shared_ptr<WPSStream> stream(new WPSStream(getInput(), ascii()));
774
23.9k
  RVNGInputStreamPtr &input = stream->m_input;
775
23.9k
  libwps::DebugFile &ascFile=stream->m_ascii;
776
23.9k
  input->seek(0, librevenge::RVNG_SEEK_SET);
777
6.32M
  while (true)
778
6.32M
  {
779
6.32M
    if (!stream->checkFilePosition(input->tell()+4))
780
7.59k
      break;
781
6.32M
    if (!readZone(stream))
782
16.3k
      break;
783
6.30M
    if (m_state->m_isEncrypted && !m_state->m_isDecoded)
784
28
      throw(libwps::PasswordException());
785
6.30M
  }
786
787
  //
788
  // look for ending
789
  //
790
23.8k
  long pos = input->tell();
791
23.8k
  if (!stream->checkFilePosition(pos+4))
792
7.59k
  {
793
7.59k
    WPS_DEBUG_MSG(("QuattroParser::readZones: cell header is too short\n"));
794
7.59k
    return m_spreadsheetParser->getNumSpreadsheets()>0;
795
7.59k
  }
796
16.3k
  auto type = int(libwps::readU16(input)); // 1
797
16.3k
  auto length = int(libwps::readU16(input));
798
16.3k
  if (length)
799
12.4k
  {
800
12.4k
    WPS_DEBUG_MSG(("QuattroParser::readZones: parse breaks before ending\n"));
801
12.4k
    ascFile.addPos(pos);
802
12.4k
    ascFile.addNote("Entries(BAD):###");
803
12.4k
    return m_spreadsheetParser->getNumSpreadsheets()>0;
804
12.4k
  }
805
806
3.84k
  ascFile.addPos(pos);
807
3.84k
  if (type != 1)
808
993
  {
809
993
    WPS_DEBUG_MSG(("QuattroParser::readZones: odd end cell type: %d\n", type));
810
993
    ascFile.addNote("Entries(BAD):###");
811
993
    return m_spreadsheetParser->getNumSpreadsheets();
812
993
  }
813
2.85k
  ascFile.addNote("Entries(EndSpreadsheet)");
814
815
  // checkme: crypted .wb3 files also contain an OLE zone, but it seems empty...
816
2.85k
  if (vers==1002 || (vers==1003 && m_state->m_isEncrypted))
817
2.67k
    readOLEZones(stream);
818
2.85k
  if (vers==1003)
819
30
  {
820
    // object in PerfectOffice_OBJECTS/_1507007992_c/, ... *
821
30
    parseOLEStream(getFileInput(), "PerfectOffice_MAIN");
822
30
  }
823
2.85k
  return m_spreadsheetParser->getNumSpreadsheets();
824
3.84k
}
825
826
bool QuattroParser::parseOLEStream(RVNGInputStreamPtr input, std::string const &avoid)
827
1.77k
{
828
1.77k
  if (!input || !input->isStructured())
829
0
  {
830
0
    WPS_DEBUG_MSG(("QuattroParser::parseOLEStream: oops, can not find the input stream\n"));
831
0
    return false;
832
0
  }
833
1.77k
  std::map<std::string,size_t> dirToIdMap;
834
1.77k
  WPSOLEParser oleParser(avoid, getDefaultFontType(),
835
1.77k
                         [&dirToIdMap](std::string const &dir)
836
6.15k
  {
837
6.15k
    if (dirToIdMap.find(dir)==dirToIdMap.end())
838
2.18k
      dirToIdMap[dir]=dirToIdMap.size();
839
6.15k
    return int(dirToIdMap.find(dir)->second);
840
6.15k
  });
841
1.77k
  oleParser.parse(input);
842
1.77k
  oleParser.updateMetaData(m_state->m_metaData);
843
1.77k
  auto objectMap=oleParser.getObjectsMap();
844
1.77k
  std::map<librevenge::RVNGString,WPSEmbeddedObject> nameToObjectsMap;
845
1.77k
  for (auto it : dirToIdMap)
846
2.18k
  {
847
2.18k
    if (it.first.empty()) continue;
848
4.67k
    for (int wh=0; wh<2; ++wh)
849
3.11k
    {
850
3.11k
      std::string name=it.first+"/"+(wh==0 ? "LinkInfo" : "BOlePart");
851
3.11k
      RVNGInputStreamPtr cOle(input->getSubStreamByName(name.c_str()));
852
3.11k
      if (!cOle)
853
2.12k
      {
854
2.12k
        WPS_DEBUG_MSG(("QuattroParser::parseOLEStream: oops, can not find link info for dir %s\n", name.c_str()));
855
2.12k
        continue;
856
2.12k
      }
857
989
      libwps::DebugFile asciiFile(cOle);
858
989
      asciiFile.open(libwps::Debug::flattenFileName(name));
859
989
      if (wh==1)
860
570
        readOleBOlePart(std::make_shared<WPSStream>(cOle,asciiFile));
861
419
      else
862
419
      {
863
419
        librevenge::RVNGString linkName;
864
419
        if (readOleLinkInfo(std::make_shared<WPSStream>(cOle,asciiFile),linkName) && !linkName.empty())
865
330
        {
866
330
          if (objectMap.find(int(it.second))==objectMap.end())
867
93
          {
868
93
            WPS_DEBUG_MSG(("QuattroParser::parseOLEStream: oops, can not find embedded data for %s\n", name.c_str()));
869
93
            continue;
870
93
          }
871
237
          nameToObjectsMap[linkName]=objectMap.find(int(it.second))->second;
872
237
        }
873
419
      }
874
989
    }
875
1.55k
  }
876
1.77k
  if (!nameToObjectsMap.empty())
877
168
    m_graphParser->storeObjects(nameToObjectsMap);
878
1.77k
  return true;
879
1.77k
}
880
881
bool QuattroParser::readOLEZones(std::shared_ptr<WPSStream> &stream)
882
2.67k
{
883
2.67k
  if (!stream)
884
0
    return false;
885
2.67k
  RVNGInputStreamPtr input = stream->m_input;
886
2.67k
  libwps::DebugFile &ascFile=stream->m_ascii;
887
2.67k
  libwps::DebugStream f;
888
2.67k
  f << "Entries(OLEData)[header]:";
889
2.67k
  long pos = input->tell();
890
2.67k
  long endPos = stream->m_eof;
891
2.67k
  if (!stream->checkFilePosition(pos+18))
892
29
  {
893
29
    WPS_DEBUG_MSG(("QuattroParser::readOLEZones: the zone seems to short\n"));
894
29
    ascFile.addPos(pos);
895
29
    ascFile.addNote(f.str().c_str());
896
29
    return false;
897
29
  }
898
13.2k
  for (int i=0; i<4; ++i)   // 0
899
10.5k
  {
900
10.5k
    auto val=int(libwps::read16(input));
901
10.5k
    if (val) f << "f" << i << "=" << val << ",";
902
10.5k
  }
903
2.64k
  auto sSz=long(libwps::readU32(input));
904
2.64k
  librevenge::RVNGString text; // QPW$ExtendedStorage$6.0
905
2.64k
  if (sSz<=0 || sSz > endPos-input->tell()-6 || !readCString(stream,text,sSz))
906
168
  {
907
168
    WPS_DEBUG_MSG(("QuattroParser::readOLEZones: can not read header's type\n"));
908
168
    f << "##sSz,";
909
168
    ascFile.addPos(pos);
910
168
    ascFile.addNote(f.str().c_str());
911
168
    return true;
912
168
  }
913
2.48k
  f << "type=" << text.cstr() << ",";
914
2.48k
  ascFile.addPos(pos);
915
2.48k
  ascFile.addNote(f.str().c_str());
916
5.22k
  while (input->tell()+6<=endPos)
917
4.98k
  {
918
4.98k
    pos=input->tell();
919
4.98k
    f.str("");
920
4.98k
    f << "OLEData:";
921
4.98k
    auto type=int(libwps::read16(input));
922
4.98k
    sSz=long(libwps::readU32(input));
923
4.98k
    if (sSz < 0 || sSz > endPos-pos-6 || type<1 || type>2 || (sSz==0 && type==2))
924
2.19k
    {
925
2.19k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
926
2.19k
      break;
927
2.19k
    }
928
2.78k
    if (type==1)
929
47
    {
930
47
      if (sSz)
931
0
        f << "###sz=" << sSz << ",";
932
47
      f << "end,";
933
47
      ascFile.addPos(pos);
934
47
      ascFile.addNote(f.str().c_str());
935
47
      return true;
936
47
    }
937
2.74k
    ascFile.addPos(pos);
938
2.74k
    ascFile.addNote(f.str().c_str());
939
2.74k
    unsigned long numRead;
940
2.74k
    const unsigned char *data=input->read(static_cast<unsigned long>(sSz), numRead);
941
2.74k
    if (data && long(numRead)==sSz)
942
2.74k
    {
943
2.74k
      auto ole=libwps_OLE::getOLEInputStream(std::make_shared<WPSStringStream>(data, unsigned(numRead)));
944
2.74k
      if (!ole)
945
996
      {
946
996
        WPS_DEBUG_MSG(("QuattroParser::readOLEZones::readOLE: oops, can not decode the ole\n"));
947
996
      }
948
1.74k
      else
949
1.74k
      {
950
        /* normally /_Date_XXX/ Where Date~1507005964 and XXX is an hexadecimal data.
951
           Moreever, a file can be saved many times, so must read /_Date_XXX/ to retrieve the correspondance
952
         */
953
1.74k
        ascFile.skipZone(pos+6, pos+6+sSz-1);
954
1.74k
        parseOLEStream(ole);
955
1.74k
      }
956
2.74k
    }
957
0
    else
958
0
    {
959
0
      WPS_DEBUG_MSG(("QuattroParser::readOLEZones::readOLE: I can not find the data\n"));
960
0
      input->seek(pos, librevenge::RVNG_SEEK_SET);
961
0
      break;
962
0
    }
963
2.74k
    input->seek(pos+6+sSz, librevenge::RVNG_SEEK_SET);
964
2.74k
  }
965
2.43k
  if (input->tell()<endPos)
966
2.34k
  {
967
2.34k
    WPS_DEBUG_MSG(("QuattroParser::readOLEZones: find extra data\n"));
968
2.34k
    ascFile.addPos(input->tell());
969
2.34k
    ascFile.addNote("OLEData:###extra");
970
2.34k
  }
971
2.43k
  return true;
972
2.48k
}
973
974
bool QuattroParser::readZone(std::shared_ptr<WPSStream> &stream)
975
7.01M
{
976
7.01M
  if (!stream)
977
0
    return false;
978
7.01M
  RVNGInputStreamPtr input = stream->m_input;
979
7.01M
  libwps::DebugFile &ascFile=stream->m_ascii;
980
7.01M
  libwps::DebugStream f;
981
7.01M
  long pos = input->tell();
982
7.01M
  auto id = int(libwps::readU16(input));
983
7.01M
  auto sz = long(libwps::readU16(input));
984
7.01M
  if (sz<0 || !stream->checkFilePosition(pos+4+sz))
985
12.0k
  {
986
12.0k
    WPS_DEBUG_MSG(("QuattroParser::readZone: size is bad\n"));
987
12.0k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
988
12.0k
    return false;
989
12.0k
  }
990
991
7.00M
  if (id&0x8000)
992
9.44k
  {
993
    // very rare, unsure what this means ; is it possible to have
994
    // other flags here ?
995
9.44k
    WPS_DEBUG_MSG(("QuattroParser::readZone: find type[8] flags\n"));
996
9.44k
    ascFile.addPos(pos);
997
9.44k
    ascFile.addNote("#flag8000,");
998
9.44k
    id &= 0x7fff;
999
9.44k
  }
1000
1001
7.00M
  if (id>=0x800) // I never found anything biggest than 47d, so must be ok
1002
2.17k
  {
1003
2.17k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1004
2.17k
    return false;
1005
2.17k
  }
1006
1007
7.00M
  if (sz>=0xFF00 && stream->checkFilePosition(pos+4+sz+4))
1008
422
  {
1009
422
    input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
1010
422
    if (libwps::readU16(input)==0x10f)
1011
16
    {
1012
      // incomplete block, we must rebuild it
1013
16
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1014
16
      unsigned long numRead;
1015
16
      const unsigned char *data=input->read(4+static_cast<unsigned long>(sz), numRead);
1016
16
      if (data && long(numRead)==4+sz)
1017
16
      {
1018
16
        ascFile.skipZone(pos,pos+4+sz-1);
1019
16
        auto newInput=std::make_shared<WPSStringStream>(data, unsigned(numRead));
1020
16
        bool ok=true;
1021
24
        while (true)
1022
24
        {
1023
24
          long actPos=input->tell();
1024
24
          if (!stream->checkFilePosition(actPos+4) || libwps::readU16(input)!=0x10f)
1025
8
          {
1026
8
            input->seek(actPos, librevenge::RVNG_SEEK_SET);
1027
8
            break;
1028
8
          }
1029
16
          auto extraSize=long(libwps::readU16(input));
1030
16
          if (!stream->checkFilePosition(actPos+4+extraSize))
1031
3
          {
1032
3
            ok=false;
1033
3
            break;
1034
3
          }
1035
13
          ascFile.addPos(actPos);
1036
13
          ascFile.addNote("Entries(ExtraData):");
1037
13
          if (!extraSize)
1038
5
            break;
1039
8
          data=input->read(static_cast<unsigned long>(extraSize), numRead);
1040
8
          if (!data || long(numRead)!=extraSize)
1041
0
          {
1042
0
            ok=false;
1043
0
            break;
1044
0
          }
1045
8
          newInput->append(data, unsigned(numRead));
1046
8
          ascFile.skipZone(actPos+4,actPos+4+extraSize-1);
1047
8
        }
1048
16
        if (ok)
1049
13
        {
1050
13
          std::stringstream s;
1051
13
          static int complexDataNum=0;
1052
13
          s << "Data" << ++complexDataNum;
1053
13
          auto newStream=std::make_shared<WPSStream>(newInput);
1054
13
          newStream->m_ascii.open(s.str());
1055
13
          newStream->m_ascii.setStream(newInput);
1056
13
          readZone(newStream);
1057
13
          return true;
1058
13
        }
1059
16
      }
1060
3
      WPS_DEBUG_MSG(("QuattroParser::readZone: can not reconstruct a zone\n"));
1061
3
      ascFile.addPos(pos);
1062
3
      ascFile.addNote("Entries(###Bad):");
1063
3
      input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
1064
3
      return true;
1065
16
    }
1066
422
  }
1067
7.00M
  auto zIt=m_state->m_idToZoneNameMap.find(id);
1068
7.00M
  if (zIt==m_state->m_idToZoneNameMap.end())
1069
719k
    f << "Entries(Zone" << std::hex << id << std::dec << "A):";
1070
6.28M
  else if (zIt->second.m_extra.empty())
1071
538k
    f << "Entries(" << zIt->second.m_name << "):";
1072
5.74M
  else
1073
5.74M
    f << "Entries(" << zIt->second.m_name << ")[" << zIt->second.m_extra << "]:";
1074
7.00M
  if (id>1)
1075
3.72M
  {
1076
3.72M
    ascFile.addPos(pos);
1077
3.72M
    ascFile.addNote(f.str().c_str());
1078
3.72M
  }
1079
7.00M
  f.str("");
1080
1081
7.00M
  bool ok = true, isParsed = false, needWriteInAscii = false;
1082
7.00M
  int val;
1083
7.00M
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1084
7.00M
  switch (id)
1085
7.00M
  {
1086
3.27M
  case 0:
1087
3.27M
    if (sz!=2) break;
1088
110k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1089
110k
    f << "version=" << std::hex << libwps::readU16(input) << std::dec << ",";
1090
110k
    isParsed=needWriteInAscii=true;
1091
110k
    break;
1092
2.95k
  case 0x1: // EOF
1093
2.95k
    ok = false;
1094
2.95k
    break;
1095
1096
  // no data
1097
9.54k
  case 0xfb: // print end record/end
1098
10.3k
  case 0x191:
1099
11.5k
  case 0x192:
1100
15.2k
  case 0x25a:
1101
20.0k
  case 0x25b:
1102
24.6k
  case 0x25c:
1103
24.7k
  case 0x25f:
1104
27.8k
  case 0x2bc:
1105
30.1k
  case 0x2bd:
1106
32.5k
  case 0x2be:
1107
36.1k
  case 0x2c3:
1108
39.0k
  case 0x2c4:
1109
42.1k
  case 0x2c9:
1110
44.5k
  case 0x2ca:
1111
48.4k
  case 0x2cd:
1112
51.4k
  case 0x2ce:
1113
53.8k
  case 0x31f:
1114
53.8k
    if (sz!=0) break;
1115
51.8k
    isParsed=needWriteInAscii=true;
1116
51.8k
    break;
1117
  // boolean
1118
128k
  case 0x2: // calculation mode 0: manual, 1:background, FF:auto
1119
139k
  case 0x3: // calculation order 0: default, 1:column, ff:row
1120
149k
  case 0x24: // protected if FF
1121
158k
  case 0x2f: // number of iteration
1122
165k
  case 0x30: // print page break 0=yes, 1=no
1123
173k
  case 0x99: // 0: no macro, 1: macro library
1124
177k
  case 0xe2: // 0: off, 1: on
1125
185k
  case 0xee: // 0: print results, 1: print formula
1126
195k
  case 0xf3: // 0: draft, 1: final
1127
204k
  case 0xf4: // 0: no, 1: yes, sometimes with size 5
1128
212k
  case 0xf7: // 0: landscape, 1: portrait
1129
226k
  case 0xfa: // 0: no centering, 1: print centered
1130
237k
  case 0x101: // 0: do not print headings, 1: print headings
1131
362k
  case 0x102: // 0: do not print gridlines, 1: print them
1132
366k
  case 0x109: // in fact, a name always empty
1133
372k
  case 0x10a: // in fact, a name always empty
1134
378k
  case 0x111: // 0: current page, 1: notebook, 2: block selection
1135
383k
  case 0x132: // 0: no formula compiled, 1: formula compiled
1136
390k
  case 0x133: // 0: no audit display, 1: display audit
1137
397k
  case 0x137: // 0: disabled, 1: enabled
1138
400k
  case 0x196: // 0: synchronized, 2: not synchronized
1139
401k
  case 0x19b: // 1: pane 1, 2: pane 2
1140
401k
    if (sz==0)
1141
209k
    {
1142
209k
      f << "##";
1143
209k
      break;
1144
209k
    }
1145
192k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1146
192k
    val=int(libwps::readU8(input));
1147
192k
    if (val==1) f << "true,";
1148
170k
    else if (val) f << "#val=" << val << ",";
1149
192k
    if (id==0xf4 && sz==5)
1150
3.47k
    {
1151
10.4k
      for (int i=0; i<2; ++i)
1152
6.95k
      {
1153
6.95k
        val=int(libwps::readU16(input));
1154
6.95k
        if (val!=1) f << "f" << i << "=" << val << ",";
1155
6.95k
      }
1156
3.47k
    }
1157
192k
    isParsed=needWriteInAscii=true;
1158
192k
    break;
1159
31.8k
  case 0x4c: // with 0: none, 1:low, 2:medium, 3:high
1160
41.8k
  case 0x4d: // with 0
1161
50.3k
  case 0xc9: // with [026]{00,01,c8}
1162
146k
  case 0xe0: // default panel style
1163
146k
  case 0xe1: // default panel2 style
1164
153k
  case 0xf1: // number of copies
1165
162k
  case 0xf5: // with scaling in %
1166
180k
  case 0xf6: // paper type: 0|1|9|101
1167
184k
  case 0x12d: // with 1
1168
187k
  case 0x136: // 0: show all objects, 1: show outline objects, 2: hidde object
1169
188k
  case 0x19e: // zoom factor
1170
188k
  case 0x262: // time
1171
188k
  case 0x263: // slide effect
1172
189k
  case 0x265: // speed
1173
189k
  case 0x266: // slide special effect
1174
189k
  case 0x26a: // 0: small, 1: medium, 2: large, 3: list of name
1175
191k
  case 0x2c2: // number of series
1176
191k
    if (sz!=2)
1177
111k
    {
1178
111k
      f << "##";
1179
111k
      break;
1180
111k
    }
1181
80.5k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1182
80.5k
    val=int(libwps::readU16(input));
1183
80.5k
    if (val) f << "f0=" << std::hex << val << std::dec << ",";
1184
80.5k
    break;
1185
213
  case 0xe7: // f0=column, f1>>3=def attritude
1186
8.21k
  case 0xef: // f0=number of line, f1:0=specified number of line, 1=page feed
1187
15.4k
  case 0xf0: // f0=number of line, f1:0=specified number of line, 1=page feed
1188
17.3k
  case 0x12e: // f0-f1=number
1189
20.7k
  case 0x25d: // f0=x, f1=y
1190
21.1k
  case 0x260: // f0=x, f1=y
1191
23.2k
  case 0x264: // version
1192
23.2k
    if (sz!=4)
1193
2.22k
    {
1194
2.22k
      f << "##";
1195
2.22k
      break;
1196
2.22k
    }
1197
21.0k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1198
63.1k
    for (int i=0; i<2; ++i)
1199
42.0k
    {
1200
42.0k
      val=int(libwps::readU16(input));
1201
42.0k
      if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1202
42.0k
    }
1203
21.0k
    break;
1204
6.92k
  case 0xf2: // f0=min page, f1=max page, f2:0=print range, 1=print all
1205
6.92k
    if (sz!=6)
1206
277
    {
1207
277
      f << "##";
1208
277
      break;
1209
277
    }
1210
6.64k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1211
26.5k
    for (int i=0; i<3; ++i)
1212
19.9k
    {
1213
19.9k
      val=int(libwps::readU16(input));
1214
19.9k
      if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1215
19.9k
    }
1216
6.64k
    break;
1217
  // not basic
1218
8.83k
  case 0x6: // active worksheet range
1219
8.83k
    ok = m_spreadsheetParser->readSheetSize(stream);
1220
8.83k
    isParsed = true;
1221
8.83k
    break;
1222
77.4k
  case 0xb: // named range
1223
77.4k
    readFieldName(stream);
1224
77.4k
    isParsed=true;
1225
77.4k
    break;
1226
27.6k
  case 0xc: // blank cell
1227
57.3k
  case 0xd: // integer cell
1228
69.8k
  case 0xe: // floating cell
1229
233k
  case 0xf: // label cell
1230
283k
  case 0x10: // formula cell
1231
290k
  case 0x33:  // value of string formula
1232
    // case 10e:  seems relative to cell with formula: list of dependency? can have some text
1233
290k
    ok = m_spreadsheetParser->readCell(stream);
1234
290k
    isParsed=true;
1235
290k
    break;
1236
1237
6.77k
  case 0x18: // data table range
1238
14.2k
  case 0x19: // query range
1239
26.0k
  case 0x1a: // print block
1240
32.7k
  case 0x1b: // sort range
1241
37.4k
  case 0x1c: // fill range
1242
47.3k
  case 0x1d: // primary sort key range
1243
61.3k
  case 0x20: // distribution range
1244
67.9k
  case 0x23: // secondary sort key range
1245
72.2k
  case 0x66: // parse
1246
76.7k
  case 0x67: // regression
1247
80.9k
  case 0x69: // matrix
1248
85.4k
  case 0x9f: // sort third key
1249
89.8k
  case 0xa0: // sort four key
1250
94.4k
  case 0xa1: // sort fifth key
1251
98.7k
  case 0xb7: // solve for
1252
106k
  case 0xf8: // left border
1253
115k
  case 0xf9: // top border
1254
127k
  case 0x10d: //compatible slide show
1255
130k
  case 0x2bf: // X axis label
1256
132k
  case 0x2c0: // Z axis label
1257
135k
  case 0x2c1: // legend
1258
138k
  case 0x2c6: // serie data
1259
140k
  case 0x2c7: // serie legend
1260
140k
    readBlockList(stream);
1261
140k
    isParsed=true;
1262
140k
    break;
1263
  // checkme this nodes appear two times, even/odd page ?
1264
10.5k
  case 0x25: // footer
1265
21.8k
  case 0x26: // header
1266
21.8k
    readHeaderFooter(stream, id==0x26);
1267
21.8k
    isParsed = true;
1268
21.8k
    break;
1269
10.0k
  case 0x27: // print setup
1270
10.0k
    if (sz<1) break;
1271
9.63k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1272
9.63k
    val=int(libwps::readU8(input));
1273
9.63k
    if (val+1<sz) break;
1274
    // now data to send to the printer
1275
8.84k
    break;
1276
12.8k
  case 0x28: // print margin: one by spreadsheet
1277
12.8k
    if (sz!=12) break;
1278
8.45k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1279
8.45k
    f << "margs=[";
1280
42.2k
    for (int i=0; i<4; ++i)   // LRTB
1281
33.8k
      f << float(libwps::read16(input))/20.f << ",";
1282
8.45k
    f << "],";
1283
8.45k
    f << "hf[height]=[";
1284
25.3k
    for (int i=0; i<2; ++i)   // header, footer height
1285
16.9k
      f << float(libwps::read16(input))/20.f << ",";
1286
8.45k
    f << "],";
1287
8.45k
    isParsed=needWriteInAscii=true;
1288
8.45k
    break;
1289
1290
27.9k
  case 0x4b:
1291
27.9k
    m_state->m_isEncrypted=true;
1292
27.9k
    if (sz==20)
1293
27.7k
    {
1294
27.7k
      m_state->m_isEncrypted=true;
1295
27.7k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1296
27.7k
      uint16_t fileKey(libwps::readU16(input));
1297
27.7k
      f << "pass=" << std::hex << fileKey << std::dec << ",";
1298
27.7k
      f << "len=" << int(libwps::readU16(input)) << ",";
1299
27.7k
      isParsed = needWriteInAscii = true;
1300
27.7k
      std::vector<uint8_t> keys;
1301
27.7k
      keys.resize(16);
1302
444k
      for (auto &k : keys) k=uint8_t(libwps::readU8(input));
1303
      // to check users password:
1304
      //   call libwps::encodeLotusPassword(m_state->m_password, key, lotusKeys, someDefValues);
1305
      //   and check if  int16_t(key<<8|key>>8)==fileKey
1306
27.7k
      if (!m_state->m_isDecoded)
1307
6.18k
      {
1308
6.18k
        auto newInput=decodeStream(input, keys);
1309
6.18k
        if (newInput)
1310
6.18k
        {
1311
          // let's replace the current input by the decoded input
1312
6.18k
          m_state->m_isDecoded=true;
1313
6.18k
          stream->m_input=newInput;
1314
6.18k
          stream->m_ascii.setStream(newInput);
1315
6.18k
        }
1316
6.18k
      }
1317
27.7k
    }
1318
27.9k
    if (!m_state->m_isDecoded)
1319
28
    {
1320
28
      WPS_DEBUG_MSG(("QuattroParser::parse: can not decode the file\n"));
1321
28
    }
1322
27.9k
    break;
1323
9.35k
  case 0x96:
1324
9.35k
    readCellPosition(stream);
1325
9.35k
    isParsed=true;
1326
9.35k
    break;
1327
17.1k
  case 0x97: // name use to refer to some external spreadsheet
1328
18.6k
  case 0x98: // external name
1329
18.6k
    readExternalData(stream);
1330
18.6k
    isParsed=true;
1331
18.6k
    break;
1332
1333
42.3k
  case 0xca:
1334
48.4k
  case 0xcb:
1335
48.4k
    isParsed=m_spreadsheetParser->readBeginEndSheet(stream, m_state->m_actualSheet);
1336
48.4k
    break;
1337
16.0k
  case 0xcc:
1338
16.0k
    isParsed=m_spreadsheetParser->readSheetName(stream);
1339
16.0k
    break;
1340
364k
  case 0xce:
1341
364k
    ok = m_spreadsheetParser->readCellStyle(stream);
1342
364k
    isParsed = true;
1343
364k
    break;
1344
170k
  case 0xcf:
1345
201k
  case 0xfc:
1346
207k
  case 0x110:
1347
207k
    isParsed=readFontDef(stream);
1348
207k
    break;
1349
50.1k
  case 0xd0:
1350
50.1k
    isParsed=readStyleName(stream);
1351
50.1k
    break;
1352
8.99k
  case 0xd1:
1353
8.99k
    isParsed=readPaneAttribute(stream);
1354
8.99k
    break;
1355
100k
  case 0xd6:
1356
102k
  case 0xd7:
1357
102k
    isParsed = m_spreadsheetParser->readRowSize(stream);
1358
102k
    break;
1359
47.2k
  case 0xd8:
1360
47.7k
  case 0xd9:
1361
47.7k
    isParsed = m_spreadsheetParser->readColumnSize(stream);
1362
47.7k
    break;
1363
4.53k
  case 0xd2: // default row height
1364
5.98k
  case 0xd3:
1365
9.82k
  case 0xd4: // default col width
1366
10.1k
  case 0xd5:
1367
10.1k
    isParsed=m_spreadsheetParser->readColumnRowDefaultSize(stream);
1368
10.1k
    break;
1369
15.6k
  case 0xda:
1370
16.7k
  case 0xdb:
1371
16.7k
    if (sz!=3 && sz!=4)
1372
1.50k
    {
1373
1.50k
      f << "##";
1374
1.50k
      break;
1375
1.50k
    }
1376
15.2k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1377
15.2k
    f << "row=" << libwps::readU16(input) << ",";
1378
15.2k
    f << "F" << int(libwps::readU8(input)) << ",";
1379
15.2k
    if (sz==4)
1380
10.4k
    {
1381
10.4k
      val=int(libwps::readU8(input)); // 0, 5b, da
1382
10.4k
      if (val) f << "f0=" << std::hex << val << std::dec << ",";
1383
10.4k
    }
1384
15.2k
    break;
1385
19.1k
  case 0xdc:
1386
19.4k
  case 0xdd:
1387
20.0k
  case 0xde:
1388
20.9k
  case 0xdf:
1389
20.9k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1390
20.9k
    f << "hidden=[";
1391
349k
    for (long i=0; i<sz; ++i)
1392
328k
    {
1393
328k
      val=int(libwps::readU8(input));
1394
328k
      if (val==0) continue;
1395
2.24M
      for (int d=0, b=1; d<8; ++d, b<<=1)   // check byte order
1396
1.99M
      {
1397
1.99M
        if (val&b) f << 8*i+d << ",";
1398
1.99M
      }
1399
249k
    }
1400
20.9k
    f << "],";
1401
20.9k
    break;
1402
348
  case 0xe3:
1403
15.3k
  case 0xe4:
1404
213k
  case 0xe5:
1405
229k
  case 0xe6: // user format, FIXME: parse me and use me
1406
229k
  {
1407
229k
    if (sz<3) break;
1408
227k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1409
227k
    val=int(libwps::readU16(input));
1410
227k
    if (id==0xe3)
1411
267
      f << "sheet" << (val&0xff) << "=>" << (val>>8) << ",";
1412
226k
    else
1413
226k
      f << "id=" << val << ",";
1414
227k
    librevenge::RVNGString text;
1415
227k
    if (!readCString(stream, text,sz-2))
1416
0
      f << "###";
1417
227k
    else
1418
227k
    {
1419
227k
      if (id==0xe4 || id==0xe5)
1420
212k
        m_spreadsheetParser->addDLLIdName(val, text, id==0xe4);
1421
14.2k
      else if (id==0xe6)
1422
13.9k
        m_spreadsheetParser->addUserFormat(val, text);
1423
227k
      f << text.cstr() << ",";
1424
227k
    }
1425
227k
    break;
1426
229k
  }
1427
7.30k
  case 0xe8:
1428
7.30k
    isParsed=readColorList(stream);
1429
7.30k
    break;
1430
1.52k
  case 0xed:
1431
5.38k
  case 0x259:
1432
5.52k
  case 0x25e:
1433
5.75k
  case 0x261:
1434
5.78k
  case 0x26b:
1435
5.86k
  case 0x26c:
1436
9.47k
  case 0x2c8:
1437
9.47k
  {
1438
9.47k
    if (sz<1) break;
1439
5.79k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1440
5.79k
    librevenge::RVNGString text;
1441
5.79k
    if (!readCString(stream, text,sz))
1442
0
      f << "###";
1443
5.79k
    else
1444
5.79k
      f << text.cstr() << ",";
1445
5.79k
    break;
1446
9.47k
  }
1447
  // case 0xfe: some counter
1448
12.5k
  case 0x103:
1449
12.5k
    isParsed=readOptimizer(stream);
1450
12.5k
    break;
1451
  // 0x104: optimizer constraint: readme
1452
12.1k
  case 0x105:
1453
17.9k
  case 0x106:
1454
17.9k
    isParsed=m_spreadsheetParser->readRowRangeSize(stream);
1455
17.9k
    break;
1456
57.5k
  case 0x107: // never seems
1457
58.7k
  case 0x108:
1458
58.7k
    if (sz!=5)
1459
58.7k
    {
1460
58.7k
      f << "##";
1461
58.7k
      break;
1462
58.7k
    }
1463
55
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1464
55
    f << "rows=" << libwps::readU16(input) << ",";
1465
55
    f << "x" << libwps::readU16(input) << ",";
1466
55
    f << "F" << int(libwps::readU8(input)) << ",";
1467
55
    break;
1468
7.60k
  case 0x10c: // draft margin: never seens
1469
7.60k
    if (sz!=12) break;
1470
7.45k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1471
7.45k
    f << "margs=[";
1472
37.2k
    for (int i=0; i<4; ++i)   // LRTB
1473
29.8k
      f << float(libwps::read16(input))/20.f << ",";
1474
7.45k
    f << "],";
1475
7.45k
    f << "height=" << float(libwps::read16(input))/20.f << ",";
1476
7.45k
    f << "units=" << libwps::read16(input) << ","; // 0: char, 1: inches, 2: centimters
1477
7.45k
    isParsed=needWriteInAscii=true;
1478
7.45k
    break;
1479
4.31k
  case 0x12f:
1480
4.31k
    isParsed=readQueryCommand(stream);
1481
4.31k
    break;
1482
442
  case 0x134:
1483
442
  {
1484
442
    if (sz!=4)
1485
210
    {
1486
210
      f << "###";
1487
210
      break;
1488
210
    }
1489
232
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1490
232
    unsigned char colors[4];
1491
928
    for (auto &c : colors) c=static_cast<unsigned char>(libwps::readU8(input));
1492
232
    f << WPSColor(colors[0],colors[1],colors[2]) << ",";
1493
232
    break;
1494
442
  }
1495
2.13k
  case 0x135:
1496
2.13k
  {
1497
2.13k
    if (sz!=4)
1498
2.05k
    {
1499
2.05k
      f << "###";
1500
2.05k
      break;
1501
2.05k
    }
1502
83
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1503
83
    int values[2];
1504
166
    for (auto &v : values) v=int(libwps::readU16(input));
1505
83
    if (values[0]==100)
1506
43
      f << values[1] << "%,";
1507
40
    else if (values[0]!=1 || values[1]!=1)
1508
40
      f << values[1] << "/" << values[0] << ",";
1509
83
    break;
1510
2.13k
  }
1511
870
  case 0x193:
1512
870
  {
1513
870
    if (sz!=6)
1514
82
    {
1515
82
      f << "###";
1516
82
      break;
1517
82
    }
1518
788
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1519
788
    f << "size=" << libwps::readU16(input) << "x";
1520
788
    f << libwps::readU16(input) << ",";
1521
788
    f << "state=" << libwps::readU16(input) << ","; // 0:normal, 1:max, 2:min, 3:hidden
1522
788
    break;
1523
870
  }
1524
1.35k
  case 0x194:
1525
1.35k
  {
1526
1.35k
    if (sz!=4)
1527
230
    {
1528
230
      f << "###";
1529
230
      break;
1530
230
    }
1531
1.12k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1532
1.12k
    f << "pos=" << libwps::read16(input) << "x";
1533
1.12k
    f << libwps::read16(input) << ",";
1534
1.12k
    break;
1535
1.35k
  }
1536
1.21k
  case 0x195:
1537
1.21k
  {
1538
1.21k
    if (sz!=6)
1539
279
    {
1540
279
      f << "###";
1541
279
      break;
1542
279
    }
1543
938
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1544
938
    f << "type=" << libwps::readU16(input) << ","; // 0: no split, 1: horizontal, 2:vertical
1545
938
    f << "split=" << libwps::readU16(input) << "%,";
1546
938
    f << libwps::readU16(input) << "%],";
1547
938
    break;
1548
1.21k
  }
1549
3.63k
  case 0x197: // one by sheet
1550
4.30k
  case 0x198:
1551
4.30k
    isParsed=m_spreadsheetParser->readViewInfo(stream);
1552
4.30k
    break;
1553
1.23k
  case 0x19c:
1554
1.23k
  {
1555
1.23k
    if (sz!=8)
1556
229
    {
1557
229
      f << "###";
1558
229
      break;
1559
229
    }
1560
1.00k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1561
5.02k
    for (int i=0; i<4; ++i)   // f0: group mode, f1: hori bar vis, f2: vert bar vis, f3: vis tabs
1562
4.01k
    {
1563
4.01k
      val=int(libwps::readU16(input));
1564
4.01k
      if (val!=1) f << "f" << i << "=" << val << ",";
1565
4.01k
    }
1566
1.00k
    break;
1567
1.23k
  }
1568
539
  case 0x267:
1569
539
    if (sz!=2)
1570
429
    {
1571
429
      f << "###";
1572
429
      break;
1573
429
    }
1574
110
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1575
110
    val=int(libwps::readU16(input));
1576
110
    f << "ident=" << (val&0xff) << ",";
1577
110
    if (val&0x100) f << "no[master],";
1578
110
    if (val&0x1000) f << "skip[slide],";
1579
110
    break;
1580
5.30k
  case 0x2db: // find with size 0 or 2
1581
5.30k
    if (sz==0) break;
1582
2.52k
    if (sz%2)
1583
111
    {
1584
111
      f << "###";
1585
111
      break;
1586
111
    }
1587
2.41k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1588
71.6k
    for (long i=0; i<sz; ++i)
1589
69.2k
    {
1590
69.2k
      val=int(libwps::readU8(input));
1591
69.2k
      if (val) // small number 0-c
1592
56.4k
        f << "f" << i << "=" << std::hex << val << std::dec << ",";
1593
69.2k
    }
1594
2.41k
    break;
1595
3.66k
  case 0x2dc:
1596
3.66k
    isParsed=readSerieExtension(stream);
1597
3.66k
    break;
1598
24.1k
  case 0x321:
1599
29.8k
  case 0x322:
1600
29.8k
    isParsed=m_graphParser->readBeginEnd(stream, m_state->m_actualSheet);
1601
29.8k
    break;
1602
  // case 0x324: a list of XX:20?: seems relative to graph/chart
1603
5.76k
  case 0x33e: // oval
1604
5.76k
    isParsed = m_graphParser->readRect(stream);
1605
5.76k
    break;
1606
6.01k
  case 0x341: // maybe chart ?
1607
6.01k
    isParsed = readZone341(stream);
1608
6.01k
    break;
1609
2.18k
  case 0x335:
1610
2.35k
  case 0x337:
1611
5.42k
  case 0x33f:
1612
5.55k
  case 0x342:
1613
5.87k
  case 0x343:
1614
5.92k
  case 0x345:
1615
6.21k
  case 0x349:
1616
6.69k
  case 0x34a:
1617
6.99k
  case 0x34e:
1618
7.40k
  case 0x34f:
1619
7.62k
  case 0x351:
1620
8.04k
  case 0x35d:
1621
8.45k
  case 0x36d:
1622
8.45k
    isParsed = m_graphParser->readDialogUnknown(stream);
1623
8.45k
    break;
1624
166k
  case 0x35a:
1625
166k
    isParsed = m_graphParser->readLine(stream);
1626
166k
    break;
1627
581
  case 0x35b: // polygon
1628
6.68k
  case 0x35c: // polyline
1629
6.81k
  case 0x37c: // free polyline
1630
11.7k
  case 0x388: // free polygon
1631
11.7k
    isParsed = m_graphParser->readPolygon(stream);
1632
11.7k
    break;
1633
4.27k
  case 0x35e:
1634
4.27k
    isParsed = m_graphParser->readDialog(stream);
1635
4.27k
    break;
1636
1.23k
  case 0x364:
1637
1.23k
    isParsed = m_graphParser->readRect(stream);
1638
1.23k
    break;
1639
18.1k
  case 0x36f:
1640
18.1k
    isParsed = m_graphParser->readTextBox(stream);
1641
18.1k
    break;
1642
431
  case 0x379: // round rect
1643
431
    isParsed = m_graphParser->readRect(stream);
1644
431
    break;
1645
904
  case 0x37b: // arrow
1646
904
    isParsed = m_graphParser->readLine(stream);
1647
904
    break;
1648
85.8k
  case 0x381: // frame wb2
1649
85.8k
    isParsed = m_graphParser->readFrameOLE(stream);
1650
85.8k
    break;
1651
4.70k
  case 0x382: // only wb2?
1652
4.70k
    isParsed = m_graphParser->readImage(stream);
1653
4.70k
    break;
1654
8.45k
  case 0x383: // only wb2?
1655
8.45k
    isParsed = m_graphParser->readBitmap(stream);
1656
8.45k
    break;
1657
23.0k
  case 0x384:
1658
23.0k
    isParsed = m_graphParser->readChart(stream);
1659
23.0k
    break;
1660
15.9k
  case 0x385:
1661
15.9k
    isParsed = m_graphParser->readFrame(stream);
1662
15.9k
    break;
1663
15.7k
  case 0x386:
1664
15.7k
    isParsed = m_graphParser->readButton(stream);
1665
15.7k
    break;
1666
12.0k
  case 0x38b:
1667
12.0k
    isParsed = m_graphParser->readOLEData(stream);
1668
12.0k
    break;
1669
1670
31.0k
  case 0x4d3: // wb2 and wb3
1671
31.0k
    isParsed = m_graphParser->readShape(stream);
1672
31.0k
    break;
1673
718k
  default:
1674
718k
    break;
1675
7.00M
  }
1676
1677
7.00M
  if (!ok)
1678
3.59k
  {
1679
3.59k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1680
3.59k
    return false;
1681
3.59k
  }
1682
7.00M
  if (isParsed)
1683
2.28M
  {
1684
2.28M
    if (needWriteInAscii)
1685
397k
    {
1686
397k
      ascFile.addPos(pos);
1687
397k
      ascFile.addNote(f.str().c_str());
1688
397k
    }
1689
2.28M
    input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
1690
2.28M
    return true;
1691
2.28M
  }
1692
1693
4.71M
  if (sz && input->tell()!=pos && input->tell()!=pos+4+sz)
1694
24.4k
    ascFile.addDelimiter(input->tell(),'|');
1695
4.71M
  input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
1696
4.71M
  ascFile.addPos(pos);
1697
4.71M
  ascFile.addNote(f.str().c_str());
1698
4.71M
  return true;
1699
7.00M
}
1700
1701
////////////////////////////////////////////////////////////
1702
//   generic
1703
////////////////////////////////////////////////////////////
1704
1705
bool QuattroParser::readCString(std::shared_ptr<WPSStream> stream, librevenge::RVNGString &string, long maxSize)
1706
835k
{
1707
835k
  RVNGInputStreamPtr input = stream->m_input;
1708
835k
  long pos = input->tell();
1709
835k
  string.clear();
1710
835k
  if (!stream->checkFilePosition(pos+maxSize))
1711
3
  {
1712
3
    WPS_DEBUG_MSG(("QuattroParser::readCString: string's size seems bad\n"));
1713
3
    return false;
1714
3
  }
1715
835k
  std::string text;
1716
7.64M
  for (long i=0; i<maxSize; ++i)
1717
7.37M
  {
1718
7.37M
    auto c = char(libwps::readU8(input));
1719
7.37M
    if (c == '\0') break;
1720
6.81M
    text.push_back(c);
1721
6.81M
  }
1722
835k
  if (!text.empty())
1723
672k
    string=libwps_tools_win::Font::unicodeString(text, getDefaultFontType());
1724
835k
  return true;
1725
835k
}
1726
1727
bool QuattroParser::readFieldName(std::shared_ptr<WPSStream> stream)
1728
77.4k
{
1729
77.4k
  RVNGInputStreamPtr input = stream->m_input;
1730
77.4k
  libwps::DebugFile &ascFile=stream->m_ascii;
1731
77.4k
  libwps::DebugStream f;
1732
1733
77.4k
  long pos = input->tell();
1734
77.4k
  auto type = long(libwps::readU16(input)&0x7fff);
1735
77.4k
  if (type != 0xb)
1736
0
  {
1737
0
    WPS_DEBUG_MSG(("QuattroParser::readFieldName: not a zoneB type\n"));
1738
0
    return false;
1739
0
  }
1740
77.4k
  auto sz = long(libwps::readU16(input));
1741
77.4k
  long endPos=pos+4+sz;
1742
77.4k
  if (sz < 4)
1743
1.48k
  {
1744
1.48k
    WPS_DEBUG_MSG(("QuattroParser::readFieldName: size seems bad\n"));
1745
1.48k
    f << "###";
1746
1.48k
    ascFile.addPos(pos);
1747
1.48k
    ascFile.addNote(f.str().c_str());
1748
1.48k
    return true;
1749
1.48k
  }
1750
75.9k
  auto id=int(libwps::readU16(input));
1751
75.9k
  f << "id=" << id << ",";
1752
75.9k
  auto val=int(libwps::readU8(input)); // always 1?
1753
75.9k
  bool hasRef=(val&1);
1754
75.9k
  if ((val&1)==0) f << "no[ref],";
1755
75.9k
  if (val&2) f << "deleted,";
1756
  // val&4 must be zero, other reserved
1757
75.9k
  librevenge::RVNGString name;
1758
75.9k
  auto sSz=int(libwps::readU8(input));
1759
75.9k
  if (4+sSz+(hasRef ? 6 : 0)>sz || !readCString(stream,name,sSz))
1760
4.58k
  {
1761
4.58k
    f << "##sSz,";
1762
4.58k
    ascFile.addPos(pos);
1763
4.58k
    ascFile.addNote(f.str().c_str());
1764
4.58k
    return true;
1765
4.58k
  }
1766
71.3k
  else if (!name.empty())
1767
2.36k
    f << name.cstr() << ',';
1768
71.3k
  input->seek(pos+4+4+sSz, librevenge::RVNG_SEEK_SET);
1769
71.3k
  if (!hasRef)
1770
7.82k
  {
1771
7.82k
    ascFile.addPos(pos);
1772
7.82k
    ascFile.addNote(f.str().c_str());
1773
7.82k
    return true;
1774
7.82k
  }
1775
63.5k
  QuattroFormulaInternal::CellReference instr;
1776
63.5k
  if (!m_spreadsheetParser->readCellReference(stream, endPos, instr))
1777
23.7k
  {
1778
23.7k
    WPS_DEBUG_MSG(("QuattroParser::readFieldName: can not read some reference\n"));
1779
23.7k
    f << "###";
1780
23.7k
    ascFile.addPos(pos);
1781
23.7k
    ascFile.addNote(f.str().c_str());
1782
23.7k
    return true;
1783
23.7k
  }
1784
39.7k
  else if (!instr.empty())
1785
35.1k
  {
1786
35.1k
    f << instr;
1787
35.1k
    if (m_state->m_idToFieldMap.find(id)!=m_state->m_idToFieldMap.end())
1788
32.9k
    {
1789
32.9k
      WPS_DEBUG_MSG(("QuattroParser::readFieldName: oops a field with id=%d already exists\n", id));
1790
32.9k
    }
1791
2.18k
    else
1792
2.18k
      m_state->m_idToFieldMap[id]=std::pair<librevenge::RVNGString,QuattroFormulaInternal::CellReference>(name, instr);
1793
35.1k
  }
1794
39.7k
  ascFile.addPos(pos);
1795
39.7k
  ascFile.addNote(f.str().c_str());
1796
39.7k
  return true;
1797
63.5k
}
1798
1799
bool QuattroParser::readExternalData(std::shared_ptr<WPSStream> stream)
1800
18.6k
{
1801
18.6k
  RVNGInputStreamPtr input = stream->m_input;
1802
18.6k
  libwps::DebugFile &ascFile=stream->m_ascii;
1803
18.6k
  libwps::DebugStream f;
1804
18.6k
  long pos = input->tell();
1805
18.6k
  auto type = int(libwps::readU16(input)&0x7fff);
1806
1807
18.6k
  if (type != 0x97 && type != 0x98)
1808
0
  {
1809
0
    WPS_DEBUG_MSG(("QuattroParser::readExternalData: not a font zone\n"));
1810
0
    return false;
1811
0
  }
1812
18.6k
  auto sz = long(libwps::readU16(input));
1813
18.6k
  if (sz<3)
1814
2.47k
  {
1815
2.47k
    WPS_DEBUG_MSG(("QuattroParser::readExternalData: seems very short\n"));
1816
2.47k
    f << "###";
1817
2.47k
    ascFile.addPos(pos);
1818
2.47k
    ascFile.addNote(f.str().c_str());
1819
2.47k
    return true;
1820
2.47k
  }
1821
16.2k
  auto id=int(libwps::readU16(input));
1822
16.2k
  f << "id=" << id << ",";
1823
16.2k
  librevenge::RVNGString name;
1824
16.2k
  auto &map=type==0x98? m_state->m_idToExternalNameMap : m_state->m_idToExternalFileMap;
1825
16.2k
  if (!readCString(stream,name,sz-2))
1826
0
  {
1827
0
    f << "##name,";
1828
0
  }
1829
16.2k
  else if (map.find(id)!=map.end())
1830
8.79k
  {
1831
8.79k
    WPS_DEBUG_MSG(("QuattroParser::readExternalData: id=%d already found\n", id));
1832
8.79k
    f << "##duplicated,";
1833
8.79k
  }
1834
7.42k
  else if (!name.empty() || type==0x97) // external[file]=="" means current
1835
6.89k
    map[id]=name;
1836
16.2k
  if (!name.empty())
1837
9.99k
    f << name.cstr() << ',';
1838
  /* if name not empty,
1839
     0x97 is followed by first page/second page (or 00000000e97f9409)
1840
     0x98 is followed by index of owner + a potential cell ref?
1841
   */
1842
16.2k
  if (input->tell()!=pos+4+sz) ascFile.addDelimiter(input->tell(),'|');
1843
16.2k
  ascFile.addPos(pos);
1844
16.2k
  ascFile.addNote(f.str().c_str());
1845
16.2k
  return true;
1846
18.6k
}
1847
1848
bool QuattroParser::readFontDef(std::shared_ptr<WPSStream> stream)
1849
207k
{
1850
207k
  RVNGInputStreamPtr input = stream->m_input;
1851
207k
  libwps::DebugFile &ascFile=stream->m_ascii;
1852
207k
  libwps::DebugStream f;
1853
207k
  long pos = input->tell();
1854
207k
  auto type = int(libwps::readU16(input)&0x7fff);
1855
1856
207k
  if (type != 0xcf && type != 0xfc && type!=0x110)
1857
0
  {
1858
0
    WPS_DEBUG_MSG(("QuattroParser::readFontDef: not a font zone\n"));
1859
0
    return false;
1860
0
  }
1861
207k
  auto sz = long(libwps::readU16(input));
1862
207k
  if (type==0xcf)
1863
170k
    f << "[F" << m_state->m_fontsList.size() << "],";
1864
207k
  QuattroParserInternal::Font font(getDefaultFontType());
1865
207k
  if (sz!=0x24)
1866
30.5k
  {
1867
30.5k
    WPS_DEBUG_MSG(("QuattroParser::readFontDef: seems very bad\n"));
1868
30.5k
    f << "###";
1869
30.5k
    ascFile.addPos(pos);
1870
30.5k
    ascFile.addNote(f.str().c_str());
1871
30.5k
    m_state->m_fontsList.push_back(font);
1872
30.5k
    return true;
1873
30.5k
  }
1874
177k
  auto fSize = int(libwps::readU16(input));
1875
177k
  if (fSize >= 1 && fSize <= 50) // in fact maximum is 500
1876
44.6k
    font.m_size=double(fSize);
1877
132k
  else
1878
132k
    f << "###fSize=" << fSize << ",";
1879
177k
  auto flags = int(libwps::readU16(input));
1880
177k
  uint32_t attributes = 0;
1881
177k
  if (flags & 1) attributes |= WPS_BOLD_BIT;
1882
177k
  if (flags & 2) attributes |= WPS_ITALICS_BIT;
1883
177k
  if (flags & 4) attributes |= WPS_UNDERLINE_BIT;
1884
177k
  if (flags & 8) attributes |= WPS_SUBSCRIPT_BIT; // reserved
1885
177k
  if (flags & 0x10) attributes |= WPS_SUPERSCRIPT_BIT; // reserved
1886
177k
  if (flags & 0x20) attributes |= WPS_STRIKEOUT_BIT;
1887
177k
  if (flags & 0x40) attributes |= WPS_DOUBLE_UNDERLINE_BIT; // reserved
1888
177k
  if (flags & 0x80) attributes |= WPS_OUTLINE_BIT; // reserved
1889
177k
  if (flags & 0x100) attributes |= WPS_SHADOW_BIT; // reserved
1890
1891
177k
  font.m_attributes=attributes;
1892
177k
  flags &= 0xfe00;
1893
177k
  if (flags)
1894
55.2k
    f << "##fl=" << std::hex << flags << std::dec << ",";
1895
177k
  librevenge::RVNGString name;
1896
177k
  if (!readCString(stream,name,32))
1897
0
  {
1898
0
    f << "##name,";
1899
0
  }
1900
177k
  else
1901
177k
    font.m_name=name;
1902
177k
  if (type==0xcf)
1903
165k
    m_state->m_fontsList.push_back(font);
1904
1905
177k
  f << font;
1906
177k
  ascFile.addPos(pos);
1907
177k
  ascFile.addNote(f.str().c_str());
1908
177k
  return true;
1909
207k
}
1910
1911
bool QuattroParser::readColorList(std::shared_ptr<WPSStream> stream)
1912
7.30k
{
1913
7.30k
  RVNGInputStreamPtr input = stream->m_input;
1914
7.30k
  libwps::DebugFile &ascFile=stream->m_ascii;
1915
7.30k
  libwps::DebugStream f;
1916
7.30k
  long pos = input->tell();
1917
7.30k
  auto type = int(libwps::readU16(input)&0x7fff);
1918
1919
7.30k
  if (type != 0xe8)
1920
0
  {
1921
0
    WPS_DEBUG_MSG(("QuattroParser::readColorList: not a font zone\n"));
1922
0
    return false;
1923
0
  }
1924
7.30k
  auto sz = long(libwps::readU16(input));
1925
7.30k
  if (sz<0x40 || (sz%4))
1926
2.06k
  {
1927
2.06k
    WPS_DEBUG_MSG(("QuattroParser::readColorList: seems very bad\n"));
1928
2.06k
    f << "###";
1929
2.06k
    ascFile.addPos(pos);
1930
2.06k
    ascFile.addNote(f.str().c_str());
1931
2.06k
    return true;
1932
2.06k
  }
1933
5.24k
  auto N=size_t(sz/4);
1934
5.24k
  m_state->m_colorsList.resize(N);
1935
5.24k
  for (auto &c: m_state->m_colorsList)
1936
269k
  {
1937
269k
    uint8_t cols[4];
1938
1.07M
    for (auto &co : cols) co=uint8_t(libwps::readU8(input));
1939
269k
    c=WPSColor(cols[0],cols[1],cols[2],cols[3]);
1940
269k
    f << c << ",";
1941
269k
  }
1942
5.24k
  ascFile.addPos(pos);
1943
5.24k
  ascFile.addNote(f.str().c_str());
1944
5.24k
  return true;
1945
7.30k
}
1946
1947
bool QuattroParser::readStyleName(std::shared_ptr<WPSStream> stream)
1948
50.1k
{
1949
50.1k
  RVNGInputStreamPtr input = stream->m_input;
1950
50.1k
  libwps::DebugFile &ascFile=stream->m_ascii;
1951
50.1k
  libwps::DebugStream f;
1952
50.1k
  long pos = input->tell();
1953
50.1k
  auto type = int(libwps::readU16(input))&0x7fff;
1954
1955
50.1k
  if (type != 0xd0)
1956
0
  {
1957
0
    WPS_DEBUG_MSG(("QuattroParser::readStyleName: not a font zone\n"));
1958
0
    return false;
1959
0
  }
1960
50.1k
  auto sz = long(libwps::readU16(input));
1961
50.1k
  if (sz<4)
1962
1.82k
  {
1963
1.82k
    WPS_DEBUG_MSG(("QuattroParser::readStyleName: seems very bad\n"));
1964
1.82k
    f << "###";
1965
1.82k
    ascFile.addPos(pos);
1966
1.82k
    ascFile.addNote(f.str().c_str());
1967
1.82k
    return true;
1968
1.82k
  }
1969
48.2k
  auto id=int(libwps::readU16(input)); // current style id
1970
48.2k
  f << "St" << id << ",";
1971
48.2k
  auto val=int(libwps::readU16(input));
1972
48.2k
  if ((val&0x3fff)!=id) f << "attrib[id]=" << val << ",";
1973
48.2k
  if (sz!=4)   // no name seems ok
1974
47.6k
  {
1975
47.6k
    librevenge::RVNGString name;
1976
47.6k
    if (!readCString(stream,name,sz-4))
1977
0
    {
1978
0
      f << "##name,";
1979
0
    }
1980
47.6k
    else if (!name.empty())
1981
47.2k
      f << name.cstr() << ",";
1982
47.6k
    if (input->tell()!=pos+4+sz)
1983
694
      ascFile.addDelimiter(input->tell(),'|');
1984
47.6k
  }
1985
48.2k
  ascFile.addPos(pos);
1986
48.2k
  ascFile.addNote(f.str().c_str());
1987
48.2k
  return true;
1988
50.1k
}
1989
1990
// ----------------------------------------------------------------------
1991
// Header/Footer
1992
// ----------------------------------------------------------------------
1993
void QuattroParser::sendHeaderFooter(bool header)
1994
3.84k
{
1995
3.84k
  if (!m_listener)
1996
0
  {
1997
0
    WPS_DEBUG_MSG(("QuattroParser::sendHeaderFooter: can not find the listener\n"));
1998
0
    return;
1999
0
  }
2000
2001
3.84k
  m_listener->setFont(m_state->getDefaultFont());
2002
3.84k
  auto const &text = header ? m_state->m_headerString : m_state->m_footerString;
2003
3.84k
  m_listener->insertUnicodeString(text);
2004
3.84k
}
2005
2006
bool QuattroParser::readHeaderFooter(std::shared_ptr<WPSStream> stream, bool header)
2007
21.8k
{
2008
21.8k
  RVNGInputStreamPtr input = stream->m_input;
2009
21.8k
  libwps::DebugFile &ascFile=stream->m_ascii;
2010
21.8k
  libwps::DebugStream f;
2011
21.8k
  long pos = input->tell();
2012
21.8k
  auto type = int(libwps::readU16(input)&0x7fff);
2013
21.8k
  if (type != 0x0026 && type != 0x0025)
2014
0
  {
2015
0
    WPS_DEBUG_MSG(("QuattroParser::readHeaderFooter: not a header/footer\n"));
2016
0
    return false;
2017
0
  }
2018
21.8k
  auto sz = long(libwps::readU16(input));
2019
21.8k
  long endPos = pos+4+sz;
2020
2021
21.8k
  librevenge::RVNGString text;
2022
21.8k
  if (!readCString(stream,text,sz))
2023
0
  {
2024
0
    f << "##sSz,";
2025
0
    ascFile.addPos(pos);
2026
0
    ascFile.addNote(f.str().c_str());
2027
0
    return true;
2028
0
  }
2029
21.8k
  if (!text.empty())
2030
7.42k
  {
2031
7.42k
    if (header)
2032
3.79k
      m_state->m_headerString=text;
2033
3.63k
    else
2034
3.63k
      m_state->m_footerString=text;
2035
7.42k
    f << text.cstr();
2036
7.42k
  }
2037
21.8k
  if (input->tell()!=endPos)
2038
1.92k
    ascFile.addDelimiter(input->tell(), '|');
2039
21.8k
  ascFile.addPos(pos);
2040
21.8k
  ascFile.addNote(f.str().c_str());
2041
2042
21.8k
  return true;
2043
21.8k
}
2044
2045
bool QuattroParser::readOptimizer(std::shared_ptr<WPSStream> stream)
2046
12.5k
{
2047
12.5k
  RVNGInputStreamPtr input = stream->m_input;
2048
12.5k
  libwps::DebugFile &ascFile=stream->m_ascii;
2049
12.5k
  libwps::DebugStream f;
2050
2051
12.5k
  long pos = input->tell();
2052
12.5k
  auto type = long(libwps::readU16(input)&0x7fff);
2053
12.5k
  auto sz = long(libwps::readU16(input));
2054
12.5k
  long endPos=pos+4+sz;
2055
12.5k
  if (type != 0x103)
2056
0
  {
2057
0
    WPS_DEBUG_MSG(("QuattroParser::readOptimizer: not an optimizer zone"));
2058
0
    return false;
2059
0
  }
2060
12.5k
  if (sz<84)
2061
3.78k
  {
2062
3.78k
    WPS_DEBUG_MSG(("QuattroParser::readOptimizer: seems very bad\n"));
2063
3.78k
    f << "###";
2064
3.78k
    ascFile.addPos(pos);
2065
3.78k
    ascFile.addNote(f.str().c_str());
2066
3.78k
    return true;
2067
3.78k
  }
2068
20.9k
  for (int i=0; i<2; ++i)
2069
15.2k
  {
2070
15.2k
    long actPos=input->tell();
2071
15.2k
    QuattroFormulaInternal::CellReference instr;
2072
15.2k
    if (!m_spreadsheetParser->readCellReference(stream, endPos, instr))
2073
3.12k
    {
2074
3.12k
      WPS_DEBUG_MSG(("QuattroParser::readOptimizer: can not read some reference\n"));
2075
3.12k
      f << "###";
2076
3.12k
      ascFile.addPos(pos);
2077
3.12k
      ascFile.addNote(f.str().c_str());
2078
3.12k
      return true;
2079
3.12k
    }
2080
12.1k
    else if (!instr.empty())
2081
4.49k
      f << "cell" << i << "=" << instr << ",";
2082
12.1k
    input->seek(actPos+10, librevenge::RVNG_SEEK_SET);
2083
12.1k
  }
2084
5.62k
  int val=int(libwps::readU16(input));
2085
5.62k
  if (val==1) f << "goal=min,";
2086
4.28k
  else if (val==2) f << "goal=max,";
2087
4.28k
  else if (val==3) f << "goal=targeted,";
2088
4.27k
  else if (val) f << "##goal=" << val << ",";
2089
5.62k
  double value;
2090
5.62k
  bool isNaN;
2091
5.62k
  long actPos=input->tell();
2092
5.62k
  if (libwps::readDouble8(input, value, isNaN))
2093
4.36k
    f << "reached=" << value << ",";
2094
1.25k
  else
2095
1.25k
  {
2096
1.25k
    f << "###reached,";
2097
1.25k
    input->seek(actPos+10, librevenge::RVNG_SEEK_SET);
2098
1.25k
  }
2099
2100
5.62k
  val=int(libwps::readU16(input));
2101
5.62k
  if (val==1) f << "quadratic,";
2102
5.58k
  else if (val) f << "##estimate=" << val << ",";
2103
5.62k
  val=int(libwps::readU16(input));
2104
5.62k
  if (val==1) f << "derivated=central,";
2105
5.62k
  else if (val) f << "##derivated=" << val << ",";
2106
5.62k
  val=int(libwps::readU16(input));
2107
5.62k
  if (val==1) f << "search=conjugate,";
2108
5.54k
  else if (val) f << "##search=" << val << ",";
2109
5.62k
  val=int(libwps::readU16(input));
2110
5.62k
  if (val==1) f << "linear,";
2111
5.61k
  else if (val) f << "##linear=" << val << ",";
2112
5.62k
  val=int(libwps::readU16(input));
2113
5.62k
  if (val==1) f << "show[result],";
2114
5.61k
  else if (val) f << "##show[result]=" << val << ",";
2115
2116
5.62k
  val=int(libwps::readU16(input));
2117
5.62k
  if (val!=100) f << "max[time]=" << val << ",";
2118
5.62k
  val=int(libwps::readU16(input));
2119
5.62k
  if (val!=100) f << "max[iteration]=" << val << ",";
2120
5.62k
  actPos=input->tell();
2121
5.62k
  if (libwps::readDouble8(input, value, isNaN))
2122
4.10k
    f << "precision=" << value << ",";
2123
1.51k
  else
2124
1.51k
  {
2125
1.51k
    f << "###precision,";
2126
1.51k
    input->seek(actPos+8, librevenge::RVNG_SEEK_SET);
2127
1.51k
  }
2128
16.4k
  for (int i=0; i<3; ++i)
2129
13.3k
  {
2130
13.3k
    actPos=input->tell();
2131
13.3k
    QuattroFormulaInternal::CellReference instr;
2132
13.3k
    if (!m_spreadsheetParser->readCellReference(stream, endPos, instr))
2133
2.54k
    {
2134
2.54k
      WPS_DEBUG_MSG(("QuattroParser::readOptimizer: can not read some reference\n"));
2135
2.54k
      f << "###";
2136
2.54k
      ascFile.addPos(pos);
2137
2.54k
      ascFile.addNote(f.str().c_str());
2138
2.54k
      return true;
2139
2.54k
    }
2140
10.7k
    else if (!instr.empty())
2141
6.44k
      f << "cell" << i+3 << "=" << instr << ",";
2142
10.7k
    input->seek(actPos+10, librevenge::RVNG_SEEK_SET);
2143
10.7k
  }
2144
3.07k
  input->seek(2, librevenge::RVNG_SEEK_CUR); // unused
2145
3.07k
  if (sz>=94)
2146
2.48k
  {
2147
2.48k
    actPos=input->tell();
2148
2.48k
    if (libwps::readDouble8(input, value, isNaN))
2149
1.70k
      f << "tolerance=" << value << ",";
2150
783
    else
2151
783
    {
2152
783
      f << "###tolerance,";
2153
783
      input->seek(actPos+8, librevenge::RVNG_SEEK_SET);
2154
783
    }
2155
2.48k
    val=int(libwps::readU16(input));
2156
2.48k
    if (val) f << "autoScale=" << val << ",";
2157
2.48k
  }
2158
3.07k
  if (input->tell()!=endPos)
2159
752
    ascFile.addDelimiter(input->tell(), '|');
2160
3.07k
  ascFile.addPos(pos);
2161
3.07k
  ascFile.addNote(f.str().c_str());
2162
2163
3.07k
  return true;
2164
5.62k
}
2165
2166
bool QuattroParser::readQueryCommand(std::shared_ptr<WPSStream> stream)
2167
4.31k
{
2168
4.31k
  RVNGInputStreamPtr input = stream->m_input;
2169
4.31k
  libwps::DebugFile &ascFile=stream->m_ascii;
2170
4.31k
  libwps::DebugStream f;
2171
2172
4.31k
  long pos = input->tell();
2173
4.31k
  auto type = long(libwps::readU16(input)&0x7fff);
2174
4.31k
  auto sz = long(libwps::readU16(input));
2175
4.31k
  long endPos=pos+4+sz;
2176
4.31k
  if (type != 0x12f)
2177
0
  {
2178
0
    WPS_DEBUG_MSG(("QuattroParser::readQueryCommand: not an queryCommand zone"));
2179
0
    return false;
2180
0
  }
2181
4.31k
  if (sz<22)
2182
500
  {
2183
500
    WPS_DEBUG_MSG(("QuattroParser::readQueryCommand: seems very bad\n"));
2184
500
    f << "###";
2185
500
    ascFile.addPos(pos);
2186
500
    ascFile.addNote(f.str().c_str());
2187
500
    return true;
2188
500
  }
2189
7.59k
  for (int i=0; i<2; ++i)
2190
5.94k
  {
2191
5.94k
    long actPos=input->tell();
2192
5.94k
    QuattroFormulaInternal::CellReference instr;
2193
5.94k
    if (!m_spreadsheetParser->readCellReference(stream, endPos, instr))
2194
2.16k
    {
2195
2.16k
      WPS_DEBUG_MSG(("QuattroParser::readQueryCommand: can not read some reference\n"));
2196
2.16k
      f << "###";
2197
2.16k
      ascFile.addPos(pos);
2198
2.16k
      ascFile.addNote(f.str().c_str());
2199
2.16k
      return true;
2200
2.16k
    }
2201
3.78k
    else if (!instr.empty())
2202
84
      f << "cell" << i << "=" << instr << ",";
2203
3.78k
    input->seek(actPos+10, librevenge::RVNG_SEEK_SET);
2204
3.78k
  }
2205
1.65k
  int val=int(libwps::readU16(input));
2206
1.65k
  if (val) f << "id=" << val << ",";
2207
  // then a name?
2208
1.65k
  if (input->tell()!=endPos)
2209
1.65k
    ascFile.addDelimiter(input->tell(), '|');
2210
1.65k
  ascFile.addPos(pos);
2211
1.65k
  ascFile.addNote(f.str().c_str());
2212
2213
1.65k
  return true;
2214
3.81k
}
2215
2216
////////////////////////////////////////////////////////////
2217
//   Unknown
2218
////////////////////////////////////////////////////////////
2219
2220
bool QuattroParser::readBlockList(std::shared_ptr<WPSStream> stream)
2221
140k
{
2222
140k
  RVNGInputStreamPtr input = stream->m_input;
2223
140k
  libwps::DebugFile &ascFile=stream->m_ascii;
2224
140k
  libwps::DebugStream f;
2225
2226
140k
  long pos = input->tell();
2227
140k
  auto type = long(libwps::readU16(input)&0x7fff);
2228
140k
  int N=0;
2229
140k
  auto sz = long(libwps::readU16(input));
2230
140k
  long endPos=pos+4+sz;
2231
140k
  int extraSize=0;
2232
140k
  switch (type)
2233
140k
  {
2234
6.77k
  case 0x18: // table, extra value: 0,1,2: 0,1,2 free variables
2235
14.2k
  case 0x19: // query, extra value: 0-4: meaning none,find,extract,delete, unique
2236
14.2k
    N=3;
2237
14.2k
    extraSize=2;
2238
14.2k
    break;
2239
11.7k
  case 0x1a: // print block
2240
11.7k
    N=1;
2241
11.7k
    break;
2242
6.73k
  case 0x1b: // sort block
2243
6.73k
    N=1;
2244
6.73k
    extraSize=4; // f0=0(numbers first),1(labels first), f1=0(dictionnary),1(ascii) order
2245
6.73k
    break;
2246
4.68k
  case 0x1c: // fill range
2247
4.68k
    N=1;
2248
4.68k
    break;
2249
9.88k
  case 0x1d: // key
2250
16.5k
  case 0x23:
2251
21.0k
  case 0x9f:
2252
25.3k
  case 0xa0:
2253
29.9k
  case 0xa1:
2254
29.9k
    N=1;
2255
29.9k
    extraSize=2; // 0: ascending, 1: descending when sz==12
2256
29.9k
    break;
2257
14.0k
  case 0x20: // frequency
2258
18.3k
  case 0x66: // parse
2259
18.3k
    N=2;
2260
18.3k
    break;
2261
4.42k
  case 0x67: // regression
2262
4.42k
    N=3;
2263
4.42k
    extraSize=2; // 0: compute-y-intersect, 1: y-intersect is 0
2264
4.42k
    break;
2265
4.26k
  case 0x69: // matrix
2266
4.26k
    N=5;
2267
4.26k
    break;
2268
4.32k
  case 0xb7: // solve
2269
4.32k
    N=2;
2270
4.32k
    extraSize=18;
2271
4.32k
    break;
2272
8.10k
  case 0xf8: // print left
2273
16.6k
  case 0xf9: // print top
2274
16.6k
    N=1;
2275
16.6k
    break;
2276
11.7k
  case 0x10d: // show compatible
2277
11.7k
    N=1;
2278
11.7k
    break;
2279
3.04k
  case 0x2bf: // X label
2280
5.49k
  case 0x2c0: // Z label
2281
7.86k
  case 0x2c1: // legend
2282
10.7k
  case 0x2c6: // serie data
2283
13.7k
  case 0x2c7: // serie legend
2284
13.7k
    N=1;
2285
13.7k
    break;
2286
0
  default:
2287
0
    break;
2288
140k
  }
2289
140k
  bool fixedSize10=10*N+extraSize==sz;
2290
227k
  for (int i=0; i<N; ++i)
2291
171k
  {
2292
171k
    QuattroFormulaInternal::CellReference instr;
2293
171k
    long actPos=input->tell();
2294
171k
    if (!m_spreadsheetParser->readCellReference(stream, endPos, instr))
2295
84.6k
    {
2296
84.6k
      WPS_DEBUG_MSG(("QuattroParser::readBlockList: can not read a reference\n"));
2297
84.6k
      f << "###";
2298
84.6k
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
2299
84.6k
      ascFile.addPos(pos);
2300
84.6k
      ascFile.addNote(f.str().c_str());
2301
2302
84.6k
      return true;
2303
84.6k
    }
2304
86.6k
    if (!instr.empty())
2305
10.2k
      f << "cell" << i << "=" << instr << ",";
2306
86.6k
    if (fixedSize10)
2307
44.5k
      input->seek(actPos+10, librevenge::RVNG_SEEK_SET);
2308
86.6k
  }
2309
56.3k
  long remainSize=endPos-input->tell();
2310
56.3k
  if (type==0xb7 && (remainSize==2 || remainSize==18))
2311
1.57k
  {
2312
3.99k
    for (int i=0; i<(remainSize==2 ? 0 : 2); ++i)   // target, accuracy
2313
2.41k
    {
2314
2.41k
      long actPos=input->tell();
2315
2.41k
      double val;
2316
2.41k
      bool isNaN;
2317
2.41k
      if (libwps::readDouble8(input, val, isNaN))
2318
2.25k
        f << "f" << i << "=" << val << ",";
2319
160
      else
2320
160
        f << "###f" << i << ",";
2321
2.41k
      input->seek(actPos+8, librevenge::RVNG_SEEK_SET);
2322
2.41k
    }
2323
1.57k
    f << "max[iter]=" << libwps::readU16(input) << ",";
2324
1.57k
  }
2325
54.7k
  else if (remainSize!=extraSize)
2326
4.58k
  {
2327
4.58k
    ascFile.addDelimiter(input->tell(),'|');
2328
4.58k
    WPS_DEBUG_MSG(("QuattroParser::readBlockList: unexpected extra data\n"));
2329
4.58k
    f << "###";
2330
4.58k
  }
2331
50.1k
  else
2332
50.1k
  {
2333
69.5k
    for (int i=0; i<extraSize/2; ++i)
2334
19.3k
    {
2335
19.3k
      auto val=int(libwps::read16(input));
2336
19.3k
      if (val) f << "f" << i << "=" << val << ",";
2337
19.3k
    }
2338
50.1k
  }
2339
56.3k
  ascFile.addPos(pos);
2340
56.3k
  ascFile.addNote(f.str().c_str());
2341
2342
56.3k
  return true;
2343
140k
}
2344
2345
bool QuattroParser::readCellPosition(std::shared_ptr<WPSStream> stream)
2346
9.35k
{
2347
9.35k
  RVNGInputStreamPtr input = stream->m_input;
2348
9.35k
  libwps::DebugFile &ascFile=stream->m_ascii;
2349
9.35k
  libwps::DebugStream f;
2350
9.35k
  long pos = input->tell();
2351
9.35k
  auto type = int(libwps::readU16(input)&0x7fff);
2352
2353
9.35k
  if (type != 0x96)
2354
0
  {
2355
0
    WPS_DEBUG_MSG(("QuattroParser::readCellPosition: not a cell position zone\n"));
2356
0
    return false;
2357
0
  }
2358
9.35k
  auto sz = long(libwps::readU16(input));
2359
9.35k
  if (sz%6)
2360
665
  {
2361
665
    WPS_DEBUG_MSG(("QuattroParser::readCellPosition: size seems very bad\n"));
2362
665
    f << "###";
2363
665
    ascFile.addPos(pos);
2364
665
    ascFile.addNote(f.str().c_str());
2365
665
    return true;
2366
665
  }
2367
8.69k
  auto N=int(sz/6);
2368
78.6k
  for (int i=0; i<N; ++i)
2369
69.9k
  {
2370
69.9k
    int cellPos[3]; // col, rowMin, rowMax
2371
209k
    for (auto &c : cellPos) c=int(libwps::readU16(input));
2372
69.9k
    f <<  "C" << cellPos[0] << "[" << cellPos[1] << "->" << cellPos[2] << "],";
2373
69.9k
  }
2374
8.69k
  ascFile.addPos(pos);
2375
8.69k
  ascFile.addNote(f.str().c_str());
2376
8.69k
  return true;
2377
9.35k
}
2378
2379
bool QuattroParser::readPaneAttribute(std::shared_ptr<WPSStream> stream)
2380
8.99k
{
2381
8.99k
  RVNGInputStreamPtr input = stream->m_input;
2382
8.99k
  libwps::DebugFile &ascFile=stream->m_ascii;
2383
8.99k
  libwps::DebugStream f;
2384
8.99k
  long pos = input->tell();
2385
8.99k
  auto type = int(libwps::readU16(input)&0x7fff);
2386
2387
8.99k
  if (type != 0xd1)
2388
0
  {
2389
0
    WPS_DEBUG_MSG(("QuattroParser::readPaneAttribute: not a attribute zone\n"));
2390
0
    return false;
2391
0
  }
2392
8.99k
  auto sz = long(libwps::readU16(input));
2393
8.99k
  if (sz<30)
2394
430
  {
2395
430
    WPS_DEBUG_MSG(("QuattroParser::readPaneAttribute: size seems very bad\n"));
2396
430
    f << "###";
2397
430
    ascFile.addPos(pos);
2398
430
    ascFile.addNote(f.str().c_str());
2399
430
    return true;
2400
430
  }
2401
8.56k
  int val=int(libwps::readU8(input));
2402
8.56k
  if (val==0)
2403
76
    f << "diplay[no],";
2404
8.49k
  else if (val!=1)
2405
3.52k
    f << "##display=" << val << ",";
2406
8.56k
  val=int(libwps::readU8(input));
2407
8.56k
  WPSColor color;
2408
8.56k
  if (!getColor(val, color))
2409
2.97k
    f << "##lineColor=" << val << ",";
2410
5.58k
  else if (!color.isBlack())
2411
1.98k
    f << "lineColor=" << color << ",";
2412
8.56k
  val=int(libwps::readU16(input));
2413
8.56k
  switch (val)
2414
8.56k
  {
2415
170
  case 0:
2416
170
    f << "lab[align]=default,";
2417
170
    break;
2418
3.72k
  case 1:
2419
3.72k
    break;
2420
37
  case 2:
2421
37
    f << "lab[align]=center,";
2422
37
    break;
2423
766
  case 3:
2424
766
    f << "lab[align]=right,";
2425
766
    break;
2426
3.86k
  default:
2427
3.86k
    f << "##lab[align]=" << val << ",";
2428
3.86k
    break;
2429
8.56k
  }
2430
8.56k
  val=int(libwps::readU16(input));
2431
8.56k
  switch (val)
2432
8.56k
  {
2433
31
  case 0:
2434
31
    f << "number[align]=default,";
2435
31
    break;
2436
45
  case 1:
2437
45
    f << "number[align]=left,";
2438
45
    break;
2439
36
  case 2:
2440
36
    f << "number[align]=center,";
2441
36
    break;
2442
4.36k
  case 3:
2443
4.36k
    break;
2444
4.08k
  default:
2445
4.08k
    f << "##number[align]=" << val << ",";
2446
4.08k
    break;
2447
8.56k
  }
2448
8.56k
  val=int(libwps::readU16(input));
2449
8.56k
  if (val==1)
2450
41
  {
2451
41
    f << "has[cond],";
2452
123
    for (int i=0; i<2; ++i)
2453
82
    {
2454
82
      double value;
2455
82
      bool isNaN;
2456
82
      if (libwps::readDouble8(input, value, isNaN))
2457
76
        f << "cond" << i << "=" << value << ",";
2458
6
      else
2459
6
        f << "###cond" << i << ",";
2460
82
    }
2461
41
  }
2462
8.52k
  else if (val)
2463
2.86k
    f << "##has[cond]=" << val << ",";
2464
8.56k
  input->seek(pos+4+8+16, librevenge::RVNG_SEEK_SET);
2465
8.56k
  f << "colors=[";
2466
42.8k
  for (int i=0; i<4; ++i)   // color < cond0, normal, color> cond1, error
2467
34.2k
  {
2468
34.2k
    val=int(libwps::readU8(input));
2469
34.2k
    int const expected[]= {4,3,5,4};
2470
34.2k
    if (val==expected[i])
2471
19.0k
      f << "_,";
2472
15.1k
    else if (!getColor(val, color))
2473
13.4k
      f << "##" << val << ",";
2474
1.75k
    else
2475
1.75k
      f << color << ",";
2476
34.2k
  }
2477
8.56k
  f << "],";
2478
8.56k
  input->seek(2, librevenge::RVNG_SEEK_CUR); // reserved
2479
8.56k
  if (sz!=30) // find sometimes +4|8 bytes
2480
1.61k
    ascFile.addDelimiter(input->tell(),'|');
2481
8.56k
  ascFile.addPos(pos);
2482
8.56k
  ascFile.addNote(f.str().c_str());
2483
8.56k
  return true;
2484
8.56k
}
2485
2486
bool QuattroParser::readSerieExtension(std::shared_ptr<WPSStream> stream)
2487
3.66k
{
2488
3.66k
  RVNGInputStreamPtr input = stream->m_input;
2489
3.66k
  libwps::DebugFile &ascFile=stream->m_ascii;
2490
3.66k
  libwps::DebugStream f;
2491
3.66k
  long pos = input->tell();
2492
3.66k
  auto type = int(libwps::readU16(input)&0x7fff);
2493
2494
3.66k
  if (type != 0x2dc)
2495
0
  {
2496
0
    WPS_DEBUG_MSG(("QuattroParser::readSerieExtension: not a extension zone\n"));
2497
0
    return false;
2498
0
  }
2499
3.66k
  auto sz = long(libwps::readU16(input));
2500
3.66k
  long endPos=pos+4+sz;
2501
3.66k
  if (sz<6)
2502
338
  {
2503
338
    WPS_DEBUG_MSG(("QuattroParser::readSerieExtension: size seems very bad\n"));
2504
338
    f << "###";
2505
338
    ascFile.addPos(pos);
2506
338
    ascFile.addNote(f.str().c_str());
2507
338
    return true;
2508
338
  }
2509
3.33k
  int val=int(libwps::readU16(input));
2510
3.33k
  if (val==1)
2511
16
    f << "ysecondary,";
2512
3.31k
  else if (val)
2513
895
    f << "##ysecondaty=" << val << ",";
2514
3.33k
  val=int(libwps::readU16(input));
2515
3.33k
  if (val>=1 && val<5)
2516
22
  {
2517
22
    char const *wh[]= {nullptr, "bar", "line", "area", "high-low"};
2518
22
    f << wh[val] << ",";
2519
22
  }
2520
3.30k
  else if (val)
2521
671
    f << "#override=" << val << ",";
2522
3.33k
  input->seek(2, librevenge::RVNG_SEEK_CUR);
2523
3.33k
  if (sz<10)
2524
2.84k
  {
2525
2.84k
    ascFile.addPos(pos);
2526
2.84k
    ascFile.addNote(f.str().c_str());
2527
2.84k
    return true;
2528
2.84k
  }
2529
488
  val=int(libwps::readU16(input));
2530
488
  if (val) f << "f0=" << val << ",";
2531
488
  int dSz=int(libwps::readU16(input));
2532
488
  if (dSz+5>sz || dSz<4)
2533
282
  {
2534
282
    f << "###dSz=" << dSz << ",";
2535
282
    WPS_DEBUG_MSG(("QuattroParser::readSerieExtension: can not read the size extension\n"));
2536
282
    ascFile.addDelimiter(input->tell(),'|');
2537
282
    ascFile.addPos(pos);
2538
282
    ascFile.addNote(f.str().c_str());
2539
282
    return true;
2540
282
  }
2541
206
  val=int(libwps::readU16(input));
2542
206
  if (val) f << "type=" << val << ","; // 0: aggregation, 1: moving aggregation, 2: linear, 3: expon
2543
206
  val=int(libwps::readU16(input));
2544
206
  if (val&1) f << "filter[in legend],";
2545
206
  if (val&2) f << "filter[in table],";
2546
206
  if (val&4) f << "table[can be increased],";
2547
206
  val &= 0xfff8;
2548
206
  if (val) f << "fl=" << std::hex << val << std::dec << ",";
2549
206
  if (input->tell()!=endPos)
2550
151
    ascFile.addDelimiter(input->tell(),'|');
2551
206
  ascFile.addPos(pos);
2552
206
  ascFile.addNote(f.str().c_str());
2553
206
  return true;
2554
488
}
2555
2556
bool QuattroParser::readZone341(std::shared_ptr<WPSStream> stream)
2557
6.01k
{
2558
6.01k
  if (m_state->m_readingZone341)
2559
683
  {
2560
683
    WPS_DEBUG_MSG(("QuattroParser::readZone341: recursive call\n"));
2561
683
    return false;
2562
683
  }
2563
5.33k
  RVNGInputStreamPtr input = stream->m_input;
2564
5.33k
  libwps::DebugFile &ascFile=stream->m_ascii;
2565
5.33k
  libwps::DebugStream f;
2566
5.33k
  long pos = input->tell();
2567
5.33k
  auto type = int(libwps::readU16(input)&0x7fff);
2568
5.33k
  if (type != 0x341)
2569
0
  {
2570
0
    WPS_DEBUG_MSG(("QuattroParser::readZone341: not a 341 zone\n"));
2571
0
    return false;
2572
0
  }
2573
5.33k
  auto sz = long(libwps::readU16(input));
2574
5.33k
  long endPos=pos+4+sz;
2575
5.33k
  int const headerSize=version()>=1003 ? 82 : 75;
2576
5.33k
  if (sz<headerSize)
2577
906
  {
2578
906
    WPS_DEBUG_MSG(("QuattroParser::readZone341: size seems very bad\n"));
2579
906
    f << "###";
2580
906
    ascFile.addPos(pos);
2581
906
    ascFile.addNote(f.str().c_str());
2582
906
    return true;
2583
906
  }
2584
4.42k
  ascFile.addDelimiter(input->tell(),'|');
2585
4.42k
  input->seek(pos+4+headerSize, librevenge::RVNG_SEEK_SET);
2586
4.42k
  ascFile.addPos(pos);
2587
4.42k
  ascFile.addNote(f.str().c_str());
2588
2589
698k
  while (input->tell()+4<=endPos)
2590
698k
  {
2591
698k
    pos=input->tell();
2592
698k
    bool end=(libwps::readU16(input)&0x7fff)==0x31f;
2593
698k
    input->seek(pos,librevenge::RVNG_SEEK_SET);
2594
698k
    m_state->m_readingZone341 = true;
2595
698k
    const bool ok = readZone(stream);
2596
698k
    m_state->m_readingZone341 = false;
2597
698k
    if (!ok || input->tell()>endPos)
2598
2.10k
    {
2599
2.10k
      WPS_DEBUG_MSG(("QuattroParser::readZone341: find extra data\n"));
2600
2.10k
      ascFile.addPos(pos);
2601
2.10k
      ascFile.addNote("Zone341:###extra");
2602
2.10k
      return true;
2603
2.10k
    }
2604
696k
    if (end)
2605
2.21k
    {
2606
2.21k
      if (input->tell()<endPos)
2607
5
      {
2608
5
        ascFile.addPos(input->tell());
2609
5
        ascFile.addNote("_");
2610
5
      }
2611
2.21k
      return true;
2612
2.21k
    }
2613
696k
  }
2614
113
  if (sz!=headerSize)
2615
113
  {
2616
113
    WPS_DEBUG_MSG(("QuattroParser::readZone341: oops, does not find end zone\n"));
2617
113
  }
2618
113
  return true;
2619
4.42k
}
2620
2621
////////////////////////////////////////////////////////////
2622
//   ole stream
2623
////////////////////////////////////////////////////////////
2624
bool QuattroParser::readOleLinkInfo(std::shared_ptr<WPSStream> stream, librevenge::RVNGString &link)
2625
419
{
2626
419
  if (!stream || !stream->checkFilePosition(4))
2627
15
  {
2628
15
    WPS_DEBUG_MSG(("QuattroParser::readLinkInfo: unexpected zone\n"));
2629
15
    return false;
2630
15
  }
2631
404
  RVNGInputStreamPtr input = stream->m_input;
2632
404
  libwps::DebugFile &ascFile=stream->m_ascii;
2633
404
  libwps::DebugStream f;
2634
404
  f << "Entries(LinkInfo):";
2635
404
  int val=libwps::readU8(input);
2636
404
  if (val!=0x53)
2637
177
    f << "f0=" << std::hex << val << std::dec << ",";
2638
404
  val=libwps::readU16(input); // 2 or 3
2639
404
  if (val) f << "f1=" << val << ",";
2640
404
  if (!readCString(stream, link, stream->m_eof-3))
2641
0
  {
2642
0
    WPS_DEBUG_MSG(("QuattroParser::readLinkInfo: can not read the link\n"));
2643
0
    f << "##link,";
2644
0
    ascFile.addPos(0);
2645
0
    ascFile.addNote(f.str().c_str());
2646
0
    return false;
2647
0
  }
2648
404
  if (!link.empty())
2649
330
    f << "link=" << link.cstr() << ",";
2650
404
  ascFile.addPos(0);
2651
404
  ascFile.addNote(f.str().c_str());
2652
404
  return true;
2653
404
}
2654
2655
bool QuattroParser::readOleBOlePart(std::shared_ptr<WPSStream> stream)
2656
570
{
2657
570
  if (!stream || !stream->checkFilePosition(20))
2658
31
  {
2659
31
    WPS_DEBUG_MSG(("QuattroParser::readOleBOlePart: unexpected zone\n"));
2660
31
    return false;
2661
31
  }
2662
539
  RVNGInputStreamPtr input = stream->m_input;
2663
539
  libwps::DebugFile &ascFile=stream->m_ascii;
2664
539
  libwps::DebugStream f;
2665
539
  f << "Entries(BOlePart):";
2666
3.23k
  for (int i=0; i<5; ++i)   // f0=1, f1=f2=small int(often 1), f3=f4=small int(often 1)
2667
2.69k
  {
2668
2.69k
    auto val=int(libwps::read32(input));
2669
2.69k
    if (val!=1) f << "f" << i << "=" << val << ",";
2670
2.69k
  }
2671
539
  ascFile.addPos(0);
2672
539
  ascFile.addNote(f.str().c_str());
2673
539
  return true;
2674
570
}
2675
2676
////////////////////////////////////////////////////////////
2677
//   decode
2678
////////////////////////////////////////////////////////////
2679
RVNGInputStreamPtr QuattroParser::decodeStream(RVNGInputStreamPtr input, std::vector<uint8_t> const &key) const
2680
6.18k
{
2681
6.18k
  int const vers=version();
2682
6.18k
  if (!input || key.size()!=16)
2683
0
  {
2684
0
    WPS_DEBUG_MSG(("QuattroParser::decodeStream: the arguments seems bad\n"));
2685
0
    return RVNGInputStreamPtr();
2686
0
  }
2687
6.18k
  long actPos=input->tell();
2688
6.18k
  input->seek(0,librevenge::RVNG_SEEK_SET);
2689
6.18k
  librevenge::RVNGBinaryData data;
2690
6.18k
  if (!libwps::readDataToEnd(input, data) || !data.getDataBuffer())
2691
0
  {
2692
0
    WPS_DEBUG_MSG(("QuattroParser::decodeStream: can not read the original input\n"));
2693
0
    return RVNGInputStreamPtr();
2694
0
  }
2695
6.18k
  auto *buf=const_cast<unsigned char *>(data.getDataBuffer());
2696
6.18k
  auto endPos=long(data.size());
2697
6.18k
  input->seek(actPos,librevenge::RVNG_SEEK_SET);
2698
6.18k
  uint32_t d7=0;
2699
6.18k
  std::stack<long> stack;
2700
6.18k
  stack.push(endPos);
2701
6.18k
  const int zone341Size=vers<=1002 ? 75 : 82;
2702
5.18M
  while (!input->isEnd() && !stack.empty())
2703
5.18M
  {
2704
5.18M
    long pos=input->tell();
2705
5.18M
    if (pos+4>stack.top()) break;
2706
5.18M
    auto id=int(libwps::readU16(input)&0x7fff);
2707
5.18M
    auto sSz=int(libwps::readU16(input));
2708
5.18M
    if (pos+4+sSz>stack.top())
2709
4.16k
    {
2710
4.16k
      input->seek(pos,librevenge::RVNG_SEEK_SET);
2711
4.16k
      break;
2712
4.16k
    }
2713
5.17M
    if (id==0x341 && sSz>zone341Size)
2714
2.80k
    {
2715
      // special case a container with header of size 75 + different subzones
2716
2.80k
      stack.push(pos+4+sSz);
2717
2.80k
      sSz=zone341Size; // transform only the header
2718
2.80k
    }
2719
101M
    for (int i=0; i<sSz; ++i)
2720
96.8M
    {
2721
96.8M
      auto c=uint8_t(libwps::readU8(input));
2722
96.8M
      c=(c^key[(d7++)&0xf]);
2723
96.8M
      buf[pos+4+i]=uint8_t((c>>5)|(c<<3));
2724
96.8M
    }
2725
    // main zone ends with zone 1, zone 341 ends with zone 31f
2726
5.17M
    if (id==(stack.size()==1 ? 1 : 0x31f))
2727
3.52k
    {
2728
3.52k
      input->seek(stack.top(),librevenge::RVNG_SEEK_SET);
2729
3.52k
      stack.pop();
2730
3.52k
    }
2731
5.17M
  }
2732
6.18k
  if (input->tell()!=endPos)
2733
4.73k
  {
2734
4.73k
    WPS_DEBUG_MSG(("QuattroParser::decodeStream: can not decode the end of the file, data may be bad %lx %lx\n", static_cast<unsigned long>(input->tell()), static_cast<unsigned long>(endPos)));
2735
4.73k
  }
2736
6.18k
  RVNGInputStreamPtr res(new WPSStringStream(data.getDataBuffer(), static_cast<unsigned int>(endPos)));
2737
6.18k
  res->seek(actPos, librevenge::RVNG_SEEK_SET);
2738
6.18k
  return res;
2739
6.18k
}
2740
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */