Coverage Report

Created: 2026-10-28 06:34

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