Coverage Report

Created: 2026-09-06 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/QuattroDosSpreadsheet.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 <cmath>
26
#include <limits>
27
#include <set>
28
#include <sstream>
29
#include <stack>
30
#include <string>
31
32
#include <librevenge-stream/librevenge-stream.h>
33
34
#include "libwps_internal.h"
35
#include "libwps_tools_win.h"
36
37
#include "WPSCell.h"
38
#include "WKSContentListener.h"
39
#include "WPSEntry.h"
40
#include "WPSFont.h"
41
#include "WPSTable.h"
42
43
#include "QuattroDos.h"
44
45
#include "QuattroDosSpreadsheet.h"
46
47
namespace QuattroDosSpreadsheetInternal
48
{
49
50
///////////////////////////////////////////////////////////////////
51
//! a class used to store a style of a cell in QuattroDosSpreadsheet
52
struct Style final : public WPSCellFormat
53
{
54
  //! construtor
55
  explicit Style(libwps_tools_win::Font::Type type)
56
85.1k
    : WPSCellFormat()
57
85.1k
    , m_fontType(type)
58
85.1k
    , m_fileFormat(0xFF)
59
85.1k
    , m_extra("")
60
85.1k
  {
61
85.1k
  }
62
18.8k
  Style(Style const &)=default;
63
15.6k
  Style &operator=(Style const &)=default;
64
  //! destructor
65
  ~Style() final;
66
  //! operator<<
67
  friend std::ostream &operator<<(std::ostream &o, Style const &style);
68
  //! operator==
69
  bool operator==(Style const &st) const;
70
  //! operator!=
71
  bool operator!=(Style const &st) const
72
0
  {
73
0
    return !(*this==st);
74
0
  }
75
  //! font encoding type
76
  libwps_tools_win::Font::Type m_fontType;
77
  //! the file format
78
  int m_fileFormat;
79
  /** extra data */
80
  std::string m_extra;
81
};
82
83
Style::~Style()
84
104k
{
85
104k
}
86
87
//! operator<<
88
std::ostream &operator<<(std::ostream &o, Style const &style)
89
0
{
90
0
  o << static_cast<WPSCellFormat const &>(style) << ",";
91
0
  if (style.m_fileFormat!=0xFF)
92
0
    o << "format=" << std::hex << style.m_fileFormat << std::dec << ",";
93
0
  if (style.m_extra.length())
94
0
    o << "extra=[" << style.m_extra << "],";
95
96
0
  return o;
97
0
}
98
99
bool Style::operator==(Style const &st) const
100
0
{
101
0
  if (m_fontType!=st.m_fontType || m_fileFormat!=st.m_fileFormat) return false;
102
0
  int diff = WPSCellFormat::compare(st);
103
0
  if (diff) return false;
104
0
  return m_extra==st.m_extra;
105
0
}
106
107
///////////////////////////////////////////////////////////////////
108
//! the style manager
109
class StyleManager
110
{
111
public:
112
  StyleManager()
113
11.4k
    : m_idStyleMap() {}
114
  //! add a new style and returns its id
115
  void add(int id, Style const &st)
116
47.2k
  {
117
47.2k
    if (m_idStyleMap.find(id)!=m_idStyleMap.end())
118
37.7k
    {
119
37.7k
      WPS_DEBUG_MSG(("QuattroDosParserInternal::StyleManager::add style %d already exists\n", id));
120
37.7k
      return;
121
37.7k
    }
122
9.44k
    m_idStyleMap.insert(std::map<int, Style>::value_type(id,st));
123
9.44k
  }
124
  //! returns the style with id
125
  bool get(int id, Style &style) const
126
54.4k
  {
127
54.4k
    if (m_idStyleMap.find(id)==m_idStyleMap.end())
128
38.7k
    {
129
38.7k
      WPS_DEBUG_MSG(("QuattroDosParserInternal::StyleManager::get can not find style %d\n", id));
130
38.7k
      return false;
131
38.7k
    }
132
15.6k
    style=m_idStyleMap.find(id)->second;
133
15.6k
    return true;
134
54.4k
  }
135
  //! print a style
136
  void print(int id, std::ostream &o) const
137
0
  {
138
0
    if (m_idStyleMap.find(id)!=m_idStyleMap.end())
139
0
      o << ", style=" << m_idStyleMap.find(id)->second;
140
0
    else
141
0
    {
142
0
      WPS_DEBUG_MSG(("QuattroDosParserInternal::StyleManager::print: can not find a style\n"));
143
0
      o << ", ###style=" << id;
144
0
    }
145
0
  }
146
147
protected:
148
  //! the styles
149
  std::map<int, Style> m_idStyleMap;
150
};
151
152
//! a cellule of a Quattro spreadsheet
153
class Cell final : public WPSCell
154
{
155
public:
156
  /// constructor
157
  explicit Cell(libwps_tools_win::Font::Type type)
158
57.8k
    : m_fontType(type)
159
57.8k
    , m_fileFormat(0xFF)
160
57.8k
    , m_content() { }
161
162
  //! operator<<
163
  friend std::ostream &operator<<(std::ostream &o, Cell const &cell);
164
165
  //! call when a cell must be send
166
  bool send(WPSListenerPtr &/*listener*/) final;
167
168
  //! call when the content of a cell must be send
169
  bool sendContent(WPSListenerPtr &/*listener*/) final
170
0
  {
171
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheetInternal::Cell::sendContent: must not be called\n"));
172
0
    return false;
173
0
  }
174
175
  //! update the cell format using file format
176
  void updateFormat()
177
41.9k
  {
178
41.9k
    switch ((m_fileFormat>>4)&7)
179
41.9k
    {
180
15.5k
    case 0: // fixed
181
15.5k
      setFormat(F_NUMBER, 1);
182
15.5k
      setDigits(m_fileFormat&0xF);
183
15.5k
      break;
184
302
    case 1: // scientific
185
302
      setFormat(F_NUMBER, 2);
186
302
      setDigits(m_fileFormat&0xF);
187
302
      break;
188
510
    case 2: // currency
189
510
      setFormat(F_NUMBER, 4);
190
510
      setDigits(m_fileFormat&0xF);
191
510
      break;
192
279
    case 3: // percent
193
279
      setFormat(F_NUMBER, 3);
194
279
      setDigits(m_fileFormat&0xF);
195
279
      break;
196
3.34k
    case 4: // decimal
197
3.34k
      setFormat(F_NUMBER, 1);
198
3.34k
      setDigits(m_fileFormat&0xF);
199
3.34k
      break;
200
606
    case 5:
201
606
      switch (m_fileFormat&0xF)
202
606
      {
203
60
      case 4: // a date but no sure which format is good
204
60
        setDTFormat(F_DATE, "%m/%d/%y");
205
60
        break;
206
249
      case 5:
207
249
        setDTFormat(F_DATE, "%m/%d");
208
249
        break;
209
297
      default:
210
297
        WPS_DEBUG_MSG(("QuattroDosSpreadsheetInternal::Cell::updateFormat: unknown format %x\n", unsigned(m_fileFormat)));
211
297
        break;
212
606
      }
213
606
      break;
214
606
    case 6:
215
327
      WPS_DEBUG_MSG(("QuattroDosSpreadsheetInternal::Cell::updateFormat: unknown format %x\n", unsigned(m_fileFormat)));
216
327
      break;
217
21.0k
    case 7:
218
21.0k
      switch (m_fileFormat&0xF)
219
21.0k
      {
220
206
      case 0: // +/- : kind of bool
221
206
        setFormat(F_BOOLEAN);
222
206
        break;
223
8.55k
      case 1:
224
8.55k
        setFormat(F_NUMBER, 0);
225
8.55k
        break;
226
399
      case 2:
227
399
        setDTFormat(F_DATE, "%d %B %y");
228
399
        break;
229
143
      case 3:
230
143
        setDTFormat(F_DATE, "%d %B");
231
143
        break;
232
98
      case 4:
233
98
        setDTFormat(F_DATE, "%B %y");
234
98
        break;
235
177
      case 5:
236
177
        setFormat(F_TEXT);
237
177
        break;
238
144
      case 6:
239
144
        setFormat(F_TEXT);
240
144
        m_font.m_attributes |= WPS_HIDDEN_BIT;
241
144
        break;
242
181
      case 7:
243
181
        setDTFormat(F_TIME, "%I:%M:%S%p");
244
181
        break;
245
279
      case 8:
246
279
        setDTFormat(F_TIME, "%I:%M%p");
247
279
        break;
248
123
      case 9:
249
123
        setDTFormat(F_DATE, "%m/%d/%y");
250
123
        break;
251
1.76k
      case 0xa:
252
1.76k
        setDTFormat(F_DATE, "%m/%d");
253
1.76k
        break;
254
348
      case 0xb:
255
348
        setDTFormat(F_TIME, "%H:%M:%S");
256
348
        break;
257
118
      case 0xc:
258
118
        setDTFormat(F_TIME, "%H:%M");
259
118
        break;
260
161
      case 0xd:
261
161
        setFormat(F_TEXT);
262
161
        break;
263
8.16k
      case 0xf: // automatic
264
8.16k
        break;
265
159
      default:
266
159
        break;
267
21.0k
      }
268
21.0k
      break;
269
21.0k
    default:
270
0
      break;
271
41.9k
    }
272
41.9k
  }
273
  //! font encoding type
274
  libwps_tools_win::Font::Type m_fontType;
275
  //! the file format
276
  int m_fileFormat;
277
  //! the content
278
  WKSContentListener::CellContent m_content;
279
};
280
281
bool Cell::send(WPSListenerPtr &/*listener*/)
282
0
{
283
0
  WPS_DEBUG_MSG(("QuattroDosSpreadsheetInternal::Cell::send: must not be called\n"));
284
0
  return false;
285
0
}
286
//! operator<<
287
std::ostream &operator<<(std::ostream &o, Cell const &cell)
288
0
{
289
0
  o << reinterpret_cast<WPSCell const &>(cell) << cell.m_content << ",";
290
0
  if (cell.m_fileFormat!=0xFF)
291
0
    o << "format=" << std::hex << cell.m_fileFormat << std::dec << ",";
292
0
  return o;
293
0
}
294
295
///////////////////////////////////////////////////////////////////
296
//! the spreadsheet of a QuattroDos Spreadsheet
297
class Spreadsheet
298
{
299
public:
300
  //! the spreadsheet type
301
  enum Type { T_Spreadsheet, T_Filter, T_Report };
302
303
  //! a constructor
304
  explicit Spreadsheet(Type type=T_Spreadsheet, int id=0)
305
700k
    : m_type(type)
306
700k
    , m_id(id)
307
700k
    , m_numCols(0)
308
700k
    , m_widthCols()
309
700k
    , m_rowHeightMap()
310
700k
    , m_heightDefault(16)
311
700k
    , m_widthDefault(76)
312
700k
    , m_positionToCellMap()
313
700k
    , m_lastCellPos()
314
700k
    , m_rowPageBreaksList() {}
315
  //! return a cell corresponding to a spreadsheet, create one if needed
316
  Cell &getCell(Vec2i const &pos, libwps_tools_win::Font::Type type)
317
103k
  {
318
103k
    if (m_positionToCellMap.find(pos)==m_positionToCellMap.end())
319
41.1k
    {
320
41.1k
      Cell cell(type);
321
41.1k
      cell.setPosition(pos);
322
41.1k
      m_positionToCellMap.insert(std::map<Vec2i, Cell>::value_type(pos,cell));
323
41.1k
    }
324
103k
    m_lastCellPos=pos;
325
103k
    return m_positionToCellMap.find(pos)->second;
326
103k
  }
327
  //! returns the last cell
328
  Cell *getLastCell()
329
0
  {
330
0
    if (m_positionToCellMap.find(m_lastCellPos)==m_positionToCellMap.end())
331
0
      return nullptr;
332
0
    return &m_positionToCellMap.find(m_lastCellPos)->second;
333
0
  }
334
  void insertChartPositions(std::map<Vec2i, Vec2i> const &chartPosMap, libwps_tools_win::Font::Type type)
335
66.1k
  {
336
66.1k
    int maxCol=-1;
337
66.1k
    for (auto const &it : chartPosMap)
338
3.07k
    {
339
3.07k
      if (it.second[0]>maxCol) maxCol=it.second[0];
340
3.07k
      if (m_positionToCellMap.find(it.first)!=m_positionToCellMap.end())
341
146
        continue;
342
2.93k
      Cell cell(type);
343
2.93k
      cell.setPosition(it.first);
344
2.93k
      m_positionToCellMap.insert(std::map<Vec2i, Cell>::value_type(it.first,cell));
345
2.93k
    }
346
66.1k
    if (maxCol>=int(m_widthCols.size()))
347
1.48k
      m_widthCols.resize(size_t(maxCol+1), -1);
348
66.1k
  }
349
  //! set the columns size
350
  void setColumnWidth(int col, int w=-1)
351
6.11k
  {
352
6.11k
    if (col < 0) return;
353
6.11k
    if (col >= int(m_widthCols.size())) m_widthCols.resize(size_t(col)+1, -1);
354
6.11k
    m_widthCols[size_t(col)] = w;
355
6.11k
    if (col >= m_numCols) m_numCols=col+1;
356
6.11k
  }
357
358
  //! return the columns format
359
  std::vector<WPSColumnFormat> getWidths() const
360
66.1k
  {
361
66.1k
    std::vector<WPSColumnFormat> widths;
362
66.1k
    WPSColumnFormat defWidth(static_cast<float>(m_widthDefault)), actWidth;
363
66.1k
    defWidth.m_useOptimalWidth=true;
364
66.1k
    int repeat=0;
365
66.1k
    for (auto const &w : m_widthCols)
366
1.59M
    {
367
1.59M
      WPSColumnFormat newWidth;
368
1.59M
      if (w < 0)
369
1.59M
        newWidth=defWidth;
370
792
      else
371
792
        newWidth=WPSColumnFormat(float(w)/20.f);
372
1.59M
      if (repeat && newWidth!=actWidth)
373
632
      {
374
632
        actWidth.m_numRepeat=repeat;
375
632
        widths.push_back(actWidth);
376
632
        repeat=0;
377
632
      }
378
1.59M
      if (repeat==0)
379
2.65k
        actWidth=newWidth;
380
1.59M
      ++repeat;
381
1.59M
    }
382
66.1k
    if (repeat)
383
2.02k
    {
384
2.02k
      actWidth.m_numRepeat=repeat;
385
2.02k
      widths.push_back(actWidth);
386
2.02k
    }
387
66.1k
    return widths;
388
66.1k
  }
389
  //! returns the row size in point
390
  float getRowHeight(int row) const
391
17.3k
  {
392
17.3k
    auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row));
393
17.3k
    if (rIt!=m_rowHeightMap.end() && rIt->first[0]<=row && rIt->first[1]>=row)
394
1.06k
      return float(rIt->second);
395
16.3k
    return float(m_heightDefault);
396
17.3k
  }
397
  //! returns the height of a row in point and updated repeated row
398
  float getRowHeight(int row, int &numRepeated) const
399
14.9k
  {
400
14.9k
    auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row));
401
14.9k
    if (rIt!=m_rowHeightMap.end() && rIt->first[0]<=row && rIt->first[1]>=row)
402
3.17k
    {
403
3.17k
      numRepeated=rIt->first[1]-row+1;
404
3.17k
      return float(rIt->second);
405
3.17k
    }
406
11.7k
    numRepeated=10000;
407
11.7k
    return float(m_heightDefault);
408
14.9k
  }
409
  //! set the rows size
410
  void setRowHeight(int row, int h)
411
5.12k
  {
412
5.12k
    if (h>=0)
413
5.12k
      m_rowHeightMap[Vec2i(row,row)]=h;
414
5.12k
  }
415
  //! return the size of the zone containning two cells
416
  Vec2f getZoneSize(Vec2i const &cell0, Vec2i const &cell1) const
417
3.07k
  {
418
3.07k
    int w=0;
419
3.07k
    auto numCol=int(m_widthCols.size());
420
50.8k
    for (int i=cell0[0]; i<=cell1[0]; ++i)
421
47.8k
    {
422
47.8k
      if (i<0 || i>=numCol)
423
0
        w+=m_widthDefault;
424
47.8k
      else
425
47.8k
        w+=m_widthCols[size_t(i)];
426
47.8k
    }
427
3.07k
    int h=0;
428
3.07k
    auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,cell0[1]));
429
3.25M
    for (int i=cell0[1]; i<=cell1[1]; ++i)
430
3.24M
    {
431
3.24M
      if (rIt==m_rowHeightMap.end())
432
3.04M
        h+=m_heightDefault;
433
205k
      else if (rIt->first[0]<=i && rIt->first[1]>=i)
434
205k
        h+=rIt->second;
435
253
      else
436
253
      {
437
253
        if (rIt->first[1]<i) ++rIt;
438
253
        if (rIt==m_rowHeightMap.end() || rIt->first[0]>i)
439
45
          h+=m_heightDefault;
440
208
        else
441
208
          h+=rIt->second;
442
253
      }
443
3.24M
    }
444
3.07k
    return Vec2f(float(w),float(h));
445
3.07k
  }
446
  //! try to compress the list of row height
447
  void compressRowHeights()
448
66.1k
  {
449
66.1k
    auto oldMap=m_rowHeightMap;
450
66.1k
    m_rowHeightMap.clear();
451
66.1k
    int actHeight=-1;
452
66.1k
    Vec2i actPos(0, -1);
453
66.1k
    for (auto const &rIt : oldMap)
454
2.16k
    {
455
      // first check for not filled row
456
2.16k
      if (rIt.first[0]!=actPos[1]+1)
457
1.92k
      {
458
1.92k
        if (actHeight==m_heightDefault)
459
40
          actPos[1]=rIt.first[0]-1;
460
1.88k
        else
461
1.88k
        {
462
1.88k
          if (actPos[1]>=actPos[0])
463
1.30k
            m_rowHeightMap[actPos]=actHeight;
464
1.88k
          actHeight=m_heightDefault;
465
1.88k
          actPos=Vec2i(actPos[1]+1, rIt.first[0]-1);
466
1.88k
        }
467
1.92k
      }
468
2.16k
      if (rIt.second!=actHeight)
469
2.09k
      {
470
2.09k
        if (actPos[1]>=actPos[0])
471
1.86k
          m_rowHeightMap[actPos]=actHeight;
472
2.09k
        actPos[0]=rIt.first[0];
473
2.09k
        actHeight=rIt.second;
474
2.09k
      }
475
2.16k
      actPos[1]=rIt.first[1];
476
2.16k
    }
477
66.1k
    if (actPos[1]>=actPos[0])
478
803
      m_rowHeightMap[actPos]=actHeight;
479
66.1k
  }
480
  //! returns true if the spreedsheet is empty
481
  bool empty() const
482
442k
  {
483
442k
    return m_positionToCellMap.empty();
484
442k
  }
485
  //! the spreadsheet type
486
  Type m_type;
487
  //! the spreadsheet id
488
  int m_id;
489
  /** the number of columns */
490
  int m_numCols;
491
492
  /** the column size in TWIP (?) */
493
  std::vector<int> m_widthCols;
494
  /** the map Vec2i(min row, max row) to size in points */
495
  std::map<Vec2i, int> m_rowHeightMap;
496
  /** the default row size in point */
497
  int m_heightDefault;
498
  /** the default width size in point */
499
  int m_widthDefault;
500
  /** a map cell to not empty cells */
501
  std::map<Vec2i, Cell> m_positionToCellMap;
502
  /** the last cell position */
503
  Vec2i m_lastCellPos;
504
  /** the list of row page break */
505
  std::vector<int> m_rowPageBreaksList;
506
507
};
508
509
//! the state of QuattroDosSpreadsheet
510
struct State
511
{
512
  //! constructor
513
  State()
514
11.4k
    : m_eof(-1)
515
11.4k
    , m_version(-1)
516
11.4k
    , m_styleManager()
517
11.4k
    , m_spreadsheetList()
518
11.4k
    , m_spreadsheetStack()
519
11.4k
    , m_idToSheetNameMap()
520
11.4k
    , m_sheetNameSet()
521
11.4k
  {
522
11.4k
    pushNewSheet(std::shared_ptr<Spreadsheet>(new Spreadsheet(Spreadsheet::T_Spreadsheet, 0)));
523
11.4k
  }
524
  //! returns the maximal spreadsheet
525
  int getMaximalSheet(Spreadsheet::Type type=Spreadsheet::T_Spreadsheet) const
526
13.1k
  {
527
13.1k
    int max=-1;
528
13.1k
    for (auto const &sheet : m_spreadsheetList)
529
925k
    {
530
925k
      if (!sheet || sheet->m_type != type || sheet->m_id<=max || sheet->empty()) continue;
531
9.57k
      max=sheet->m_id;
532
9.57k
    }
533
13.1k
    return max;
534
13.1k
  }
535
  //! returns the ith real spreadsheet
536
  std::shared_ptr<Spreadsheet> getSheet(Spreadsheet::Type type, int id)
537
66.1k
  {
538
66.1k
    for (auto const &sheet : m_spreadsheetList)
539
52.6M
    {
540
52.6M
      if (!sheet || sheet->m_type!=type || sheet->m_id!=id)
541
52.6M
        continue;
542
6.27k
      return sheet;
543
52.6M
    }
544
59.9k
    return std::shared_ptr<Spreadsheet>();
545
66.1k
  }
546
  //! returns the ith spreadsheet
547
  librevenge::RVNGString getSheetName(int id)
548
112k
  {
549
112k
    auto it = m_idToSheetNameMap.find(id);
550
112k
    if (it!=m_idToSheetNameMap.end() && !it->second.empty())
551
45.6k
      return it->second;
552
66.7k
    librevenge::RVNGString name;
553
66.7k
    name.sprintf("Sheet%d", id+1);
554
66.7k
    name=getUniqueSheetName(name);
555
66.7k
    m_idToSheetNameMap[id]=name;
556
66.7k
    return name;
557
112k
  }
558
  //! returns a unique sheet name build using the current name
559
  librevenge::RVNGString getUniqueSheetName(librevenge::RVNGString const &name)
560
67.2k
  {
561
67.2k
    if (m_sheetNameSet.find(name)==m_sheetNameSet.end())
562
66.9k
    {
563
66.9k
      m_sheetNameSet.insert(name);
564
66.9k
      return name;
565
66.9k
    }
566
267
    int i=0;
567
716
    while (i<1000)
568
716
    {
569
716
      librevenge::RVNGString uniqueName(name);
570
716
      uniqueName.append((std::string("_")+std::to_string(i++)).c_str());
571
716
      if (m_sheetNameSet.find(uniqueName)==m_sheetNameSet.end())
572
267
      {
573
267
        m_sheetNameSet.insert(uniqueName);
574
267
        return uniqueName;
575
267
      }
576
716
    }
577
0
    return name;
578
267
  }
579
  //! returns the actual sheet
580
  Spreadsheet &getActualSheet()
581
124k
  {
582
124k
    return *m_spreadsheetStack.top();
583
124k
  }
584
  //! create a new sheet and stack id
585
  void pushNewSheet(std::shared_ptr<Spreadsheet> sheet)
586
640k
  {
587
640k
    if (!sheet)
588
0
    {
589
0
      WPS_DEBUG_MSG(("QuattroDosSpreadsheetInternal::State::pushSheet: can not find the sheet\n"));
590
0
      return;
591
0
    }
592
640k
    m_spreadsheetStack.push(sheet);
593
640k
    m_spreadsheetList.push_back(sheet);
594
640k
  }
595
  //! try to pop the actual sheet
596
  bool popSheet()
597
1.08k
  {
598
1.08k
    if (m_spreadsheetStack.size()<=1)
599
0
    {
600
0
      WPS_DEBUG_MSG(("QuattroDosSpreadsheetInternal::State::popSheet: can pop the main sheet\n"));
601
0
      return false;
602
0
    }
603
1.08k
    m_spreadsheetStack.pop();
604
1.08k
    return true;
605
1.08k
  }
606
607
  //! the last file position
608
  long m_eof;
609
  //! the file version
610
  int m_version;
611
  //! the style manager
612
  StyleManager m_styleManager;
613
614
  //! the list of spreadsheet ( first: main spreadsheet, other report spreadsheet )
615
  std::vector<std::shared_ptr<Spreadsheet> > m_spreadsheetList;
616
  //! the stack of spreadsheet id
617
  std::stack<std::shared_ptr<Spreadsheet> > m_spreadsheetStack;
618
  //! the map id to sheet's name
619
  std::map<int, librevenge::RVNGString> m_idToSheetNameMap;
620
  //! the set of used sheet name
621
  std::set<librevenge::RVNGString> m_sheetNameSet;
622
623
};
624
625
}
626
627
// constructor, destructor
628
QuattroDosSpreadsheet::QuattroDosSpreadsheet(QuattroDosParser &parser)
629
11.4k
  : m_input(parser.getInput())
630
11.4k
  , m_listener()
631
11.4k
  , m_mainParser(parser)
632
11.4k
  , m_state(new QuattroDosSpreadsheetInternal::State)
633
11.4k
  , m_asciiFile(parser.ascii())
634
11.4k
{
635
11.4k
}
636
637
QuattroDosSpreadsheet::~QuattroDosSpreadsheet()
638
11.4k
{
639
11.4k
}
640
641
int QuattroDosSpreadsheet::version() const
642
180k
{
643
180k
  if (m_state->m_version<0)
644
7.03k
    m_state->m_version=m_mainParser.version();
645
180k
  return m_state->m_version;
646
180k
}
647
648
bool QuattroDosSpreadsheet::checkFilePosition(long pos)
649
0
{
650
0
  if (m_state->m_eof < 0)
651
0
  {
652
0
    long actPos = m_input->tell();
653
0
    m_input->seek(0, librevenge::RVNG_SEEK_END);
654
0
    m_state->m_eof=m_input->tell();
655
0
    m_input->seek(actPos, librevenge::RVNG_SEEK_SET);
656
0
  }
657
0
  return pos <= m_state->m_eof;
658
0
}
659
660
int QuattroDosSpreadsheet::getNumSpreadsheets() const
661
13.1k
{
662
13.1k
  return m_state->getMaximalSheet(QuattroDosSpreadsheetInternal::Spreadsheet::T_Spreadsheet)+1;
663
13.1k
}
664
665
librevenge::RVNGString QuattroDosSpreadsheet::getSheetName(int id) const
666
46.1k
{
667
46.1k
  return m_state->getSheetName(id);
668
46.1k
}
669
670
////////////////////////////////////////////////////////////
671
// low level
672
673
////////////////////////////////////////////////////////////
674
//   parse sheet data
675
////////////////////////////////////////////////////////////
676
bool QuattroDosSpreadsheet::readSheetSize()
677
2.69k
{
678
2.69k
  libwps::DebugStream f;
679
2.69k
  long pos = m_input->tell();
680
2.69k
  long type = libwps::read16(m_input);
681
2.69k
  if (type != 0x6)
682
0
  {
683
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSheetSize: not a sheet zone\n"));
684
0
    return false;
685
0
  }
686
2.69k
  long sz = libwps::readU16(m_input);
687
2.69k
  int const vers=version();
688
2.69k
  if (sz < (vers>=2 ? 12 : 8))
689
16
  {
690
16
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSheetSize: block is too short\n"));
691
16
    return false;
692
16
  }
693
2.67k
  f << "Entries(SheetSize):";
694
8.59k
  for (int i = 0; i < (vers>=2 ? 3 : 2); i++)   // always 2 zero ?
695
5.91k
  {
696
5.91k
    int val = libwps::read16(m_input);
697
5.91k
    if (!val) continue;
698
2.14k
    f << "f" << i << "=" << std::hex << val << std::dec << ",";
699
2.14k
  }
700
2.67k
  int nCol = libwps::read16(m_input)+1;
701
2.67k
  f << "nCols=" << nCol << ",";
702
2.67k
  int nRow =  libwps::read16(m_input);
703
2.67k
  f << "nRow=" << nRow << ",";
704
2.67k
  int nSheet =  vers<=1 ? 0 : libwps::read16(m_input);
705
2.67k
  if (nSheet>0)
706
214
    f << "nSheet=" << nSheet << ",";
707
2.67k
  ascii().addPos(pos);
708
2.67k
  ascii().addNote(f.str().c_str());
709
  // empty spreadsheet
710
2.67k
  if (nRow==-1 && nCol==0) return true;
711
2.64k
  if (nRow < 0 || nCol <= 0) return false;
712
2.57k
  m_state->getActualSheet().setColumnWidth(nCol-1);
713
2.57k
  return true;
714
715
2.64k
}
716
717
bool QuattroDosSpreadsheet::readRowSize()
718
6.22k
{
719
6.22k
  libwps::DebugStream f;
720
6.22k
  long pos = m_input->tell();
721
6.22k
  long type = libwps::read16(m_input);
722
6.22k
  if (type != 0xe0)
723
0
  {
724
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readRowSize: not a row size zone\n"));
725
0
    return false;
726
0
  }
727
6.22k
  long sz = libwps::readU16(m_input);
728
6.22k
  if (sz < 3)
729
990
  {
730
990
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readRowSize: block is too short\n"));
731
990
    return false;
732
990
  }
733
734
5.23k
  int row = libwps::read16(m_input);
735
5.23k
  int height = libwps::readU8(m_input);
736
737
5.23k
  bool ok = row >= 0;
738
5.23k
  f << "Entries(Row):Row" << row << "";
739
5.23k
  if (!ok) f << "###";
740
5.23k
  f << ":height=" << height << ",";
741
742
5.23k
  if (ok)
743
5.12k
    m_state->getActualSheet().setRowHeight(row, height); // point to TWIP
744
5.23k
  ascii().addPos(pos);
745
5.23k
  ascii().addNote(f.str().c_str());
746
747
5.23k
  return ok;
748
6.22k
}
749
750
bool QuattroDosSpreadsheet::readColumnSize()
751
3.88k
{
752
3.88k
  libwps::DebugStream f;
753
3.88k
  long pos = m_input->tell();
754
3.88k
  long type = libwps::read16(m_input);
755
3.88k
  if (type != 0x8 && type != 0xe2)
756
0
  {
757
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readColumnSize: not a column size zone\n"));
758
0
    return false;
759
0
  }
760
3.88k
  long sz = libwps::readU16(m_input);
761
3.88k
  if (sz < 3)
762
215
  {
763
215
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readColumnSize: block is too short\n"));
764
215
    return false;
765
215
  }
766
767
3.67k
  int col = libwps::read16(m_input);
768
3.67k
  int width = libwps::readU8(m_input);
769
770
3.67k
  bool ok = col >= 0 && col < m_state->getActualSheet().m_numCols+10;
771
3.67k
  f << "Entries(Column):Col" << col << "";
772
3.67k
  if (!ok) f << "###";
773
3.67k
  f << ":width=" << width << ",";
774
775
3.67k
  if (ok)
776
3.54k
  {
777
3.54k
    if (col >= m_state->getActualSheet().m_numCols)
778
849
    {
779
849
      static bool first = true;
780
849
      if (first)
781
4
      {
782
4
        first = false;
783
4
        WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readColumnSize: I must increase the number of columns\n"));
784
4
      }
785
849
      f << "#col[inc],";
786
849
    }
787
    // checkme: unit in character(?) -> TWIP
788
3.54k
    m_state->getActualSheet().setColumnWidth(col, width*105);
789
3.54k
  }
790
3.67k
  ascii().addPos(pos);
791
3.67k
  ascii().addNote(f.str().c_str());
792
793
3.67k
  return ok;
794
3.88k
}
795
796
bool QuattroDosSpreadsheet::readHiddenColumns()
797
1.21k
{
798
1.21k
  libwps::DebugStream f;
799
1.21k
  long pos = m_input->tell();
800
1.21k
  long type = libwps::read16(m_input);
801
1.21k
  if (type != 0x64)
802
0
  {
803
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readHiddenColumns: not a column hidden zone\n"));
804
0
    return false;
805
0
  }
806
1.21k
  long sz = libwps::readU16(m_input);
807
1.21k
  if (sz != 32)
808
523
  {
809
523
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readHiddenColumns: block size seems bad\n"));
810
523
    return false;
811
523
  }
812
813
687
  f << "Entries(HiddenCol):";
814
22.6k
  for (int i=0; i<32; ++i)
815
21.9k
  {
816
21.9k
    auto val=int(libwps::readU8(m_input));
817
21.9k
    if (!val) continue;
818
7.12k
    static bool first=true;
819
7.12k
    if (first)
820
3
    {
821
3
      WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readHiddenColumns: find some hidden col, ignored\n"));
822
3
      first=false;
823
3
    }
824
    // checkme
825
64.0k
    for (int j=0, depl=1; j<8; ++j, depl<<=1)
826
56.9k
    {
827
56.9k
      if (val&depl)
828
15.6k
        f << "col" << j+8*i << "[hidden],";
829
56.9k
    }
830
7.12k
  }
831
687
  ascii().addPos(pos);
832
687
  ascii().addNote(f.str().c_str());
833
834
687
  return true;
835
1.21k
}
836
837
bool QuattroDosSpreadsheet::readCellStyle()
838
29.0k
{
839
29.0k
  libwps::DebugStream f;
840
29.0k
  long pos = m_input->tell();
841
29.0k
  long type = libwps::read16(m_input);
842
29.0k
  int const vers=version();
843
29.0k
  if (type != 0xd8)
844
0
  {
845
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCellStyle: not a style zone\n"));
846
0
    return false;
847
0
  }
848
29.0k
  long sz = libwps::readU16(m_input);
849
29.0k
  f << "Entries(CellStyle):";
850
29.0k
  if ((vers==1&&(sz%12)!=0) || (vers>1 && sz!=0x16))
851
4.03k
  {
852
4.03k
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCellStyle: size seems bad\n"));
853
4.03k
    f << "###";
854
4.03k
    ascii().addPos(pos);
855
4.03k
    ascii().addNote(f.str().c_str());
856
4.03k
    return true;
857
4.03k
  }
858
25.0k
  if (vers>1)
859
16.5k
  {
860
16.5k
    auto pId=int(libwps::readU16(m_input));
861
16.5k
    QuattroDosSpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
862
16.5k
    auto id=int(libwps::readU16(m_input));
863
16.5k
    f.str("");
864
16.5k
    if (!m_state->m_styleManager.get(pId, style))
865
7.78k
      f << "###";
866
16.5k
    if (pId!=0xFF0F)
867
15.6k
    {
868
15.6k
      f << "Ce" << (pId>>8);
869
15.6k
      if (pId&0xFF) f << "-" << (pId&0xFF);
870
15.6k
      f << "[parent],";
871
15.6k
    }
872
16.5k
    int val;
873
82.6k
    for (int i=0; i<4; ++i)   // small int, unknown
874
66.1k
    {
875
66.1k
      val=int(libwps::readU8(m_input));
876
66.1k
      if (val) f << "f" << i << "=" << val << ",";
877
66.1k
    }
878
16.5k
    WPSFont font;
879
16.5k
    auto flags = int(libwps::readU16(m_input));
880
16.5k
    uint32_t attributes = 0;
881
16.5k
    if (flags & 1) attributes |= WPS_BOLD_BIT;
882
16.5k
    if (flags & 2) attributes |= WPS_ITALICS_BIT;
883
16.5k
    if (flags & 8) attributes |= WPS_UNDERLINE_BIT;
884
885
16.5k
    font.m_attributes=attributes;
886
16.5k
    if (flags & 0xFFF4)
887
11.4k
      f << "fl=" << std::hex << (flags & 0xFFF4) << std::dec << ",";
888
16.5k
    auto fId=int(libwps::readU16(m_input));
889
16.5k
    f << "fId=" << fId << ",";
890
16.5k
    auto fSize = int(libwps::readU16(m_input));
891
16.5k
    if (fSize >= 1 && fSize <= 50)
892
6.78k
      font.m_size=double(fSize);
893
9.75k
    else
894
9.75k
      f << "###fSize=" << fSize << ",";
895
16.5k
    auto color = int(libwps::readU16(m_input));
896
16.5k
    if (color && !m_mainParser.getColor(color, font.m_color))
897
11.7k
    {
898
11.7k
      WPS_DEBUG_MSG(("QuattroDosParser::readCellStyle: unknown color\n"));
899
11.7k
      f << "##color=" << color << ",";
900
11.7k
    }
901
16.5k
    style.setFont(font);
902
903
16.5k
    val=int(libwps::readU8(m_input));
904
16.5k
    if (val)
905
14.9k
    {
906
74.9k
      for (int i=0, depl=0; i<4; ++i, depl+=2)
907
59.9k
      {
908
59.9k
        int bd=(val>>depl)&3;
909
59.9k
        if (!bd) continue;
910
43.8k
        WPSBorder border;
911
43.8k
        switch (bd)
912
43.8k
        {
913
16.0k
        case 1: // normal
914
16.0k
          break;
915
11.4k
        case 2:
916
11.4k
          border.m_type = WPSBorder::Double;
917
11.4k
          break;
918
16.4k
        case 3:
919
16.4k
          border.m_width=2;
920
16.4k
          break;
921
0
        default:
922
0
          break;
923
43.8k
        }
924
43.8k
        int const wh[]= {WPSBorder::TopBit, WPSBorder::LeftBit, WPSBorder::BottomBit, WPSBorder::RightBit};
925
43.8k
        style.setBorders(wh[i], border);
926
43.8k
      }
927
14.9k
    }
928
16.5k
    val=int(libwps::readU8(m_input)); // 8|10|18
929
16.5k
    if (val) f << "g0=" << val << ",";
930
16.5k
    val=int(libwps::readU8(m_input));
931
16.5k
    switch (val)
932
16.5k
    {
933
3.84k
    case 0:
934
3.84k
      break;
935
1.76k
    case 1:
936
1.76k
      style.setBackgroundColor(WPSColor(0x80,0x80,0x80));
937
1.76k
      break;
938
1.69k
    case 2:
939
1.69k
      style.setBackgroundColor(WPSColor::black());
940
1.69k
      break;
941
9.23k
    default:
942
9.23k
      if ((val&3)==3)
943
882
      {
944
882
        WPSColor col;
945
882
        if (!m_mainParser.getColor(val>>2, col))
946
417
        {
947
417
          WPS_DEBUG_MSG(("QuattroDosParser::readCellStyle: unknown background color\n"));
948
417
          f << "##color=" << val << ",";
949
417
        }
950
465
        else
951
465
          style.setBackgroundColor(col);
952
882
      }
953
8.34k
      else
954
8.34k
        f << "##background=" << val << ",";
955
9.23k
      break;
956
16.5k
    }
957
16.5k
    style.m_fileFormat=int(libwps::readU8(m_input));
958
49.5k
    for (int i=0; i<2; ++i)   // g1=0|f8
959
33.0k
    {
960
33.0k
      val=int(libwps::readU8(m_input));
961
33.0k
      if (val) f << "g" << i+1 << "=" << val << ",";
962
33.0k
    }
963
964
16.5k
    style.m_extra=f.str();
965
16.5k
    m_state->m_styleManager.add(id, style);
966
16.5k
    f.str("");
967
16.5k
    f << "Entries(CellStyle):Ce" << (id>>8);
968
16.5k
    if (id&0xFF)
969
12.8k
      f << "-" << (id&0xFF) << "#";
970
16.5k
    f << "," << style << ",";
971
972
16.5k
    ascii().addPos(pos);
973
16.5k
    ascii().addNote(f.str().c_str());
974
16.5k
    return true;
975
16.5k
  }
976
8.50k
  ascii().addPos(pos);
977
8.50k
  ascii().addNote(f.str().c_str());
978
8.50k
  auto N=int(sz/12);
979
26.6k
  for (int i=0; i<N; ++i)
980
18.1k
  {
981
18.1k
    pos=m_input->tell();
982
18.1k
    QuattroDosSpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
983
18.1k
    f.str("");
984
18.1k
    auto id=int(libwps::readU16(m_input));
985
986
18.1k
    WPSFont font;
987
18.1k
    auto flags = int(libwps::readU16(m_input));
988
18.1k
    uint32_t attributes = 0;
989
18.1k
    if (flags & 1) attributes |= WPS_BOLD_BIT;
990
18.1k
    if (flags & 2) attributes |= WPS_ITALICS_BIT;
991
18.1k
    if (flags & 8) attributes |= WPS_UNDERLINE_BIT;
992
993
18.1k
    font.m_attributes=attributes;
994
18.1k
    if (flags & 0xFFF4)
995
12.5k
      f << "fl=" << std::hex << (flags & 0xFFF4) << std::dec << ",";
996
18.1k
    auto fId=int(libwps::readU16(m_input));
997
18.1k
    f << "fId=" << fId << ",";
998
18.1k
    auto fSize = int(libwps::readU16(m_input));
999
18.1k
    if (fSize >= 1 && fSize <= 50)
1000
2.44k
      font.m_size=double(fSize);
1001
15.6k
    else
1002
15.6k
      f << "###fSize=" << fSize << ",";
1003
18.1k
    auto color = int(libwps::readU16(m_input));
1004
18.1k
    if (color && !m_mainParser.getColor(color, font.m_color))
1005
11.2k
    {
1006
11.2k
      WPS_DEBUG_MSG(("QuattroDosParser::readCellStyle: unknown color\n"));
1007
11.2k
      f << "##color=" << color << ",";
1008
11.2k
    }
1009
18.1k
    style.setFont(font);
1010
18.1k
    auto val=int(libwps::readU16(m_input));
1011
18.1k
    f << "f0=" << std::hex << val << std::dec << ",";
1012
1013
18.1k
    style.m_extra=f.str();
1014
18.1k
    m_state->m_styleManager.add(id, style);
1015
18.1k
    f.str("");
1016
18.1k
    f << "CellStyle-" << i << ":Ce" << id << "," << style << ",";
1017
1018
18.1k
    ascii().addPos(pos);
1019
18.1k
    ascii().addNote(f.str().c_str());
1020
18.1k
    m_input->seek(pos+12, librevenge::RVNG_SEEK_SET);
1021
18.1k
  }
1022
8.50k
  return true;
1023
25.0k
}
1024
1025
bool QuattroDosSpreadsheet::readCellProperty()
1026
14.5k
{
1027
14.5k
  libwps::DebugStream f;
1028
14.5k
  long pos = m_input->tell();
1029
14.5k
  long type = libwps::read16(m_input);
1030
14.5k
  auto defFontType=m_mainParser.getDefaultFontType();
1031
1032
14.5k
  if (type != 0x9d)
1033
0
  {
1034
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCellProperty: not a cell property zone zone\n"));
1035
0
    return false;
1036
0
  }
1037
14.5k
  f << "Entries(CellProperty):";
1038
14.5k
  long sz = libwps::readU16(m_input);
1039
14.5k
  if (sz!=7)
1040
762
  {
1041
762
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCellProperty: the size seems bad\n"));
1042
762
    f << "###sz";
1043
762
    ascii().addPos(pos);
1044
762
    ascii().addNote(f.str().c_str());
1045
762
    return true;
1046
762
  }
1047
13.7k
  auto format=int(libwps::readU8(m_input));
1048
13.7k
  auto col=int(libwps::read16(m_input));
1049
13.7k
  auto row=int(libwps::read16(m_input));
1050
13.7k
  QuattroDosSpreadsheetInternal::Cell emptyCell(defFontType);
1051
13.7k
  auto *cell=&emptyCell;
1052
13.7k
  if (col<0 || row<0)
1053
1.42k
  {
1054
1.42k
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCellProperty: the position seems bad\n"));
1055
1.42k
    f << "###";
1056
1.42k
  }
1057
12.3k
  else
1058
12.3k
    cell = &m_state->getActualSheet().getCell(Vec2i(col,row),defFontType);
1059
13.7k
  f << "C" << Vec2i(col,row) << ",";
1060
13.7k
  auto flag=int(libwps::readU8(m_input));
1061
13.7k
  auto id=int(libwps::readU8(m_input));
1062
13.7k
  if (id&0x80)
1063
7.11k
  {
1064
7.11k
    f << "Ce" << (id&0x7f) << ",";
1065
7.11k
    QuattroDosSpreadsheetInternal::Style style(cell->m_fontType);
1066
7.11k
    if (!m_state->m_styleManager.get((id&0x7f), style))
1067
6.53k
    {
1068
6.53k
      WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCellProperty: can not find a style\n"));
1069
6.53k
      f << "###";
1070
6.53k
    }
1071
579
    else
1072
579
    {
1073
579
      if (style.m_fileFormat==0xFF)
1074
385
        cell->m_fileFormat=style.m_fileFormat;
1075
579
      cell->m_fontType=style.m_fontType;
1076
579
      cell->setFont(style.getFont());
1077
579
      cell->setBackgroundColor(style.backgroundColor());
1078
579
      if (style.hasBorders())
1079
135
        cell->setBorders(style.borders());
1080
579
    }
1081
7.11k
  }
1082
6.67k
  else if (id)
1083
3.99k
  {
1084
3.99k
    if (id&0x7c)
1085
3.91k
      f << "Fo" << (id>>2) << ",";
1086
3.99k
    WPSFont font;
1087
3.99k
    if (!m_mainParser.getFont(id>>2, font, cell->m_fontType))
1088
3.92k
    {
1089
3.92k
      WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCellProperty: can not find a font\n"));
1090
3.92k
      f << "###";
1091
3.92k
    }
1092
75
    else
1093
75
      cell->setFont(font);
1094
3.99k
    if (id&0x3)
1095
3.51k
      f << "f0=" << (id&3) << ",";
1096
3.99k
  }
1097
13.7k
  if (format!=0xFF)
1098
12.1k
  {
1099
12.1k
    cell->m_fileFormat=format;
1100
12.1k
    f << "form=" << std::hex << format << std::dec << ",";
1101
12.1k
  }
1102
13.7k
  switch (flag>>6)
1103
13.7k
  {
1104
737
  case 1:
1105
737
    cell->setHAlignment(WPSCellFormat::HALIGN_LEFT);
1106
737
    f << "left,";
1107
737
    break;
1108
685
  case 2:
1109
685
    cell->setHAlignment(WPSCellFormat::HALIGN_RIGHT);
1110
685
    f << "right,";
1111
685
    break;
1112
1.23k
  case 3:
1113
1.23k
    cell->setHAlignment(WPSCellFormat::HALIGN_CENTER);
1114
1.23k
    f << "center,";
1115
1.23k
    break;
1116
0
  default:
1117
11.1k
  case 0:
1118
11.1k
    break;
1119
13.7k
  }
1120
41.3k
  for (int i=0; i<2; ++i)
1121
27.5k
  {
1122
27.5k
    int bd=(flag>>(2*i))&0x3;
1123
27.5k
    if (!bd) continue;
1124
14.3k
    f << "bord" << (i==0 ? "T" : "L");
1125
14.3k
    WPSBorder border;
1126
14.3k
    switch (bd)
1127
14.3k
    {
1128
6.94k
    case 2:
1129
6.94k
      border.m_type = WPSBorder::Double;
1130
6.94k
      f << "[double]";
1131
6.94k
      break;
1132
4.67k
    case 3:
1133
4.67k
      border.m_width=2;
1134
4.67k
      f << "[w=2]";
1135
4.67k
      break;
1136
0
    default:
1137
2.75k
    case 1: // normal
1138
2.75k
      break;
1139
14.3k
    }
1140
14.3k
    f << ",";
1141
14.3k
    cell->setBorders(i==0 ? WPSBorder::TopBit : WPSBorder::LeftBit, border);
1142
14.3k
  }
1143
13.7k
  switch ((flag>>4)&3)
1144
13.7k
  {
1145
4.13k
  case 1:
1146
4.13k
    cell->setBackgroundColor(WPSColor(0x80,0x80,0x80));
1147
4.13k
    f << "back[grey],";
1148
4.13k
    break;
1149
1.82k
  case 2:
1150
1.82k
    cell->setBackgroundColor(WPSColor::black());
1151
1.82k
    f << "back[black],";
1152
1.82k
    break;
1153
1.19k
  case 3:
1154
1.19k
    f << "#back[color]=3";
1155
1.19k
    break;
1156
6.63k
  case 0:
1157
6.63k
  default:
1158
6.63k
    break;
1159
13.7k
  }
1160
13.7k
  ascii().addPos(pos);
1161
13.7k
  ascii().addNote(f.str().c_str());
1162
13.7k
  return true;
1163
13.7k
}
1164
1165
bool QuattroDosSpreadsheet::readUserStyle()
1166
13.8k
{
1167
13.8k
  libwps::DebugStream f;
1168
13.8k
  long pos = m_input->tell();
1169
13.8k
  long type = libwps::read16(m_input);
1170
13.8k
  int const vers=version();
1171
13.8k
  if (type != 0xc9)
1172
0
  {
1173
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readUserStyle: not a style zone\n"));
1174
0
    return false;
1175
0
  }
1176
13.8k
  long sz = libwps::readU16(m_input);
1177
13.8k
  f << "Entries(UserStyle):";
1178
13.8k
  if ((vers==1&&sz!=0x2a) || (vers>1 && sz!=0x24))
1179
1.31k
  {
1180
1.31k
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readUserStyle: size seems bad\n"));
1181
1.31k
    f << "###";
1182
1.31k
    ascii().addPos(pos);
1183
1.31k
    ascii().addNote(f.str().c_str());
1184
1.31k
    return true;
1185
1.31k
  }
1186
12.5k
  if (vers>1)
1187
3.09k
  {
1188
3.09k
    f.str("");
1189
3.09k
    librevenge::RVNGString name("");
1190
3.09k
    if (!m_mainParser.readPString(name,15))
1191
213
      f << "##sSz,";
1192
2.87k
    else if (!name.empty())
1193
1.00k
      f << name.cstr() << ",";
1194
3.09k
    m_input->seek(pos+20, librevenge::RVNG_SEEK_SET);
1195
1196
3.09k
    QuattroDosSpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
1197
3.09k
    auto id=int(libwps::readU16(m_input));
1198
3.09k
    int val;
1199
15.4k
    for (int i=0; i<4; ++i)   // small int, unknown
1200
12.3k
    {
1201
12.3k
      val=int(libwps::readU8(m_input));
1202
12.3k
      if (val) f << "f" << i << "=" << val << ",";
1203
12.3k
    }
1204
1205
3.09k
    WPSFont font;
1206
3.09k
    auto flags = int(libwps::readU16(m_input));
1207
3.09k
    uint32_t attributes = 0;
1208
3.09k
    if (flags & 1) attributes |= WPS_BOLD_BIT;
1209
3.09k
    if (flags & 2) attributes |= WPS_ITALICS_BIT;
1210
3.09k
    if (flags & 8) attributes |= WPS_UNDERLINE_BIT;
1211
1212
3.09k
    font.m_attributes=attributes;
1213
3.09k
    if (flags & 0xFFF4)
1214
2.57k
      f << "fl=" << std::hex << (flags & 0xFFF4) << std::dec << ",";
1215
3.09k
    auto fId=int(libwps::readU16(m_input));
1216
3.09k
    f << "fId=" << fId << ",";
1217
3.09k
    auto fSize = int(libwps::readU16(m_input));
1218
3.09k
    if (fSize >= 1 && fSize <= 50)
1219
377
      font.m_size=double(fSize);
1220
2.71k
    else
1221
2.71k
      f << "###fSize=" << fSize << ",";
1222
3.09k
    auto color = int(libwps::readU16(m_input));
1223
3.09k
    if (color && !m_mainParser.getColor(color, font.m_color))
1224
2.31k
    {
1225
2.31k
      WPS_DEBUG_MSG(("QuattroDosParser::readCellStyle: unknown color\n"));
1226
2.31k
      f << "##color=" << color << ",";
1227
2.31k
    }
1228
3.09k
    style.setFont(font);
1229
3.09k
    val=int(libwps::readU8(m_input));
1230
3.09k
    if (val)
1231
2.09k
    {
1232
10.4k
      for (int i=0, depl=0; i<4; ++i, depl+=2)
1233
8.36k
      {
1234
8.36k
        int bd=(val>>depl)&3;
1235
8.36k
        if (!bd) continue;
1236
6.44k
        WPSBorder border;
1237
6.44k
        switch (bd)
1238
6.44k
        {
1239
2.82k
        case 1: // normal
1240
2.82k
          break;
1241
1.63k
        case 2:
1242
1.63k
          border.m_type = WPSBorder::Double;
1243
1.63k
          break;
1244
1.97k
        case 3:
1245
1.97k
          border.m_width=2;
1246
1.97k
          break;
1247
0
        default:
1248
0
          break;
1249
6.44k
        }
1250
6.44k
        int const wh[]= {WPSBorder::TopBit, WPSBorder::LeftBit, WPSBorder::BottomBit, WPSBorder::RightBit};
1251
6.44k
        style.setBorders(wh[i], border);
1252
6.44k
      }
1253
2.09k
    }
1254
3.09k
    val=int(libwps::readU8(m_input)); // 8|10|18
1255
3.09k
    if (val) f << "g0=" << val << ",";
1256
3.09k
    val=int(libwps::readU8(m_input));
1257
3.09k
    switch (val)
1258
3.09k
    {
1259
1.13k
    case 0:
1260
1.13k
      break;
1261
140
    case 1:
1262
140
      style.setBackgroundColor(WPSColor(0x80,0x80,0x80));
1263
140
      break;
1264
1.11k
    case 2:
1265
1.11k
      style.setBackgroundColor(WPSColor::black());
1266
1.11k
      break;
1267
709
    default:
1268
709
      f << "##background=" << val << ",";
1269
709
      break;
1270
3.09k
    }
1271
3.09k
    style.m_fileFormat=int(libwps::readU8(m_input));
1272
9.27k
    for (int i=0; i<2; ++i)   // g1=0|f8
1273
6.18k
    {
1274
6.18k
      val=int(libwps::readU8(m_input));
1275
6.18k
      if (val) f << "g" << i+1 << "=" << val << ",";
1276
6.18k
    }
1277
1278
3.09k
    style.m_extra=f.str();
1279
3.09k
    m_state->m_styleManager.add(id, style);
1280
3.09k
    f.str("");
1281
3.09k
    f << "Entries(UserStyle):";
1282
3.09k
    if (id!=0xFF0F)
1283
3.07k
    {
1284
3.07k
      f << "Ce" << (id>>8) << "-" << (id&0xFF);
1285
3.07k
      if ((id&0xFF)!=8)
1286
2.95k
        f  << "#";
1287
3.07k
    }
1288
3.09k
    f << "," << style << ",";
1289
1290
3.09k
    ascii().addPos(pos);
1291
3.09k
    ascii().addNote(f.str().c_str());
1292
3.09k
    return true;
1293
3.09k
  }
1294
1295
9.45k
  QuattroDosSpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
1296
9.45k
  auto id=int(libwps::readU16(m_input));
1297
9.45k
  f.str("");
1298
9.45k
  auto flags = int(libwps::readU16(m_input));
1299
1300
9.45k
  WPSFont font;
1301
9.45k
  uint32_t attributes = 0;
1302
9.45k
  if (flags & 1) attributes |= WPS_BOLD_BIT;
1303
9.45k
  if (flags & 2) attributes |= WPS_ITALICS_BIT;
1304
9.45k
  if (flags & 8) attributes |= WPS_UNDERLINE_BIT;
1305
1306
9.45k
  font.m_attributes=attributes;
1307
9.45k
  if (flags & 0xFFF4)
1308
6.81k
    f << "font[fl]=" << std::hex << (flags & 0xFFF4) << std::dec << ",";
1309
9.45k
  auto fId=int(libwps::readU16(m_input));
1310
9.45k
  f << "fId=" << fId << ",";
1311
9.45k
  auto fSize = int(libwps::readU16(m_input));
1312
9.45k
  if (fSize >= 1 && fSize <= 50)
1313
3.51k
    font.m_size=double(fSize);
1314
5.93k
  else
1315
5.93k
    f << "###fSize=" << fSize << ",";
1316
9.45k
  auto color = int(libwps::readU16(m_input));
1317
9.45k
  if (color && !m_mainParser.getColor(color, font.m_color))
1318
4.80k
  {
1319
4.80k
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readUserStyle: unknown color\n"));
1320
4.80k
    f << "##font[color]=" << color << ",";
1321
4.80k
  }
1322
9.45k
  style.setFont(font);
1323
9.45k
  auto val=int(libwps::read16(m_input));
1324
9.45k
  if (val!=-1) f << "f1=" << val << ",";
1325
9.45k
  librevenge::RVNGString name("");
1326
9.45k
  if (!m_mainParser.readPString(name,15))
1327
2.33k
    f << "##sSz,";
1328
7.11k
  else if (!name.empty())
1329
2.60k
    f << name.cstr() << ",";
1330
9.45k
  m_input->seek(pos+32, librevenge::RVNG_SEEK_SET);
1331
9.45k
  val=int(libwps::readU16(m_input)); // probably list of modified flags
1332
9.45k
  if (val)
1333
5.83k
    f << "wh=" << std::hex << val << std::dec << ",";
1334
47.2k
  for (int i=0; i<4; ++i)
1335
37.8k
  {
1336
37.8k
    val=int(libwps::readU8(m_input));
1337
37.8k
    if (!val) continue;
1338
15.8k
    WPSBorder border;
1339
15.8k
    switch (val)
1340
15.8k
    {
1341
1.08k
    case 1: // normal
1342
1.08k
      break;
1343
1.46k
    case 2:
1344
1.46k
      border.m_type = WPSBorder::Double;
1345
1.46k
      break;
1346
1.96k
    case 3:
1347
1.96k
      border.m_width=2;
1348
1.96k
      break;
1349
11.3k
    default:
1350
11.3k
      f << "#border" << i << "=" << val << ",";
1351
11.3k
      break;
1352
15.8k
    }
1353
15.8k
    int const wh[]= {WPSBorder::TopBit, WPSBorder::LeftBit, WPSBorder::BottomBit, WPSBorder::RightBit};
1354
15.8k
    style.setBorders(wh[i], border);
1355
15.8k
  }
1356
9.45k
  val=int(libwps::readU8(m_input));
1357
9.45k
  switch (val)
1358
9.45k
  {
1359
4.56k
  case 0:
1360
4.56k
    break;
1361
94
  case 1:
1362
94
    style.setBackgroundColor(WPSColor(0x80,0x80,0x80));
1363
94
    break;
1364
440
  case 2:
1365
440
    style.setBackgroundColor(WPSColor::black());
1366
440
    break;
1367
4.35k
  default:
1368
4.35k
    f << "#back[color]=" << val << ",";
1369
4.35k
    break;
1370
9.45k
  }
1371
9.45k
  val=int(libwps::readU8(m_input));
1372
9.45k
  switch (val)
1373
9.45k
  {
1374
6.48k
  case 0:
1375
6.48k
    break;
1376
65
  case 1:
1377
65
    style.setHAlignment(WPSCellFormat::HALIGN_LEFT);
1378
65
    break;
1379
76
  case 2:
1380
76
    style.setHAlignment(WPSCellFormat::HALIGN_RIGHT);
1381
76
    break;
1382
912
  case 3:
1383
912
    style.setHAlignment(WPSCellFormat::HALIGN_CENTER);
1384
912
    break;
1385
1.91k
  default:
1386
1.91k
    f << "#align=" << val << ",";
1387
1.91k
    break;
1388
9.45k
  }
1389
9.45k
  val=int(libwps::readU8(m_input));
1390
9.45k
  switch (val)
1391
9.45k
  {
1392
6.49k
  case 0:
1393
6.49k
    break;
1394
447
  case 1:
1395
447
    f << "input=labels[only],";
1396
447
    break;
1397
29
  case 2:
1398
29
    f << "input=dates[only],";
1399
29
    break;
1400
2.48k
  default:
1401
2.48k
    f << "#input=" << val << ",";
1402
2.48k
    break;
1403
9.45k
  }
1404
9.45k
  style.m_fileFormat=int(libwps::readU8(m_input));
1405
37.8k
  for (int i=0; i<3; ++i)
1406
28.3k
  {
1407
28.3k
    val=int(libwps::read8(m_input));
1408
28.3k
    if (val) f << "f" << i+2 << "=" << val << ",";
1409
28.3k
  }
1410
1411
9.45k
  style.m_extra=f.str();
1412
9.45k
  m_state->m_styleManager.add(id, style);
1413
9.45k
  f.str("");
1414
9.45k
  f << "Entries(UserStyle):Ce" << id << "," << style << ",";
1415
9.45k
  ascii().addPos(pos);
1416
9.45k
  ascii().addNote(f.str().c_str());
1417
9.45k
  return true;
1418
9.45k
}
1419
1420
////////////////////////////////////////////////////////////
1421
// general
1422
////////////////////////////////////////////////////////////
1423
1424
bool QuattroDosSpreadsheet::readCell()
1425
90.9k
{
1426
90.9k
  libwps::DebugStream f;
1427
1428
90.9k
  long pos = m_input->tell();
1429
90.9k
  long type = libwps::read16(m_input);
1430
90.9k
  if (type < 0xc || type > 0x10)
1431
0
  {
1432
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCell: not a cell property\n"));
1433
0
    return false;
1434
0
  }
1435
90.9k
  long sz = libwps::readU16(m_input);
1436
90.9k
  long endPos = pos+4+sz;
1437
1438
90.9k
  if (sz < 5)
1439
85
  {
1440
85
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCell: cell def is too short\n"));
1441
85
    return false;
1442
85
  }
1443
90.9k
  int const vers=version();
1444
90.9k
  auto defFontType=m_mainParser.getDefaultFontType();
1445
90.9k
  bool dosFile = vers<=1;
1446
90.9k
  int format=0xFF;
1447
90.9k
  if (dosFile)
1448
60.0k
    format = int(libwps::readU8(m_input));
1449
90.9k
  int cellPos[2];
1450
90.9k
  cellPos[0]=int(libwps::readU8(m_input));
1451
90.9k
  auto sheetId=int(libwps::readU8(m_input));
1452
90.9k
  cellPos[1]=int(libwps::read16(m_input));
1453
90.9k
  if (cellPos[1] < 0)
1454
69
  {
1455
69
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCell: cell pos is bad\n"));
1456
69
    return false;
1457
69
  }
1458
90.8k
  if (sheetId)
1459
25.3k
  {
1460
25.3k
    if (vers==1)
1461
15.0k
    {
1462
15.0k
      WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCell: find unexpected sheet id\n"));
1463
15.0k
      f << "###";
1464
15.0k
    }
1465
25.3k
    f << "sheet[id]=" << sheetId << ",";
1466
25.3k
  }
1467
1468
90.8k
  auto &cell=m_state->getActualSheet().getCell(Vec2i(cellPos[0],cellPos[1]), defFontType);
1469
90.8k
  cell.m_fileFormat=format;
1470
90.8k
  if (!dosFile)
1471
30.8k
  {
1472
30.8k
    auto id=int(libwps::readU16(m_input));
1473
30.8k
    if (id!=0xFF0F)
1474
29.5k
    {
1475
29.5k
      f << "Ce" << (id>>8);
1476
29.5k
      if (id&0xFF) f << "-" << (id&0xFF);
1477
29.5k
      f << ",";
1478
29.5k
    }
1479
30.8k
    QuattroDosSpreadsheetInternal::Style style(defFontType);
1480
30.8k
    if (!m_state->m_styleManager.get(id, style))
1481
24.4k
      f << "###";
1482
6.35k
    else
1483
6.35k
    {
1484
6.35k
      cell.m_fileFormat=style.m_fileFormat;
1485
6.35k
      cell.m_fontType=style.m_fontType;
1486
6.35k
      cell.setFont(style.getFont());
1487
6.35k
      cell.setBackgroundColor(style.backgroundColor());
1488
6.35k
      if (style.hasBorders())
1489
2.19k
        cell.setBorders(style.borders());
1490
6.35k
    }
1491
30.8k
  }
1492
1493
90.8k
  long dataPos = m_input->tell();
1494
90.8k
  auto dataSz = int(endPos-dataPos);
1495
1496
90.8k
  bool ok = true;
1497
90.8k
  switch (type)
1498
90.8k
  {
1499
10.8k
  case 12:
1500
10.8k
  {
1501
10.8k
    if (dataSz == 0)
1502
7.84k
    {
1503
7.84k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_NONE;
1504
7.84k
      break;
1505
7.84k
    }
1506
2.97k
    ok = false;
1507
2.97k
    break;
1508
10.8k
  }
1509
14.7k
  case 13:
1510
14.7k
  {
1511
14.7k
    if (dataSz == 2)
1512
9.13k
    {
1513
9.13k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
1514
9.13k
      cell.m_content.setValue(libwps::read16(m_input));
1515
9.13k
      break;
1516
9.13k
    }
1517
5.63k
    ok = false;
1518
5.63k
    break;
1519
14.7k
  }
1520
14.0k
  case 14:
1521
14.0k
  {
1522
14.0k
    long double val;
1523
14.0k
    bool isNaN;
1524
14.0k
    if (dataSz == 8 && libwps::readDouble8(m_input, val, isNaN))
1525
5.14k
    {
1526
5.14k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
1527
5.14k
      cell.m_content.setValue(val);
1528
5.14k
      break;
1529
5.14k
    }
1530
8.86k
    ok = false;
1531
8.86k
    break;
1532
14.0k
  }
1533
15.0k
  case 15:
1534
15.0k
  {
1535
15.0k
    cell.m_content.m_contentType=WKSContentListener::CellContent::C_TEXT;
1536
15.0k
    long begText=m_input->tell()+2, endText=begText+dataSz-2;
1537
    // pascal string
1538
15.0k
    auto align=char(libwps::readU8(m_input));
1539
15.0k
    if (align=='\'') cell.setHAlignment(WPSCellFormat::HALIGN_DEFAULT);
1540
5.70k
    else if (align=='\\') cell.setHAlignment(WPSCellFormat::HALIGN_LEFT);
1541
5.45k
    else if (align=='^') cell.setHAlignment(WPSCellFormat::HALIGN_CENTER);
1542
5.07k
    else if (align=='\"') cell.setHAlignment(WPSCellFormat::HALIGN_RIGHT);
1543
4.19k
    else f << "#align=" << int(align) << ",";
1544
1545
15.0k
    librevenge::RVNGString text("");
1546
15.0k
    if (!m_mainParser.readPString(text,dataSz-2))
1547
12.9k
      f << "##sSz,";
1548
2.06k
    else
1549
2.06k
    {
1550
2.06k
      if (endText!=m_input->tell())
1551
1.28k
      {
1552
1.28k
        endText=m_input->tell();
1553
1.28k
        f << "#extra,";
1554
1.28k
        ascii().addDelimiter(m_input->tell(), '|');
1555
1.28k
      }
1556
2.06k
      if (!text.empty())
1557
1.08k
        f << text.cstr() << ",";
1558
2.06k
    }
1559
15.0k
    cell.m_content.m_textEntry.setBegin(begText);
1560
15.0k
    cell.m_content.m_textEntry.setEnd(endText);
1561
15.0k
    break;
1562
14.0k
  }
1563
36.2k
  case 16:
1564
36.2k
  {
1565
36.2k
    long double val;
1566
36.2k
    bool isNaN;
1567
36.2k
    if (dataSz >= 8 && libwps::readDouble8(m_input, val, isNaN))
1568
33.4k
    {
1569
33.4k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_FORMULA;
1570
33.4k
      cell.m_content.setValue(val);
1571
33.4k
      std::string error;
1572
33.4k
      if (!readFormula(endPos, cell.position(), sheetId, cell.m_content.m_formula, error))
1573
32.9k
      {
1574
32.9k
        cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
1575
32.9k
        ascii().addDelimiter(m_input->tell()-1, '#');
1576
32.9k
      }
1577
33.4k
      if (error.length()) f << error;
1578
33.4k
      break;
1579
33.4k
    }
1580
2.74k
    ok = false;
1581
2.74k
    break;
1582
36.2k
  }
1583
0
  default:
1584
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCell: unknown type=%ld\n", type));
1585
0
    ok = false;
1586
0
    break;
1587
90.8k
  }
1588
90.8k
  if (!ok) ascii().addDelimiter(dataPos, '#');
1589
1590
90.8k
  if (dosFile)
1591
60.0k
  {
1592
60.0k
    switch (cell.m_content.m_contentType)
1593
60.0k
    {
1594
7.94k
    case WKSContentListener::CellContent::C_NONE:
1595
7.94k
      break;
1596
13.7k
    case WKSContentListener::CellContent::C_TEXT:
1597
13.7k
      cell.setFormat(WPSCell::F_TEXT);
1598
13.7k
      break;
1599
29.9k
    case WKSContentListener::CellContent::C_NUMBER:
1600
29.9k
    case WKSContentListener::CellContent::C_FORMULA:
1601
38.3k
    case WKSContentListener::CellContent::C_UNKNOWN:
1602
38.3k
    default:
1603
38.3k
      cell.setFormat(WPSCell::F_NUMBER);
1604
38.3k
      break;
1605
60.0k
    }
1606
1607
60.0k
  }
1608
90.8k
  m_input->seek(pos+sz, librevenge::RVNG_SEEK_SET);
1609
1610
90.8k
  std::string extra=f.str();
1611
90.8k
  f.str("");
1612
90.8k
  f << "Entries(CellContent):" << cell << "," << extra;
1613
1614
90.8k
  ascii().addPos(pos);
1615
90.8k
  ascii().addNote(f.str().c_str());
1616
1617
90.8k
  return true;
1618
90.8k
}
1619
1620
bool QuattroDosSpreadsheet::readCellFormulaResult()
1621
1.12k
{
1622
1.12k
  libwps::DebugStream f;
1623
1624
1.12k
  long pos = m_input->tell();
1625
1.12k
  long type = libwps::read16(m_input);
1626
1.12k
  if (type != 0x33)
1627
0
  {
1628
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCellFormulaResult: not a cell property\n"));
1629
0
    return false;
1630
0
  }
1631
1.12k
  long sz = libwps::readU16(m_input);
1632
1.12k
  if (sz<6)
1633
10
  {
1634
10
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCellFormulaResult: the zone seems to short\n"));
1635
10
    return false;
1636
10
  }
1637
1.11k
  long endPos = pos+4+sz;
1638
1639
1.11k
  bool dosFile = version() <= 1;
1640
  // skip format for dosFile
1641
1.11k
  m_input->seek(dosFile ? pos+5 : pos+4, librevenge::RVNG_SEEK_SET);
1642
1.11k
  f << "CellContent[res]:";
1643
1.11k
  int dim[2];
1644
2.23k
  for (int &i : dim) i=int(libwps::readU16(m_input));
1645
1.11k
  f << "C" << dim[0] << "x" << dim[1] << ",";
1646
  // skip format for windows file
1647
1.11k
  if (!dosFile) m_input->seek(2, librevenge::RVNG_SEEK_CUR);
1648
1.11k
  librevenge::RVNGString text("");
1649
1.11k
  if (!m_mainParser.readPString(text,endPos-m_input->tell()-1))
1650
545
    f << "##sSz,";
1651
570
  else if (!text.empty())
1652
328
    f << text.cstr() << ",";
1653
1.11k
  ascii().addPos(pos);
1654
1.11k
  ascii().addNote(f.str().c_str());
1655
1.11k
  return true;
1656
1.12k
}
1657
1658
////////////////////////////////////////////////////////////
1659
// Data
1660
////////////////////////////////////////////////////////////
1661
bool QuattroDosSpreadsheet::readSpreadsheetName()
1662
3.62k
{
1663
3.62k
  libwps::DebugStream f;
1664
1665
3.62k
  long pos = m_input->tell();
1666
3.62k
  auto type = long(libwps::readU16(m_input));
1667
3.62k
  if (type != 0xde)
1668
0
  {
1669
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSpreadsheetName: not a spreadsheet header\n"));
1670
0
    return false;
1671
0
  }
1672
3.62k
  auto sz = int(libwps::readU16(m_input));
1673
3.62k
  f << "Entries(SheetName):";
1674
3.62k
  librevenge::RVNGString name("");
1675
3.62k
  if (!m_mainParser.readPString(name,sz-1))
1676
1.60k
    f << "##sSz,";
1677
2.02k
  else if (!name.empty())
1678
1.44k
  {
1679
1.44k
    f << name.cstr() << ",";
1680
1.44k
    if (m_state->m_spreadsheetStack.empty() || !m_state->m_spreadsheetStack.top())
1681
0
    {
1682
0
      WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSpreadsheetName: can not find the current sheet\n"));
1683
0
      f << "###current";
1684
0
    }
1685
1.44k
    else if (m_state->m_idToSheetNameMap.find(m_state->getActualSheet().m_id)!=m_state->m_idToSheetNameMap.end())
1686
933
    {
1687
933
      WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSpreadsheetName: id=%d dupplicated\n", m_state->getActualSheet().m_id));
1688
933
      f << "###id";
1689
933
    }
1690
507
    else
1691
507
      m_state->m_idToSheetNameMap[m_state->getActualSheet().m_id]=m_state->getUniqueSheetName(name);
1692
1.44k
  }
1693
3.62k
  ascii().addPos(pos);
1694
3.62k
  ascii().addNote(f.str().c_str());
1695
3.62k
  return true;
1696
3.62k
}
1697
1698
bool QuattroDosSpreadsheet::readSpreadsheetOpen()
1699
674k
{
1700
674k
  libwps::DebugStream f;
1701
1702
674k
  long pos = m_input->tell();
1703
674k
  auto type = long(libwps::readU16(m_input));
1704
674k
  if (type != 0xdc)
1705
0
  {
1706
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSpreadsheetOpen: not a spreadsheet header\n"));
1707
0
    return false;
1708
0
  }
1709
674k
  auto sz = long(libwps::readU16(m_input));
1710
674k
  f << "Entries(Spreadsheet)[beg]:";
1711
674k
  if (sz!=2)
1712
673k
  {
1713
673k
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSpreadsheetOpen: the field size seems odd\n"));
1714
673k
    f << "###";
1715
673k
  }
1716
674k
  auto id=int(libwps::readU16(m_input));
1717
674k
  if (id<0||id>255)
1718
2.46k
  {
1719
2.46k
    f << "###";
1720
2.46k
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSpreadsheetOpen: the id seems odd\n"));
1721
2.46k
  }
1722
672k
  else if (id==0 && m_state->m_spreadsheetStack.size()!=1)
1723
39.0k
  {
1724
39.0k
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSpreadsheetOpen: find 0 but the stack is not empty\n"));
1725
39.0k
    f << "###stack";
1726
39.0k
    m_state->m_spreadsheetStack.push(m_state->m_spreadsheetList[0]);
1727
39.0k
  }
1728
633k
  else if (id)
1729
629k
    m_state->pushNewSheet(std::shared_ptr<QuattroDosSpreadsheetInternal::Spreadsheet>
1730
629k
                          (new QuattroDosSpreadsheetInternal::Spreadsheet
1731
629k
                           (QuattroDosSpreadsheetInternal::Spreadsheet::T_Spreadsheet, id)));
1732
674k
  f << id << ",";
1733
674k
  ascii().addPos(pos);
1734
674k
  ascii().addNote(f.str().c_str());
1735
674k
  return true;
1736
674k
}
1737
1738
bool QuattroDosSpreadsheet::readSpreadsheetClose()
1739
1.25k
{
1740
1.25k
  libwps::DebugStream f;
1741
1742
1.25k
  long pos = m_input->tell();
1743
1.25k
  auto type = long(libwps::readU16(m_input));
1744
1.25k
  if (type != 0xdd)
1745
0
  {
1746
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSpreadsheetClose: not a spreadsheet header\n"));
1747
0
    return false;
1748
0
  }
1749
1.25k
  auto sz = long(libwps::readU16(m_input));
1750
1.25k
  f << "Entries(Spreadsheet)[end]:";
1751
1.25k
  auto const &sheet=m_state->getActualSheet();
1752
1.25k
  if (sheet.m_type!=QuattroDosSpreadsheetInternal::Spreadsheet::T_Spreadsheet)
1753
0
  {
1754
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSpreadsheetClose: can not find spreadsheet spreadsheet\n"));
1755
0
    f << "###[noOpen],";
1756
0
  }
1757
1.25k
  else if (m_state->m_spreadsheetStack.size() > 1)
1758
1.08k
    m_state->popSheet();
1759
1.25k
  if (sz!=0)
1760
69
  {
1761
69
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readSpreadsheetClose: the field size seems odd\n"));
1762
69
    f << "###";
1763
69
  }
1764
1.25k
  ascii().addPos(pos);
1765
1.25k
  ascii().addNote(f.str().c_str());
1766
1.25k
  return true;
1767
1.25k
}
1768
1769
bool QuattroDosSpreadsheet::readCell
1770
(Vec2i actPos, WKSContentListener::FormulaInstruction &instr, bool hasSheetId, int sheetId)
1771
35.6k
{
1772
35.6k
  instr=WKSContentListener::FormulaInstruction();
1773
35.6k
  instr.m_type=WKSContentListener::FormulaInstruction::F_Cell;
1774
35.6k
  bool ok = true;
1775
35.6k
  int pos[3]= {0,0,0};
1776
35.6k
  bool absolute[3] = { true, true, true};
1777
113k
  for (int dim = 0; dim < (hasSheetId ? 3 : 2); dim++)
1778
77.7k
  {
1779
77.7k
    auto val = int(libwps::readU16(m_input));
1780
77.7k
    if (dim==0 && (val&0xF00) && (val&0xF000)!=0xF000)
1781
11.5k
    {
1782
      // checkme: probably (val>>8)&0x1f
1783
11.5k
      instr.m_fileName=m_mainParser.getFileName(int((val>>8)&0xF));
1784
11.5k
      val &= 0xF0FF;
1785
11.5k
    }
1786
77.7k
    if ((val & 0xF000) == 0); // absolute value ?
1787
13.8k
    else if ((val & 0xc000) == 0x8000)   // relative ?
1788
9.54k
    {
1789
9.54k
      if (version()==1 || dim==0)
1790
7.28k
      {
1791
7.28k
        val &= 0xFF;
1792
7.28k
        if ((val & 0x80) && val+actPos[dim] >= 0x100)
1793
          // sometimes this value is odd, so do not generate errors here
1794
1.55k
          val = val - 0x100;
1795
7.28k
      }
1796
2.26k
      else
1797
2.26k
      {
1798
2.26k
        val &= 0x3FFF;
1799
2.26k
        if (val>0x1000) val = val - 0x2000;
1800
2.26k
      }
1801
9.54k
      if (dim==2)
1802
1.15k
        val += sheetId;
1803
8.39k
      else
1804
8.39k
        val += actPos[dim];
1805
9.54k
      absolute[dim] = false;
1806
9.54k
    }
1807
4.27k
    else if (val==0xFFFF)
1808
1.05k
    {
1809
1.05k
      static bool first=true;
1810
1.05k
      if (first)   // in general associated with a nan value, so maybe be normal
1811
4
      {
1812
4
        WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCell: find some ffff cell\n"));
1813
4
        first=false;
1814
4
      }
1815
1.05k
      ok = false;
1816
1.05k
    }
1817
3.22k
    else
1818
3.22k
    {
1819
3.22k
      WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCell: can not read cell %x\n", unsigned(val)));
1820
3.22k
      ok = false;
1821
3.22k
    }
1822
77.7k
    pos[dim] = val;
1823
77.7k
  }
1824
1825
35.6k
  if (pos[0] < 0 || pos[1] < 0)
1826
367
  {
1827
367
    if (ok)
1828
336
    {
1829
336
      WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readCell: can not read cell position\n"));
1830
336
    }
1831
367
    return false;
1832
367
  }
1833
35.2k
  if (pos[0]>255) // FIXME:  can happens in some list cell, so...
1834
1.55k
    pos[0]&=0xFF;
1835
35.2k
  if (hasSheetId && pos[2]>255) // CHECKME: this can also happen, unsure why
1836
833
    pos[2]%=256;
1837
1838
35.2k
  instr.m_position[0]=Vec2i(pos[0],pos[1]);
1839
35.2k
  if (hasSheetId && sheetId!=pos[2]) instr.m_sheetId[0]=pos[2];
1840
30.5k
  else if (!instr.m_fileName.empty()) instr.m_sheetId[0]=sheetId;
1841
35.2k
  instr.m_positionRelative[0]=Vec2b(!absolute[0], !absolute[1]);
1842
35.2k
  return ok;
1843
35.6k
}
1844
1845
namespace QuattroDosSpreadsheetInternal
1846
{
1847
struct Functions
1848
{
1849
  char const *m_name;
1850
  int m_arity;
1851
};
1852
1853
static Functions const s_listFunctions[] =
1854
{
1855
  { "", 0} /*SPEC: number*/, {"", 0}/*SPEC: cell*/, {"", 0}/*SPEC: cells*/, {"=", 1} /*SPEC: end of formula*/,
1856
  { "(", 1} /* SPEC: () */, {"", 0}/*SPEC: number*/, { "", -2} /*SPEC: text*/, {"", -2}/*unused*/,
1857
  { "-", 1}, {"+", 2}, {"-", 2}, {"*", 2},
1858
  { "/", 2}, { "^", 2}, {"=", 2}, {"<>", 2},
1859
1860
  { "<=", 2},{ ">=", 2},{ "<", 2},{ ">", 2},
1861
  { "And", 2},{ "Or", 2}, { "Not", 1}, { "+", 1},
1862
  { "&", 2}, { "", -2} /*unused*/,{ "", -2} /*unused*/,{ "", -2} /*unused*/,
1863
  { "", -2} /*unused*/,{ "", -2} /*unused*/,{ "", -2} /*unused*/,{ "NA", 0} /*checkme*/,
1864
1865
  { "NA", 0} /* Error*/,{ "Abs", 1},{ "Int", 1},{ "Sqrt", 1},
1866
  { "Log10", 1},{ "Ln", 1},{ "Pi", 0},{ "Sin", 1},
1867
  { "Cos", 1},{ "Tan", 1},{ "Atan2", 2},{ "Atan", 1},
1868
  { "Asin", 1},{ "Acos", 1},{ "Exp", 1},{ "Mod", 2},
1869
1870
  { "Choose", -1},{ "IsNa", 1},{ "IsError", 1},{ "False", 0},
1871
  { "True", 0},{ "Rand", 0},{ "Date", 3},{ "Now", 0},
1872
  { "PMT", 3} /*BAD*/,{ "PV", 3} /*BAD*/,{ "FV", 3} /*BAD*/,{ "IF", 3},
1873
  { "Day", 1},{ "Month", 1},{ "Year", 1},{ "Round", 2},
1874
1875
  { "Time", 3},{ "Hour", 1},{ "Minute", 1},{ "Second", 1},
1876
  { "IsNumber", 1},{ "IsText", 1},{ "Len", 1},{ "Value", 1},
1877
  { "Fixed", 2}, { "Mid", 3}, { "Char", 1},{ "Ascii", 1},
1878
  { "Find", 3},{ "DateValue", 1} /*checkme*/,{ "TimeValue", 1} /*checkme*/,{ "CellPointer", 1} /*checkme*/,
1879
1880
  { "Sum", -1},{ "Average", -1},{ "COUNT", -1},{ "Min", -1},
1881
  { "Max", -1},{ "VLookUp", 3},{ "NPV", 2}, { "Var", -1},
1882
  { "StDev", -1},{ "IRR", 2} /*BAD*/, { "HLookUp", 3},{ "DSum", 3},
1883
  { "DAvg", 3},{ "DCount", 3},{ "DMin", 3},{ "DMax", 3},
1884
1885
  { "DVar", 3},{ "DStd", 3},{ "Index", 3}, { "Columns", 1},
1886
  { "Rows", 1},{ "Rept", 2},{ "Upper", 1},{ "Lower", 1},
1887
  { "Left", 2},{ "Right", 2},{ "Replace", 4}, { "Proper", 1},
1888
  { "Cell", 1} /*checkme*/,{ "Trim", 1},{ "Clean", 1} /*UNKN*/,{ "T", 1},
1889
1890
  { "IsNonText", 1},{ "Exact", 2},{ "", -2} /*UNKN*/,{ "Indirect", 1},
1891
  { "RRI", 3}, { "TERM", 3}, { "CTERM", 3}, { "SLN", 3},
1892
  { "SYD", 4},{ "DDB", 4}, { "StDevP", -1}, { "VarP", -1},
1893
  { "DBStdDevP", -1}, { "DBVarP", -1}, { "PV", 3}, { "PMT", 5},
1894
1895
  { "FV", 3}, { "Nper", 5}, { "Rate", 5}, { "Ipmt", 4},
1896
  { "Ppmt", 6}, { "SumProduct", 2}, { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
1897
  { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
1898
  { "", -2} /*UNKN*/,{ "And", -1},{ "Or", -1},{ "Not", 1},
1899
1900
};
1901
}
1902
1903
bool QuattroDosSpreadsheet::readFormula(long endPos, Vec2i const &position, int sheetId,
1904
                                        std::vector<WKSContentListener::FormulaInstruction> &formula, std::string &error)
1905
33.4k
{
1906
33.4k
  int const vers=version();
1907
33.4k
  formula.resize(0);
1908
33.4k
  error = "";
1909
33.4k
  long pos = m_input->tell();
1910
33.4k
  if (endPos - pos < (vers==1 ? 6 : 4)) return false;
1911
32.6k
  auto sz = int(libwps::readU16(m_input));
1912
32.6k
  if (endPos-pos-2 != sz) return false;
1913
1914
23.3k
  std::vector<WKSContentListener::FormulaInstruction> listCellsPos;
1915
23.3k
  int fieldPos[2]= {0,sz};
1916
23.3k
  for (int &i : fieldPos)
1917
34.0k
  {
1918
34.0k
    i= int(libwps::readU16(m_input));
1919
34.0k
    if (vers>1) break;
1920
34.0k
  }
1921
23.3k
  if (fieldPos[0]<0||fieldPos[0]>fieldPos[1]||fieldPos[1]>sz)
1922
2.48k
  {
1923
2.48k
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readFormula: can not find the field header\n"));
1924
2.48k
    error="###fieldPos";
1925
2.48k
    return false;
1926
2.48k
  }
1927
20.8k
  int NSingle=0;
1928
20.8k
  if (fieldPos[0]!=sz)
1929
11.9k
  {
1930
11.9k
    m_input->seek(pos+2+fieldPos[0], librevenge::RVNG_SEEK_SET);
1931
11.9k
    ascii().addDelimiter(pos+2+fieldPos[0],'|');
1932
11.9k
    if (vers==1)
1933
8.44k
    {
1934
8.44k
      int N=int(sz-fieldPos[0])/4;
1935
8.44k
      NSingle=int(fieldPos[1]-fieldPos[0])/4;
1936
35.1k
      for (int i=0; i<N; ++i)
1937
29.2k
      {
1938
29.2k
        WKSContentListener::FormulaInstruction cell;
1939
29.2k
        if (!readCell(position, cell))
1940
2.52k
        {
1941
2.52k
          ascii().addDelimiter(pos+2+fieldPos[0]+i*4,'@');
1942
2.52k
          WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readFormula: can not read some cell\n"));
1943
2.52k
          error="###cell,";
1944
2.52k
          break;
1945
2.52k
        }
1946
26.6k
        listCellsPos.push_back(cell);
1947
26.6k
      }
1948
8.44k
    }
1949
3.50k
    else
1950
3.50k
    {
1951
7.95k
      while (!m_input->isEnd())
1952
7.93k
      {
1953
7.93k
        long actPos=m_input->tell();
1954
7.93k
        if (actPos+8>endPos) break;
1955
6.73k
        auto type=int(libwps::readU16(m_input));
1956
6.73k
        WKSContentListener::FormulaInstruction cell, cell2;
1957
6.73k
        if (type==0)
1958
5.23k
        {
1959
5.23k
          if (!readCell(position, cell, true, sheetId))
1960
931
          {
1961
931
            m_input->seek(actPos, librevenge::RVNG_SEEK_SET);
1962
931
            break;
1963
931
          }
1964
4.30k
          listCellsPos.push_back(cell);
1965
4.30k
          continue;
1966
5.23k
        }
1967
1.49k
        if (type!=0x1000 || actPos+14>endPos || !readCell(position, cell, true, sheetId) ||
1968
515
          !readCell(position, cell2, true, sheetId))
1969
1.35k
        {
1970
1.35k
          m_input->seek(actPos, librevenge::RVNG_SEEK_SET);
1971
1.35k
          break;
1972
1.35k
        }
1973
140
        cell.m_type=WKSContentListener::FormulaInstruction::F_CellList;
1974
140
        cell.m_position[1]=cell2.m_position[0];
1975
140
        cell.m_positionRelative[1]=cell2.m_positionRelative[0];
1976
140
        cell.m_sheetId[1]=cell2.m_sheetId[0];
1977
140
        listCellsPos.push_back(cell);
1978
140
      }
1979
3.50k
      if (m_input->tell() !=endPos)
1980
3.21k
      {
1981
3.21k
        ascii().addDelimiter(m_input->tell(),'@');
1982
3.21k
        WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readFormula: can not read some cell\n"));
1983
3.21k
        error="###cell,";
1984
3.21k
      }
1985
3.50k
    }
1986
11.9k
    m_input->seek(pos+4+(vers==1 ? 2 : 0), librevenge::RVNG_SEEK_SET);
1987
11.9k
    endPos=pos+2+fieldPos[0];
1988
11.9k
  }
1989
20.8k
  std::stringstream f;
1990
20.8k
  std::vector<std::vector<WKSContentListener::FormulaInstruction> > stack;
1991
20.8k
  bool ok = true;
1992
20.8k
  size_t actCellId=0, actDualCellId=size_t(NSingle);
1993
53.3k
  while (long(m_input->tell()) != endPos)
1994
52.6k
  {
1995
52.6k
    long double val;
1996
52.6k
    bool isNaN;
1997
52.6k
    pos = m_input->tell();
1998
52.6k
    if (pos > endPos) return false;
1999
42.9k
    auto wh = int(libwps::readU8(m_input));
2000
42.9k
    int arity = 0;
2001
42.9k
    WKSContentListener::FormulaInstruction instr;
2002
42.9k
    switch (wh)
2003
42.9k
    {
2004
8.99k
    case 0x0:
2005
8.99k
      if (endPos-pos<9 || !libwps::readDouble8(m_input, val, isNaN))
2006
3.13k
      {
2007
3.13k
        f.str("");
2008
3.13k
        f << "###number";
2009
3.13k
        error=f.str();
2010
3.13k
        ok = false;
2011
3.13k
        break;
2012
3.13k
      }
2013
5.86k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Double;
2014
5.86k
      instr.m_doubleValue=val;
2015
5.86k
      break;
2016
778
    case 0x1:
2017
778
      if (actCellId>=listCellsPos.size())
2018
149
      {
2019
149
        f.str("");
2020
149
        f << "###unknCell" << actCellId;
2021
149
        error=f.str();
2022
149
        ok = false;
2023
149
        break;
2024
149
      }
2025
629
      instr=listCellsPos[actCellId++];
2026
629
      break;
2027
2.50k
    case 0x2:
2028
2.50k
      if (vers>=2)
2029
2.36k
      {
2030
2.36k
        if (actCellId>=listCellsPos.size())
2031
2.14k
        {
2032
2.14k
          f.str("");
2033
2.14k
          f << "###unknListCell" << actCellId;
2034
2.14k
          error=f.str();
2035
2.14k
          ok = false;
2036
2.14k
          break;
2037
2.14k
        }
2038
213
        instr=listCellsPos[actCellId++];
2039
213
        break;
2040
2.36k
      }
2041
138
      if (actDualCellId+1>=listCellsPos.size())
2042
39
      {
2043
39
        f.str("");
2044
39
        f << "###unknListCell" << actDualCellId;
2045
39
        error=f.str();
2046
39
        ok = false;
2047
39
        break;
2048
39
      }
2049
99
      instr=listCellsPos[actDualCellId++];
2050
99
      instr.m_type=WKSContentListener::FormulaInstruction::F_CellList;
2051
99
      instr.m_position[1]=listCellsPos[actDualCellId].m_position[0];
2052
99
      instr.m_positionRelative[1]=listCellsPos[actDualCellId].m_positionRelative[0];
2053
99
      ++actDualCellId;
2054
99
      break;
2055
1.35k
    case 0x5:
2056
1.35k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
2057
1.35k
      instr.m_longValue=long(libwps::read16(m_input));
2058
1.35k
      break;
2059
677
    case 0x6:
2060
677
      instr.m_type=WKSContentListener::FormulaInstruction::F_Text;
2061
8.40k
      while (!m_input->isEnd())
2062
8.39k
      {
2063
8.39k
        if (m_input->tell() >= endPos)
2064
66
        {
2065
66
          ok=false;
2066
66
          break;
2067
66
        }
2068
8.33k
        auto c = char(libwps::readU8(m_input));
2069
8.33k
        if (c==0) break;
2070
7.73k
        instr.m_content += c;
2071
7.73k
      }
2072
677
      break;
2073
28.6k
    default:
2074
28.6k
      if (wh >= 0x90 || QuattroDosSpreadsheetInternal::s_listFunctions[wh].m_arity == -2)
2075
3.98k
      {
2076
3.98k
        f.str("");
2077
3.98k
        f << "##Funct" << std::hex << wh;
2078
3.98k
        error=f.str();
2079
3.98k
        ok = false;
2080
3.98k
        break;
2081
3.98k
      }
2082
24.6k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
2083
24.6k
      instr.m_content=QuattroDosSpreadsheetInternal::s_listFunctions[wh].m_name;
2084
24.6k
      ok=!instr.m_content.empty();
2085
24.6k
      arity = QuattroDosSpreadsheetInternal::s_listFunctions[wh].m_arity;
2086
24.6k
      if (arity == -1) arity = int(libwps::read8(m_input));
2087
24.6k
      break;
2088
42.9k
    }
2089
2090
42.9k
    if (!ok) break;
2091
33.4k
    std::vector<WKSContentListener::FormulaInstruction> child;
2092
33.4k
    if (instr.m_type!=WKSContentListener::FormulaInstruction::F_Function)
2093
8.76k
    {
2094
8.76k
      child.push_back(instr);
2095
8.76k
      stack.push_back(child);
2096
8.76k
      continue;
2097
8.76k
    }
2098
24.6k
    size_t numElt = stack.size();
2099
24.6k
    if (int(numElt) < arity)
2100
374
    {
2101
374
      f.str("");
2102
374
      f << instr.m_content << "[##" << arity << "]";
2103
374
      error=f.str();
2104
374
      ok = false;
2105
374
      break;
2106
374
    }
2107
    //
2108
    // first treat the special cases
2109
    //
2110
24.2k
    if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && instr.m_content=="TERM")
2111
1.23k
    {
2112
      // @TERM(pmt,pint,fv) -> NPER(pint,-pmt,pv=0,fv)
2113
1.23k
      stack.resize(size_t(++numElt));
2114
1.23k
      std::swap(stack[size_t(int(numElt)-4)],stack[size_t(int(numElt)-3)]);
2115
1.23k
      std::swap(stack[size_t(int(numElt)-2)],stack[size_t(int(numElt)-1)]); // @TERM(pint,pmt,,fv)
2116
      // pv=0
2117
1.23k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
2118
1.23k
      instr.m_longValue=0;
2119
1.23k
      stack[size_t(int(numElt)-2)].push_back(instr);  // @TERM(pint,pmt,0,fv)
2120
      /// -pmt
2121
1.23k
      auto &pmt=stack[size_t(int(numElt)-3)];
2122
1.23k
      WKSContentListener::FormulaInstruction::enclose(pmt,false);
2123
1.23k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
2124
1.23k
      instr.m_content="-";
2125
1.23k
      pmt.insert(pmt.begin(), instr); // @TERM(pint,-pmt,0,fv)
2126
2127
1.23k
      arity=4;
2128
1.23k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
2129
1.23k
      instr.m_content="NPER";
2130
1.23k
    }
2131
23.0k
    else if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && instr.m_content=="CTERM")
2132
1.50k
    {
2133
      // @CTERM(pint,fv,pv) -> NPER(pint,pmt=0,-pv,fv)
2134
1.50k
      stack.resize(size_t(++numElt));
2135
1.50k
      std::swap(stack[size_t(int(numElt)-1)],stack[size_t(int(numElt)-3)]); // @CTERM(pint,,pv,fv)
2136
      // pmt=0
2137
1.50k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
2138
1.50k
      instr.m_longValue=0;
2139
1.50k
      stack[size_t(int(numElt)-3)].push_back(instr);  // @CTERM(pint,0,pv,fv)
2140
      // -pv
2141
1.50k
      auto &pv=stack[size_t(int(numElt)-2)];
2142
1.50k
      WKSContentListener::FormulaInstruction::enclose(pv,false);
2143
1.50k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
2144
1.50k
      instr.m_content="-";
2145
1.50k
      pv.insert(pv.begin(), instr); // @CTERM(pint,0,-pv,fv)
2146
2147
1.50k
      arity=4;
2148
1.50k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
2149
1.50k
      instr.m_content="NPER";
2150
1.50k
    }
2151
21.5k
    else if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && (instr.m_content=="VLookUp" || instr.m_content=="HLookUp"))
2152
992
      WKSContentListener::FormulaInstruction::add(stack[size_t(int(numElt)-1)],1);
2153
2154
24.2k
    if ((instr.m_content[0] >= 'A' && instr.m_content[0] <= 'Z') || instr.m_content[0] == '(')
2155
19.4k
    {
2156
19.4k
      if (instr.m_content[0] != '(')
2157
18.2k
        child.push_back(instr);
2158
2159
19.4k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
2160
19.4k
      instr.m_content="(";
2161
19.4k
      child.push_back(instr);
2162
65.2k
      for (int i = 0; i < arity; i++)
2163
45.7k
      {
2164
45.7k
        if (i)
2165
31.7k
        {
2166
31.7k
          instr.m_content=";";
2167
31.7k
          child.push_back(instr);
2168
31.7k
        }
2169
45.7k
        auto const &node=stack[size_t(int(numElt)-arity+i)];
2170
45.7k
        child.insert(child.end(), node.begin(), node.end());
2171
45.7k
      }
2172
19.4k
      instr.m_content=")";
2173
19.4k
      child.push_back(instr);
2174
2175
19.4k
      stack.resize(size_t(int(numElt)-arity+1));
2176
19.4k
      stack[size_t(int(numElt)-arity)] = child;
2177
19.4k
      continue;
2178
19.4k
    }
2179
4.82k
    if (arity==1)
2180
2.22k
    {
2181
2.22k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
2182
2.22k
      stack[numElt-1].insert(stack[numElt-1].begin(), instr);
2183
2.22k
      if (wh==3)
2184
533
        break;
2185
1.68k
      continue;
2186
2.22k
    }
2187
2.59k
    if (arity==2)
2188
2.59k
    {
2189
2.59k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
2190
2.59k
      stack[numElt-2].push_back(instr);
2191
2.59k
      stack[numElt-2].insert(stack[numElt-2].end(), stack[numElt-1].begin(), stack[numElt-1].end());
2192
2.59k
      stack.resize(numElt-1);
2193
2.59k
      continue;
2194
2.59k
    }
2195
0
    ok=false;
2196
0
    error = "### unexpected arity";
2197
0
    break;
2198
2.59k
  }
2199
2200
11.1k
  if (!ok) ;
2201
1.24k
  else if (stack.size()==1 && stack[0].size()>1 && stack[0][0].m_content=="=")
2202
518
  {
2203
518
    formula.insert(formula.begin(),stack[0].begin()+1,stack[0].end());
2204
518
    if (m_input->tell()!=endPos)
2205
309
    {
2206
309
      WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readFormula: find some extra data\n"));
2207
309
      error="##extra data";
2208
309
      ascii().addDelimiter(m_input->tell(),'#');
2209
309
    }
2210
518
    return true;
2211
518
  }
2212
728
  else
2213
728
    error = "###stack problem";
2214
2215
10.6k
  static bool first = true;
2216
10.6k
  if (first)
2217
4
  {
2218
4
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::readFormula: I can not read some formula\n"));
2219
4
    first = false;
2220
4
  }
2221
2222
10.6k
  f.str("");
2223
10.6k
  for (auto const &i : stack)
2224
228k
  {
2225
228k
    for (auto const &j : i)
2226
106k
      f << j << ",";
2227
228k
  }
2228
10.6k
  f << error << "###";
2229
10.6k
  error = f.str();
2230
10.6k
  return false;
2231
11.1k
}
2232
2233
////////////////////////////////////////////////////////////
2234
// send data
2235
////////////////////////////////////////////////////////////
2236
void QuattroDosSpreadsheet::sendSpreadsheet(int sId, std::map<Vec2i, Vec2i> const &chartPosMap)
2237
66.1k
{
2238
66.1k
  if (!m_listener)
2239
0
  {
2240
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::sendSpreadsheet: I can not find the listener\n"));
2241
0
    return;
2242
0
  }
2243
66.1k
  auto sheet = m_state->getSheet(QuattroDosSpreadsheetInternal::Spreadsheet::T_Spreadsheet, sId);
2244
66.1k
  if (!sheet)
2245
59.9k
  {
2246
59.9k
    if (sId==0)
2247
0
    {
2248
0
      WPS_DEBUG_MSG(("QuattroDosSpreadsheet::sendSpreadsheet: oops can not find the actual sheet\n"));
2249
0
    }
2250
59.9k
    sheet.reset(new QuattroDosSpreadsheetInternal::Spreadsheet);
2251
59.9k
  }
2252
66.1k
  sheet->insertChartPositions(chartPosMap, m_mainParser.getDefaultFontType());
2253
66.1k
  m_listener->openSheet(sheet->getWidths(), m_state->getSheetName(sId));
2254
66.1k
  sheet->compressRowHeights();
2255
66.1k
  auto it = sheet->m_positionToCellMap.begin();
2256
66.1k
  int prevRow = -1;
2257
108k
  while (it != sheet->m_positionToCellMap.end())
2258
41.9k
  {
2259
41.9k
    int row=it->first[1];
2260
41.9k
    auto const &cell=(it++)->second;
2261
41.9k
    if (row>prevRow+1)
2262
10.2k
    {
2263
25.2k
      while (row > prevRow+1)
2264
14.9k
      {
2265
14.9k
        if (prevRow != -1) m_listener->closeSheetRow();
2266
14.9k
        int numRepeat;
2267
14.9k
        float h=sheet->getRowHeight(prevRow+1, numRepeat);
2268
14.9k
        if (row<prevRow+1+numRepeat)
2269
10.2k
          numRepeat=row-1-prevRow;
2270
14.9k
        m_listener->openSheetRow(WPSRowFormat(h), numRepeat);
2271
14.9k
        prevRow+=numRepeat;
2272
14.9k
      }
2273
10.2k
    }
2274
41.9k
    if (row!=prevRow)
2275
17.3k
    {
2276
17.3k
      if (prevRow != -1) m_listener->closeSheetRow();
2277
17.3k
      m_listener->openSheetRow(WPSRowFormat(sheet->getRowHeight(++prevRow)));
2278
17.3k
    }
2279
41.9k
    auto cIt=chartPosMap.find(cell.position());
2280
41.9k
    if (cIt==chartPosMap.end())
2281
38.8k
      sendCellContent(cell, sId);
2282
3.07k
    else
2283
3.07k
      sendCellContent(cell, sId, sId, sheet->getZoneSize(cIt->first, cIt->second));
2284
41.9k
  }
2285
66.1k
  if (prevRow!=-1) m_listener->closeSheetRow();
2286
66.1k
  m_listener->closeSheet();
2287
66.1k
}
2288
2289
void QuattroDosSpreadsheet::sendCellContent(QuattroDosSpreadsheetInternal::Cell const &cell, int sheetId, int cellChartSheetId, Vec2f const &chartSize)
2290
41.9k
{
2291
41.9k
  if (m_listener.get() == nullptr)
2292
0
  {
2293
0
    WPS_DEBUG_MSG(("QuattroDosSpreadsheet::sendCellContent: I can not find the listener\n"));
2294
0
    return;
2295
0
  }
2296
2297
41.9k
  libwps_tools_win::Font::Type fontType = cell.m_fontType;
2298
41.9k
  m_listener->setFont(cell.getFont());
2299
2300
41.9k
  QuattroDosSpreadsheetInternal::Cell finalCell(cell);
2301
41.9k
  auto &content=finalCell.m_content;
2302
41.9k
  for (auto &f : content.m_formula)
2303
2.62k
  {
2304
2.62k
    if (f.m_type==WKSContentListener::FormulaInstruction::F_Cell ||
2305
2.49k
      f.m_type==WKSContentListener::FormulaInstruction::F_CellList)
2306
170
    {
2307
170
      int dim=f.m_type==WKSContentListener::FormulaInstruction::F_Cell ? 1 : 2;
2308
375
      for (int i=0; i<dim; ++i)
2309
205
      {
2310
205
        if (f.m_sheetId[i]>=0 && f.m_sheetName[i].empty() && (f.m_sheetId[i]!=sheetId || !f.m_fileName.empty()))
2311
87
          f.m_sheetName[i]=getSheetName(f.m_sheetId[i]);
2312
205
      }
2313
170
      continue;
2314
170
    }
2315
2.45k
    if (f.m_type!=WKSContentListener::FormulaInstruction::F_Text)
2316
2.37k
      continue;
2317
89
    std::string &text=f.m_content;
2318
89
    auto finalText=libwps_tools_win::Font::unicodeString(text, fontType);
2319
89
    if (finalText.empty())
2320
44
      text.clear();
2321
45
    else
2322
45
      text=finalText.cstr();
2323
89
  }
2324
41.9k
  finalCell.updateFormat();
2325
41.9k
  m_listener->openSheetCell(finalCell, content);
2326
2327
41.9k
  if (cell.m_content.m_textEntry.valid())
2328
11.3k
  {
2329
11.3k
    m_input->seek(cell.m_content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
2330
11.3k
    bool prevEOL=false;
2331
11.3k
    std::string text;
2332
180k
    while (m_input->tell()<=cell.m_content.m_textEntry.end())
2333
180k
    {
2334
180k
      bool last=m_input->isEnd() || m_input->tell()>=cell.m_content.m_textEntry.end();
2335
180k
      auto c=last ? '\0' : char(libwps::readU8(m_input));
2336
180k
      if ((c==0 || c==0xa || c==0xd) && !text.empty())
2337
25.6k
      {
2338
25.6k
        m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, fontType));
2339
25.6k
        text.clear();
2340
25.6k
      }
2341
180k
      if (last) break;
2342
168k
      if (c==0xd)
2343
1.40k
      {
2344
1.40k
        m_listener->insertEOL();
2345
1.40k
        prevEOL=true;
2346
1.40k
      }
2347
167k
      else if (c==0xa)
2348
840
      {
2349
840
        if (!prevEOL)
2350
834
        {
2351
834
          WPS_DEBUG_MSG(("QuattroDosSpreadsheet::sendCellContent: find 0xa without 0xd\n"));
2352
834
        }
2353
840
        prevEOL=false;
2354
840
      }
2355
166k
      else
2356
166k
      {
2357
166k
        text.push_back(c);
2358
166k
        prevEOL=false;
2359
166k
      }
2360
168k
    }
2361
11.3k
  }
2362
41.9k
  if (cellChartSheetId>=0)
2363
3.07k
    m_mainParser.sendChart(cellChartSheetId, cell.position(), chartSize);
2364
41.9k
  m_listener->closeSheetCell();
2365
41.9k
}
2366
2367
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */