Coverage Report

Created: 2026-07-20 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/LotusSpreadsheet.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 <map>
28
#include <regex>
29
#include <set>
30
#include <stack>
31
#include <sstream>
32
#include <utility>
33
34
#include <librevenge-stream/librevenge-stream.h>
35
36
#include "libwps_internal.h"
37
#include "libwps_tools_win.h"
38
39
#include "WKSContentListener.h"
40
#include "WKSSubDocument.h"
41
#include "WPSEntry.h"
42
#include "WPSFont.h"
43
#include "WPSCell.h"
44
#include "WPSStream.h"
45
#include "WPSTable.h"
46
47
#include "Lotus.h"
48
#include "LotusStyleManager.h"
49
50
#include "LotusSpreadsheet.h"
51
52
static const int MAX_COLUMNS = 255;
53
54
namespace LotusSpreadsheetInternal
55
{
56
57
///////////////////////////////////////////////////////////////////
58
//! a class used to store a style of a cell in LotusSpreadsheet
59
struct Style final : public WPSCellFormat
60
{
61
  //! construtor
62
  explicit Style(libwps_tools_win::Font::Type type)
63
3.00M
    : WPSCellFormat()
64
3.00M
    , m_fontType(type)
65
3.00M
    , m_extra("")
66
3.00M
  {
67
3.00M
    m_font.m_size=10;
68
3.00M
  }
69
6.72M
  Style(Style const &)=default;
70
248k
  Style &operator=(Style const &)=default;
71
  //! destructor
72
  ~Style() final;
73
  //! operator<<
74
  friend std::ostream &operator<<(std::ostream &o, Style const &style)
75
0
  {
76
0
    o << static_cast<WPSCellFormat const &>(style) << ",";
77
0
    if (!style.m_extra.empty()) o << style.m_extra;
78
0
    return o;
79
0
  }
80
  //! operator==
81
  bool operator==(Style const &st) const
82
0
  {
83
0
    if (m_fontType!=st.m_fontType || WPSCellFormat::compare(st)!=0) return false;
84
0
    return true;
85
0
  }
86
  //! operator!=
87
  bool operator!=(Style const &st) const
88
0
  {
89
0
    return !(*this==st);
90
0
  }
91
  //! font encoding type
92
  libwps_tools_win::Font::Type m_fontType;
93
  /** extra data */
94
  std::string m_extra;
95
};
96
97
Style::~Style()
98
9.72M
{
99
9.72M
}
100
//! the extra style
101
struct ExtraStyle
102
{
103
  //! constructor
104
  ExtraStyle()
105
496k
    : m_color(WPSColor::black())
106
496k
    , m_backColor(WPSColor::white())
107
496k
    , m_format(0)
108
496k
    , m_flag(0)
109
496k
    , m_borders(0)
110
496k
  {
111
496k
  }
112
  //! returns true if the style is empty
113
  bool empty() const
114
365k
  {
115
    // find also f[8-c]ffffffXX, which seems to have a different meaning
116
365k
    if ((m_format&0xf0)==0xf0) return true;
117
296k
    return m_color.isBlack() && m_backColor.isWhite() && (m_format&0x38)==0 && m_borders==0;
118
365k
  }
119
  //! update the cell style
120
  void update(Style &style) const
121
130k
  {
122
130k
    WPSFont font=style.getFont();
123
130k
    if (m_format&0x38)
124
66.5k
    {
125
66.5k
      if (m_format&0x8) font.m_attributes |= WPS_BOLD_BIT;
126
66.5k
      if (m_format&0x10) font.m_attributes |= WPS_ITALICS_BIT;
127
66.5k
      if (m_format&0x20) font.m_attributes |= WPS_UNDERLINE_BIT;
128
66.5k
    }
129
130k
    font.m_color=m_color;
130
130k
    style.setFont(font);
131
130k
    style.setBackgroundColor(m_backColor);
132
130k
    if (m_borders)
133
93.9k
    {
134
469k
      for (int i=0,decal=0; i<4; ++i, decal+=2)
135
375k
      {
136
375k
        int type=(m_borders>>decal)&3;
137
375k
        if (type==0) continue;
138
244k
        static int const wh[]= {WPSBorder::LeftBit,WPSBorder::RightBit,WPSBorder::TopBit,WPSBorder::BottomBit};
139
244k
        WPSBorder border;
140
244k
        if (type==2) border.m_width=2;
141
176k
        else if (type==3) border.m_type=WPSBorder::Double;
142
244k
        style.setBorders(wh[i],border);
143
244k
      }
144
93.9k
    }
145
130k
  }
146
  //! the font color
147
  WPSColor m_color;
148
  //! the backgroun color
149
  WPSColor m_backColor;
150
  //! the format
151
  int m_format;
152
  //! the second flag: graph
153
  int m_flag;
154
  //! the border
155
  int m_borders;
156
};
157
158
//! a class used to store the styles of a row in LotusSpreadsheet
159
struct RowStyles
160
{
161
  //! constructor
162
  RowStyles()
163
39.2k
    : m_colsToStyleMap()
164
39.2k
  {
165
39.2k
  }
166
  //! a map Vec2i(minCol,maxCol) to style
167
  std::map<Vec2i, Style> m_colsToStyleMap;
168
};
169
170
//! a class used to store the extra style of a row in LotusSpreadsheet
171
struct ExtraRowStyles
172
{
173
  //! constructor
174
  ExtraRowStyles()
175
47.7k
    : m_colsToStyleMap()
176
47.7k
  {
177
47.7k
  }
178
179
  //! returns true if all style are empty
180
  bool empty() const
181
3.98k
  {
182
3.98k
    for (auto const &it : m_colsToStyleMap)
183
8.57k
    {
184
8.57k
      if (!it.second.empty()) return false;
185
8.57k
    }
186
313
    return true;
187
3.98k
  }
188
  //! a map Vec2i(minCol,maxCol) to style
189
  std::map<Vec2i, ExtraStyle> m_colsToStyleMap;
190
};
191
192
//! a list of position of a Lotus spreadsheet
193
struct CellsList
194
{
195
  //! constructor
196
  CellsList()
197
19.1k
    : m_positions()
198
19.1k
  {
199
38.3k
    for (auto &id : m_ids) id=0;
200
19.1k
  }
201
  //! operator<<
202
  friend std::ostream &operator<<(std::ostream &o, CellsList const &pos)
203
0
  {
204
0
    o << pos.m_positions;
205
0
    for (int i=0; i<2; ++i)
206
0
    {
207
0
      if (pos.m_ids[i]) o << "[sheet" << i << "=" << pos.m_ids[i] << "]";
208
0
    }
209
0
    o << ",";
210
0
    return o;
211
0
  }
212
  //! the sheets id
213
  int m_ids[2];
214
  //! the first and last position
215
  WPSBox2i m_positions;
216
};
217
218
//! a cellule of a Lotus spreadsheet
219
class Cell final : public WPSCell
220
{
221
public:
222
  /// constructor
223
  Cell()
224
1.22M
    : m_input()
225
1.22M
    , m_styleId(-1)
226
1.22M
    , m_hAlignement(WPSCellFormat::HALIGN_DEFAULT)
227
1.22M
    , m_content()
228
1.22M
    , m_comment() { }
229
  /// constructor
230
  explicit Cell(RVNGInputStreamPtr const &input)
231
136k
    : m_input(input)
232
136k
    , m_styleId(-1)
233
136k
    , m_hAlignement(WPSCellFormat::HALIGN_DEFAULT)
234
136k
    , m_content()
235
136k
    , m_comment() { }
236
  //! operator<<
237
  friend std::ostream &operator<<(std::ostream &o, Cell const &cell);
238
239
  //! call when a cell must be send
240
  bool send(WPSListenerPtr &/*listener*/) final
241
0
  {
242
0
    WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Cell::send: must not be called\n"));
243
0
    return false;
244
0
  }
245
246
  //! call when the content of a cell must be send
247
  bool sendContent(WPSListenerPtr &/*listener*/) final;
248
249
  //! the input
250
  RVNGInputStreamPtr m_input;
251
  //! the style
252
  int m_styleId;
253
  //! the horizontal align (in dos file)
254
  WPSCellFormat::HorizontalAlignment m_hAlignement;
255
  //! the content
256
  WKSContentListener::CellContent m_content;
257
  //! the comment entry
258
  WPSEntry m_comment;
259
};
260
bool Cell::sendContent(WPSListenerPtr &/*listener*/)
261
0
{
262
0
  WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Cell::sendContent: must not be called\n"));
263
0
  return false;
264
0
}
265
266
//! operator<<
267
std::ostream &operator<<(std::ostream &o, Cell const &cell)
268
0
{
269
0
  o << reinterpret_cast<WPSCell const &>(cell) << cell.m_content << ",";
270
0
  if (cell.m_styleId>=0) o << "style=" << cell.m_styleId << ",";
271
0
  switch (cell.m_hAlignement)
272
0
  {
273
0
  case WPSCellFormat::HALIGN_LEFT:
274
0
    o << "left,";
275
0
    break;
276
0
  case WPSCellFormat::HALIGN_CENTER:
277
0
    o << "centered,";
278
0
    break;
279
0
  case WPSCellFormat::HALIGN_RIGHT:
280
0
    o << "right,";
281
0
    break;
282
0
  case WPSCellFormat::HALIGN_FULL:
283
0
    o << "full,";
284
0
    break;
285
0
  case WPSCellFormat::HALIGN_DEFAULT:
286
0
  default:
287
0
    break; // default
288
0
  }
289
0
  return o;
290
0
}
291
292
///////////////////////////////////////////////////////////////////
293
//! the spreadsheet of a LotusSpreadsheet
294
class Spreadsheet
295
{
296
public:
297
  //! a constructor
298
  Spreadsheet()
299
5.30M
    : m_name("")
300
5.30M
    , m_numCols(0)
301
5.30M
    , m_numRows(0)
302
5.30M
    , m_boundsColsMap()
303
5.30M
    , m_widthCols()
304
5.30M
    , m_rowHeightMap()
305
5.30M
    , m_heightDefault(16)
306
5.30M
    , m_rowPageBreaksList()
307
5.30M
    , m_positionToCellMap()
308
5.30M
    , m_rowToStyleIdMap()
309
5.30M
    , m_rowToExtraStyleMap() {}
310
  //! return a cell corresponding to a spreadsheet, create one if needed
311
  Cell &getCell(RVNGInputStreamPtr input, Vec2i const &pos)
312
196k
  {
313
196k
    if (m_positionToCellMap.find(pos)==m_positionToCellMap.end())
314
136k
    {
315
136k
      Cell cell(input);
316
136k
      cell.setPosition(pos);
317
136k
      m_positionToCellMap[pos]=cell;
318
136k
    }
319
196k
    return m_positionToCellMap.find(pos)->second;
320
196k
  }
321
  //! set the columns size
322
  void setColumnWidth(int col, WPSColumnFormat const &format)
323
1.38G
  {
324
1.38G
    if (col >= int(m_widthCols.size()))
325
543k
    {
326
      // sanity check
327
543k
      if (col>MAX_COLUMNS || (!m_boundsColsMap.empty() && col >= int(m_widthCols.size())+10 &&
328
6.15k
                              m_boundsColsMap.find(col)==m_boundsColsMap.end()))
329
6.10k
      {
330
6.10k
        WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Spreadsheet::setColumnWidth: the column %d seems bad\n", col));
331
6.10k
        return;
332
6.10k
      }
333
537k
      WPSColumnFormat defCol;
334
537k
      defCol.m_useOptimalWidth=true;
335
537k
      m_widthCols.resize(size_t(col)+1, defCol);
336
537k
    }
337
1.38G
    m_widthCols[size_t(col)] = format;
338
1.38G
    if (col >= m_numCols) m_numCols=col+1;
339
1.38G
  }
340
  //! returns the row size in point
341
  WPSRowFormat getRowHeight(int row) const
342
1.08M
  {
343
1.08M
    auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row));
344
1.08M
    if (rIt!=m_rowHeightMap.end() && rIt->first[0]<=row && rIt->first[1]>=row)
345
54.4k
      return rIt->second;
346
1.03M
    WPSRowFormat format(m_heightDefault);
347
1.03M
    format.m_isMinimalHeight=true;
348
1.03M
    return format;
349
1.08M
  }
350
  //! set the rows size
351
  void setRowHeight(int row, WPSRowFormat const &format)
352
243M
  {
353
243M
    m_rowHeightMap[Vec2i(row,row)]=format;
354
243M
  }
355
  //! returns the position corresponding to a cell
356
  Vec2f getPosition(Vec2i const &cell) const
357
22.4k
  {
358
    // first compute the height
359
22.4k
    int lastRow=0;
360
22.4k
    float h=0;
361
22.4k
    auto rIt=m_rowHeightMap.begin();
362
36.0k
    while (rIt!=m_rowHeightMap.end() && rIt->first[1]<cell[1])
363
13.6k
    {
364
13.6k
      if (rIt->first[0]>lastRow)
365
9.80k
      {
366
9.80k
        h+=float(rIt->first[0]-lastRow)*m_heightDefault;
367
9.80k
        lastRow=rIt->first[0];
368
9.80k
      }
369
13.6k
      float rHeight=rIt->second.m_height>=0 ? rIt->second.m_height : m_heightDefault;
370
13.6k
      h+=float(rIt->first[1]+1-lastRow)*rHeight;
371
13.6k
      lastRow=rIt->first[1]+1;
372
13.6k
      ++rIt;
373
13.6k
    }
374
22.4k
    if (lastRow<cell[1])
375
9.71k
    {
376
9.71k
      if (rIt!=m_rowHeightMap.end() && rIt->first[0]<cell[1] && rIt->second.m_height>=0)
377
0
        h+=float(cell[1]-lastRow)*rIt->second.m_height;
378
9.71k
      else
379
9.71k
        h+=float(cell[1]-lastRow)*m_heightDefault;
380
381
9.71k
    }
382
    // now compute the width
383
22.4k
    size_t const numCols = m_widthCols.size();
384
22.4k
    float w=0;
385
45.4k
    for (size_t i = 0; i < numCols && i<size_t(cell[0]); i++)
386
23.0k
      w+=m_widthCols[i].m_width>=0 ? m_widthCols[i].m_width : 72;
387
22.4k
    if (numCols<size_t(cell[0]))
388
14.5k
      w+=72*float(size_t(cell[0])-numCols);
389
22.4k
    return Vec2f(w, h);
390
22.4k
  }
391
  //! try to compress the list of row height
392
  void compressRowHeights()
393
2.27M
  {
394
2.27M
    auto oldMap=m_rowHeightMap;
395
2.27M
    m_rowHeightMap.clear();
396
2.27M
    auto rIt=oldMap.begin();
397
2.27M
    WPSRowFormat actHeight;
398
2.27M
    WPSRowFormat defHeight(m_heightDefault);
399
2.27M
    defHeight.m_isMinimalHeight=true;
400
2.27M
    Vec2i actPos(0,-1);
401
3.15M
    while (rIt!=oldMap.end())
402
880k
    {
403
      // first check for not filled row
404
880k
      if (rIt->first[0]!=actPos[1]+1)
405
20.6k
      {
406
20.6k
        if (actHeight==defHeight)
407
0
          actPos[1]=rIt->first[0]-1;
408
20.6k
        else
409
20.6k
        {
410
20.6k
          if (actPos[1]>=actPos[0])
411
15.9k
            m_rowHeightMap[actPos]=actHeight;
412
20.6k
          actHeight=defHeight;
413
20.6k
          actPos=Vec2i(actPos[1]+1, rIt->first[0]-1);
414
20.6k
        }
415
20.6k
      }
416
880k
      if (rIt->second!=actHeight)
417
31.1k
      {
418
31.1k
        if (actPos[1]>=actPos[0])
419
25.1k
          m_rowHeightMap[actPos]=actHeight;
420
31.1k
        actPos[0]=rIt->first[0];
421
31.1k
        actHeight=rIt->second;
422
31.1k
      }
423
880k
      actPos[1]=rIt->first[1];
424
880k
      ++rIt;
425
880k
    }
426
2.27M
    if (actPos[1]>=actPos[0])
427
10.6k
      m_rowHeightMap[actPos]=actHeight;
428
2.27M
  }
429
  //! convert the m_widthColsInChar in a vector of of point size
430
  std::vector<WPSColumnFormat> getWidths() const
431
2.27M
  {
432
2.27M
    std::vector<WPSColumnFormat> widths;
433
2.27M
    WPSColumnFormat actWidth;
434
2.27M
    int repeat=0;
435
2.27M
    for (auto const &newWidth : m_widthCols)
436
432k
    {
437
432k
      if (repeat && newWidth!=actWidth)
438
17.3k
      {
439
17.3k
        actWidth.m_numRepeat=repeat;
440
17.3k
        widths.push_back(actWidth);
441
17.3k
        repeat=0;
442
17.3k
      }
443
432k
      if (repeat==0)
444
22.0k
        actWidth=newWidth;
445
432k
      ++repeat;
446
432k
    }
447
2.27M
    if (repeat)
448
4.75k
    {
449
4.75k
      actWidth.m_numRepeat=repeat;
450
4.75k
      widths.push_back(actWidth);
451
4.75k
    }
452
2.27M
    return widths;
453
2.27M
  }
454
  //! returns the row style id corresponding to a sheetId (or -1)
455
  int getRowStyleId(int row) const
456
1.08M
  {
457
1.08M
    auto it=m_rowToStyleIdMap.lower_bound(Vec2i(-1, row));
458
1.08M
    if (it!=m_rowToStyleIdMap.end() && it->first[0]<=row && row<=it->first[1])
459
16.4k
      return int(it->second);
460
1.06M
    return -1;
461
1.08M
  }
462
463
  //! returns true if the spreedsheet is empty
464
  bool empty() const
465
2.40M
  {
466
2.40M
    return m_positionToCellMap.empty() && m_rowToStyleIdMap.empty() && m_name.empty();
467
2.40M
  }
468
  /** the sheet name */
469
  librevenge::RVNGString m_name;
470
  /** the number of columns */
471
  int m_numCols;
472
  /** the number of rows */
473
  int m_numRows;
474
  /** a map used to stored the min/max row of each columns */
475
  std::map<int, Vec2i> m_boundsColsMap;
476
  /** the column size */
477
  std::vector<WPSColumnFormat> m_widthCols;
478
  /** the map Vec2i(min row, max row) to size in points */
479
  std::map<Vec2i,WPSRowFormat> m_rowHeightMap;
480
  /** the default row size in point */
481
  float m_heightDefault;
482
  /** the list of row page break */
483
  std::vector<int> m_rowPageBreaksList;
484
  /** a map cell to not empty cells */
485
  std::map<Vec2i, Cell> m_positionToCellMap;
486
  //! map Vec2i(min row, max row) to state row style id
487
  std::map<Vec2i,size_t> m_rowToStyleIdMap;
488
  //! map row to extra style
489
  std::map<int,ExtraRowStyles> m_rowToExtraStyleMap;
490
};
491
492
// ------------------------------------------------------------
493
// Lotus 123
494
// ------------------------------------------------------------
495
//! the format style for lotus 123
496
struct Format123Style final : public WPSCellFormat
497
{
498
  //! constructor
499
  Format123Style()
500
405k
    : m_alignAcrossColumn(false)
501
405k
  {
502
405k
  }
503
2.74M
  Format123Style(Format123Style const &)=default;
504
208k
  Format123Style &operator=(Format123Style const &)=default;
505
  //! destructor
506
  ~Format123Style() final;
507
  //! update the cell style
508
  void update(Style &style) const
509
656k
  {
510
656k
    style.setDTFormat(getFormat(), getDTFormat());
511
656k
    style.setFormat(getFormat(), getSubFormat());
512
656k
    style.setDigits(digits());
513
656k
  }
514
  //! operator==
515
  bool operator==(Format123Style const &f) const
516
96.4k
  {
517
96.4k
    return m_alignAcrossColumn==f.m_alignAcrossColumn && compare(f)==0;
518
96.4k
  }
519
  //! flag to know if we must align across column
520
  bool m_alignAcrossColumn;
521
};
522
523
Format123Style::~Format123Style()
524
3.15M
{
525
3.15M
}
526
527
//! the extra style for lotus 123
528
struct Extra123Style
529
{
530
  //! constructor
531
  Extra123Style()
532
270k
  {
533
270k
    for (auto &border : m_borders)
534
541k
      border.m_style=WPSBorder::None;
535
270k
  }
536
  //! returns true if the style is empty
537
  bool empty() const
538
0
  {
539
0
    for (const auto &border : m_borders)
540
0
    {
541
0
      if (!border.isEmpty()) return false;
542
0
    }
543
0
    return true;
544
0
  }
545
  //! operator==
546
  bool operator==(Extra123Style const &f) const
547
41.6k
  {
548
83.6k
    for (int i=0; i<2; ++i)
549
65.2k
    {
550
65.2k
      if (m_borders[i]!=f.m_borders[i])
551
23.3k
        return false;
552
65.2k
    }
553
18.3k
    return true;
554
41.6k
  }
555
  //! update the cell style
556
  void update(Style &style) const
557
245k
  {
558
737k
    for (int i=0; i<2; ++i)
559
491k
    {
560
491k
      if (m_borders[i].isEmpty()) continue;
561
391k
      style.setBorders(i==0 ? WPSBorder::TopBit : WPSBorder::LeftBit,m_borders[i]);
562
391k
    }
563
245k
  }
564
  //! the top/left border
565
  WPSBorder m_borders[2];
566
};
567
568
//! a class used to store the styles of a table in LotusSpreadsheet in a lotus 123 files
569
struct Table123Styles
570
{
571
  //! constructor
572
  Table123Styles()
573
249k
    : m_defaultCellId(-1)
574
249k
    , m_rowsToColsToCellIdMap()
575
249k
    , m_rowsToColsToExtraStyleMap()
576
249k
    , m_rowsToColsToFormatStyleMap()
577
249k
  {
578
249k
  }
579
  //! add a style to a list of cell
580
  void addCellStyle(Vec2i const &cols, Vec2i const &rows, int cellId)
581
152k
  {
582
152k
    if (m_rowsToColsToCellIdMap.find(rows)==m_rowsToColsToCellIdMap.end())
583
33.3k
      m_rowsToColsToCellIdMap[rows]= std::map<Vec2i,int>();
584
152k
    auto &map=m_rowsToColsToCellIdMap.find(rows)->second;
585
152k
    if (map.find(cols)!=map.end())
586
26.1k
    {
587
26.1k
      WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Table123Styles::addCellStyle: find dupplicated cell\n"));
588
26.1k
    }
589
152k
    auto it=map.lower_bound(cols);
590
152k
    if (!map.empty() && it!=map.begin()) --it;
591
152k
    if (it!=map.end() && it->first[1]+1==cols[0] && it->second==cellId)
592
70.6k
    {
593
70.6k
      Vec2i newCols=it->first;
594
70.6k
      map.erase(newCols);
595
70.6k
      newCols[1]=cols[1];
596
70.6k
      map[newCols]=cellId;
597
70.6k
    }
598
81.3k
    else
599
81.3k
      map[cols]=cellId;
600
152k
  }
601
  //! add a extra style to a list of cell
602
  void addCellStyle(Vec2i const &cols, Vec2i const &rows, Extra123Style const &extra)
603
164k
  {
604
164k
    if (m_rowsToColsToExtraStyleMap.find(rows)==m_rowsToColsToExtraStyleMap.end())
605
41.1k
      m_rowsToColsToExtraStyleMap[rows]= std::map<Vec2i,Extra123Style>();
606
164k
    auto &map=m_rowsToColsToExtraStyleMap.find(rows)->second;
607
    // checkme: sometimes, we can retrieve the same cells again
608
164k
    auto it=map.lower_bound(cols);
609
164k
    if (!map.empty() && it!=map.begin()) --it;
610
164k
    if (it!=map.end() && it->first[1]+1==cols[0] && it->second==extra)
611
18.3k
    {
612
18.3k
      Vec2i newCols=it->first;
613
18.3k
      map.erase(newCols);
614
18.3k
      newCols[1]=cols[1];
615
18.3k
      map[newCols]=extra;
616
18.3k
    }
617
146k
    else
618
146k
      map[cols]=extra;
619
164k
  }
620
  //! add a extra style to a list of cell
621
  void addCellStyle(Vec2i const &cols, Vec2i const &rows, Format123Style const &format)
622
207k
  {
623
207k
    if (m_rowsToColsToFormatStyleMap.find(rows)==m_rowsToColsToFormatStyleMap.end())
624
42.7k
      m_rowsToColsToFormatStyleMap[rows]= std::map<Vec2i,Format123Style>();
625
207k
    auto &map=m_rowsToColsToFormatStyleMap.find(rows)->second;
626
207k
    if (map.find(cols)!=map.end())
627
55.5k
    {
628
55.5k
      WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Table123Styles::addCellStyle: find dupplicated cell\n"));
629
55.5k
    }
630
207k
    auto it=map.lower_bound(cols);
631
207k
    if (!map.empty() && it!=map.begin()) --it;
632
207k
    if (it!=map.end() && it->first[1]+1==cols[0] && it->second==format)
633
69.4k
    {
634
69.4k
      Vec2i newCols=it->first;
635
69.4k
      map.erase(newCols);
636
69.4k
      newCols[1]=cols[1];
637
69.4k
      map[newCols]=format;
638
69.4k
    }
639
137k
    else
640
137k
      map[cols]=format;
641
207k
  }
642
  //! the default cell style
643
  int m_defaultCellId;
644
  //! map rows to cols to cell id
645
  std::map<Vec2i, std::map<Vec2i,int> > m_rowsToColsToCellIdMap;
646
  //! map rows to cols to extra style
647
  std::map<Vec2i, std::map<Vec2i,Extra123Style> > m_rowsToColsToExtraStyleMap;
648
  //! map rows to cols to format style
649
  std::map<Vec2i, std::map<Vec2i,Format123Style> > m_rowsToColsToFormatStyleMap;
650
};
651
652
//! the state of LotusSpreadsheet
653
struct State
654
{
655
  //! constructor
656
  State()
657
121k
    :  m_version(-1)
658
121k
    , m_spreadsheetList()
659
121k
    , m_nameToCellsMap()
660
121k
    , m_rowStylesList()
661
121k
    , m_rowSheetIdToStyleIdMap()
662
121k
    , m_rowSheetIdToChildRowIdMap()
663
121k
    , m_sheetIdToTableStyleMap()
664
121k
    , m_sheetCurrentId(-1)
665
121k
  {
666
121k
    m_spreadsheetList.resize(1);
667
121k
  }
668
  //! returns the number of spreadsheet
669
  int getNumSheet() const
670
2.35M
  {
671
2.35M
    return int(m_spreadsheetList.size());
672
2.35M
  }
673
  //! returns the ith spreadsheet
674
  Spreadsheet &getSheet(int id)
675
1.03G
  {
676
1.03G
    if (id<0||id>=int(m_spreadsheetList.size()))
677
1.03G
    {
678
1.03G
      WPS_DEBUG_MSG(("LotusSpreadsheetInternal::State::getSheet: can not find spreadsheet %d\n", id));
679
1.03G
      static Spreadsheet empty;
680
1.03G
      return empty;
681
1.03G
    }
682
2.43M
    return m_spreadsheetList[size_t(id)];
683
1.03G
  }
684
  //! returns a table style for a sheet(if it exists)
685
  Table123Styles const *getTableStyle(int id) const
686
2.27M
  {
687
2.27M
    Vec2i pos(-1,id);
688
2.27M
    auto cIt=m_sheetIdToTableStyleMap.lower_bound(pos);
689
2.27M
    if (cIt==m_sheetIdToTableStyleMap.end() || cIt->first[0]>id || cIt->first[1]<id)
690
1.84M
      return nullptr;
691
435k
    return &cIt->second;
692
2.27M
  }
693
  //! returns a table style for a sheet zone, create it if needed
694
  Table123Styles *getTablesStyle(Vec2i pos)
695
597k
  {
696
597k
    auto cIt=m_sheetIdToTableStyleMap.lower_bound(pos);
697
597k
    if (cIt==m_sheetIdToTableStyleMap.end() || cIt->first[0]>pos[1] || cIt->first[1]<pos[0])
698
47.3k
    {
699
47.3k
      m_sheetIdToTableStyleMap[pos]=Table123Styles();
700
47.3k
      return &m_sheetIdToTableStyleMap.find(pos)->second;
701
47.3k
    }
702
550k
    if (cIt->first==pos)
703
476k
      return &cIt->second;
704
73.9k
    Vec2i actPos=cIt->first;
705
73.9k
    if (actPos[0]>pos[0] || actPos[1]<pos[1])
706
17.1k
    {
707
17.1k
      WPS_DEBUG_MSG(("LotusSpreadsheetInternal::State::getTablesStyle: problem when creating spreadsheet %d,%d\n", pos[0], pos[1]));
708
17.1k
      return nullptr;
709
17.1k
    }
710
56.8k
    auto const &table=cIt->second;
711
56.8k
    if (actPos[0]<pos[0])
712
48.6k
    {
713
48.6k
      Vec2i newPos(actPos[0],pos[0]);
714
48.6k
      m_sheetIdToTableStyleMap[newPos]=table;
715
48.6k
    }
716
56.8k
    m_sheetIdToTableStyleMap[pos]=table;
717
56.8k
    if (actPos[1]>pos[1])
718
56.8k
    {
719
56.8k
      Vec2i newPos(pos[1],actPos[1]);
720
56.8k
      m_sheetIdToTableStyleMap[newPos]=table;
721
56.8k
    }
722
56.8k
    m_sheetIdToTableStyleMap.erase(actPos);
723
56.8k
    return &m_sheetIdToTableStyleMap.find(pos)->second;
724
73.9k
  }
725
  //! returns the ith spreadsheet name
726
  librevenge::RVNGString getSheetName(int id) const
727
2.37M
  {
728
2.37M
    if (id>=0 && id<int(m_spreadsheetList.size()) && !m_spreadsheetList[size_t(id)].m_name.empty())
729
18.4k
      return m_spreadsheetList[size_t(id)].m_name;
730
2.35M
    librevenge::RVNGString name;
731
2.35M
    name.sprintf("Sheet%d", id+1);
732
2.35M
    return name;
733
2.37M
  }
734
  //! the file version
735
  int m_version;
736
  //! the list of spreadsheet ( first: main spreadsheet, other report spreadsheet )
737
  std::vector<Spreadsheet> m_spreadsheetList;
738
  //! map name to position
739
  std::map<std::string, CellsList> m_nameToCellsMap;
740
  //! the list of row styles
741
  std::vector<RowStyles> m_rowStylesList;
742
  //! map Vec2i(row, sheetId) to row style id
743
  std::map<Vec2i,size_t> m_rowSheetIdToStyleIdMap;
744
  //! map Vec2i(row, sheetId) to child style
745
  std::multimap<Vec2i,Vec2i> m_rowSheetIdToChildRowIdMap;
746
  //! map Vec2i(sheetMin, sheetMax) to table style
747
  std::map<Vec2i,Table123Styles> m_sheetIdToTableStyleMap;
748
  //! the sheet id
749
  int m_sheetCurrentId;
750
};
751
752
//! Internal: the subdocument of a LotusSpreadsheet
753
class SubDocument final : public WKSSubDocument
754
{
755
public:
756
  //! constructor for a text entry
757
  SubDocument(RVNGInputStreamPtr const &input, LotusSpreadsheet &sheetParser, WPSEntry const &entry)
758
2.23k
    : WKSSubDocument(input, nullptr)
759
2.23k
    , m_sheetParser(&sheetParser)
760
2.23k
    , m_entry(entry) {}
761
  //! destructor
762
2.23k
  ~SubDocument() final {}
763
764
  //! operator==
765
  bool operator==(std::shared_ptr<WPSSubDocument> const &doc) const final
766
0
  {
767
0
    if (!doc || !WKSSubDocument::operator==(doc))
768
0
      return false;
769
0
    auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get());
770
0
    if (!sDoc) return false;
771
0
    if (m_sheetParser != sDoc->m_sheetParser) return false;
772
0
    if (m_entry != sDoc->m_entry) return false;
773
0
    return true;
774
0
  }
775
776
  //! the parser function
777
  void parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType subDocumentType) final;
778
  //! the spreadsheet parse
779
  LotusSpreadsheet *m_sheetParser;
780
  //! a text zone entry
781
  WPSEntry m_entry;
782
private:
783
  SubDocument(SubDocument const &) = delete;
784
  SubDocument &operator=(SubDocument const &) = delete;
785
};
786
787
void SubDocument::parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType)
788
2.23k
{
789
2.23k
  if (!m_sheetParser)
790
0
  {
791
0
    listener->insertCharacter(' ');
792
0
    WPS_DEBUG_MSG(("LotusSpreadsheetInternal::SubDocument::parse: bad parser\n"));
793
0
    return;
794
0
  }
795
2.23k
  m_sheetParser->sendTextNote(m_input, m_entry);
796
2.23k
}
797
}
798
799
// constructor, destructor
800
LotusSpreadsheet::LotusSpreadsheet(LotusParser &parser)
801
60.7k
  : m_listener()
802
60.7k
  , m_mainParser(parser)
803
60.7k
  , m_styleManager(parser.m_styleManager)
804
60.7k
  , m_state(new LotusSpreadsheetInternal::State)
805
60.7k
{
806
60.7k
}
807
808
LotusSpreadsheet::~LotusSpreadsheet()
809
60.7k
{
810
60.7k
}
811
812
void LotusSpreadsheet::cleanState()
813
60.6k
{
814
60.6k
  m_state.reset(new LotusSpreadsheetInternal::State);
815
60.6k
}
816
817
bool LotusSpreadsheet::getLeftTopPosition(Vec2i const &cell, int sheetId, Vec2f &pos)
818
22.4k
{
819
  // set to default
820
22.4k
  pos=Vec2f(float(cell[0]>=0 ? cell[0]*72 : 0), float(cell[1]>=0 ? cell[1]*16 : 0));
821
22.4k
  if (sheetId<0||sheetId>=m_state->getNumSheet() || cell[0]<0 || cell[1]<0)
822
0
  {
823
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::getLeftTopPosition: the sheet %d seems bad\n", sheetId));
824
0
    return true;
825
0
  }
826
22.4k
  auto const &sheet = m_state->getSheet(sheetId);
827
22.4k
  pos=sheet.getPosition(cell);
828
22.4k
  return true;
829
22.4k
}
830
831
librevenge::RVNGString LotusSpreadsheet::getSheetName(int id) const
832
2.37M
{
833
2.37M
  return m_state->getSheetName(id);
834
2.37M
}
835
836
void LotusSpreadsheet::updateState()
837
28.1k
{
838
  // update the state correspondance between row and row's styles
839
28.1k
  if (!m_state->m_rowSheetIdToChildRowIdMap.empty())
840
1.33k
  {
841
1.33k
    std::set<Vec2i> seens;
842
1.33k
    std::stack<Vec2i> toDo;
843
1.33k
    for (auto const &it : m_state->m_rowSheetIdToStyleIdMap)
844
9.41k
      toDo.push(it.first);
845
168k
    while (!toDo.empty())
846
166k
    {
847
166k
      Vec2i pos=toDo.top();
848
166k
      toDo.pop();
849
166k
      if (seens.find(pos)!=seens.end())
850
98.5k
      {
851
98.5k
        WPS_DEBUG_MSG(("LotusSpreadsheet::updateState: dupplicated position, something is bad\n"));
852
98.5k
        continue;
853
98.5k
      }
854
68.3k
      seens.insert(pos);
855
68.3k
      auto cIt=m_state->m_rowSheetIdToChildRowIdMap.lower_bound(pos);
856
68.3k
      if (cIt==m_state->m_rowSheetIdToChildRowIdMap.end() || cIt->first!=pos)
857
11.3k
        continue;
858
56.9k
      if (m_state->m_rowSheetIdToStyleIdMap.find(pos)==m_state->m_rowSheetIdToStyleIdMap.end())
859
0
      {
860
0
        WPS_DEBUG_MSG(("LotusSpreadsheet::updateState: something is bad\n"));
861
0
        continue;
862
0
      }
863
56.9k
      size_t finalPos=m_state->m_rowSheetIdToStyleIdMap.find(pos)->second;
864
214k
      while (cIt!=m_state->m_rowSheetIdToChildRowIdMap.end() && cIt->first==pos)
865
157k
      {
866
157k
        Vec2i const &cPos=cIt++->second;
867
157k
        m_state->m_rowSheetIdToStyleIdMap[cPos]=finalPos;
868
157k
        toDo.push(cPos);
869
157k
      }
870
56.9k
    }
871
1.33k
  }
872
873
  // time to update each sheet rows style map
874
33.7k
  for (auto it=m_state->m_rowSheetIdToStyleIdMap.begin(); it!=m_state->m_rowSheetIdToStyleIdMap.end();)
875
5.54k
  {
876
5.54k
    int sheetId=it->first[1];
877
5.54k
    LotusSpreadsheetInternal::Spreadsheet *sheet=nullptr;
878
5.54k
    if (sheetId>=0 && sheetId<int(m_state->m_spreadsheetList.size()))
879
4.51k
      sheet=&m_state->m_spreadsheetList[size_t(sheetId)];
880
1.02k
    else
881
1.02k
    {
882
1.02k
      WPS_DEBUG_MSG(("LotusSpreadsheet::updateState: can not find sheet %d\n", sheetId));
883
1.02k
    }
884
5.54k
    int lastStyleId=-1;
885
5.54k
    Vec2i rows(0,-1);
886
77.6k
    while (it!=m_state->m_rowSheetIdToStyleIdMap.end() && it->first[1]==sheetId)
887
72.1k
    {
888
72.1k
      if (lastStyleId!=int(it->second) || it->first[0]!=rows[1]+1)
889
17.3k
      {
890
17.3k
        if (lastStyleId>=0 && sheet)
891
11.4k
          sheet->m_rowToStyleIdMap[rows]=size_t(lastStyleId);
892
17.3k
        lastStyleId=int(it->second);
893
17.3k
        rows=Vec2i(it->first[0], it->first[0]);
894
17.3k
      }
895
54.7k
      else
896
54.7k
        ++rows[1];
897
72.1k
      ++it;
898
72.1k
    }
899
5.54k
    if (lastStyleId>=0 && sheet)
900
4.51k
      sheet->m_rowToStyleIdMap[rows]=size_t(lastStyleId);
901
5.54k
  }
902
28.1k
}
903
904
void LotusSpreadsheet::setLastSpreadsheetId(int id)
905
75.7k
{
906
75.7k
  if (id<0)
907
0
  {
908
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::setLastSpreadsheetId: the id:%d seems bad\n", id));
909
0
    return;
910
0
  }
911
75.7k
  m_state->m_spreadsheetList.resize(size_t(id+1));
912
75.7k
}
913
914
int LotusSpreadsheet::version() const
915
1.43M
{
916
1.43M
  if (m_state->m_version<0)
917
25.5k
    m_state->m_version=m_mainParser.version();
918
1.43M
  return m_state->m_version;
919
1.43M
}
920
921
bool LotusSpreadsheet::hasSomeSpreadsheetData() const
922
45.6k
{
923
45.6k
  for (auto const &sheet : m_state->m_spreadsheetList)
924
2.40M
  {
925
2.40M
    if (!sheet.empty())
926
13.4k
      return true;
927
2.40M
  }
928
32.2k
  return false;
929
45.6k
}
930
931
////////////////////////////////////////////////////////////
932
// low level
933
934
////////////////////////////////////////////////////////////
935
//   parse sheet data
936
////////////////////////////////////////////////////////////
937
bool LotusSpreadsheet::readColumnDefinition(std::shared_ptr<WPSStream> stream)
938
46.8k
{
939
46.8k
  if (!stream) return false;
940
46.8k
  RVNGInputStreamPtr &input=stream->m_input;
941
46.8k
  libwps::DebugFile &ascFile=stream->m_ascii;
942
46.8k
  libwps::DebugStream f;
943
944
46.8k
  long pos = input->tell();
945
46.8k
  auto type = long(libwps::read16(input));
946
46.8k
  if (type != 0x1f)
947
0
  {
948
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnDefinition: not a column definition\n"));
949
0
    return false;
950
0
  }
951
46.8k
  auto sz = long(libwps::readU16(input));
952
46.8k
  f << "Entries(ColDef):";
953
46.8k
  if (sz<8 || (sz%4))
954
1.85k
  {
955
1.85k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnDefinition: the zone is too short\n"));
956
1.85k
    f << "###";
957
1.85k
    ascFile.addPos(pos);
958
1.85k
    ascFile.addNote(f.str().c_str());
959
1.85k
    return true;
960
1.85k
  }
961
44.9k
  auto sheetId=int(libwps::readU8(input));
962
44.9k
  f << "sheet[id]=" << sheetId << ",";
963
44.9k
  auto col=int(libwps::readU8(input));
964
44.9k
  f << "col=" << col << ",";
965
44.9k
  auto N=int(libwps::readU8(input));
966
44.9k
  if (N!=1) f << "N=" << N << ",";
967
44.9k
  auto val=int(libwps::readU8(input)); // between 0 and 94
968
44.9k
  if (val) f << "f0=" << val << ",";
969
44.9k
  if (sz!=4+4*N)
970
2.87k
  {
971
2.87k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnDefinition: the number of columns seems bad\n"));
972
2.87k
    f << "###N,";
973
2.87k
    if (sz==8)
974
2.70k
      N=1;
975
173
    else
976
173
    {
977
173
      ascFile.addPos(pos);
978
173
      ascFile.addNote(f.str().c_str());
979
173
      return true;
980
173
    }
981
2.87k
  }
982
44.8k
  Vec2i bound;
983
92.7k
  for (int n=0; n<N; ++n)
984
47.9k
  {
985
47.9k
    int rowPos[2];
986
95.8k
    for (int &i : rowPos) i=int(libwps::readU16(input));
987
47.9k
    if (n==0)
988
44.8k
      bound=Vec2i(rowPos[0], rowPos[1]);
989
3.13k
    else
990
3.13k
    {
991
3.13k
      if (rowPos[0]<bound[0])
992
214
        bound[0]=rowPos[0];
993
3.13k
      if (rowPos[1]>bound[1])
994
548
        bound[1]=rowPos[1];
995
3.13k
    }
996
47.9k
    f << "row" << n << "[bound]=" << Vec2i(rowPos[0], rowPos[1]) << ",";
997
47.9k
  }
998
44.8k
  if (sheetId>=m_state->getNumSheet())
999
2.00k
  {
1000
2.00k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnDefinition the zone id seems bad\n"));
1001
2.00k
    f << "##id";
1002
2.00k
  }
1003
42.7k
  else
1004
42.7k
  {
1005
42.7k
    auto &sheet=m_state->getSheet(sheetId);
1006
42.7k
    if (sheet.m_boundsColsMap.find(col)!=sheet.m_boundsColsMap.end())
1007
9.39k
    {
1008
9.39k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnDefinition the zone col seems bad\n"));
1009
9.39k
      f << "##col";
1010
9.39k
    }
1011
33.4k
    else
1012
33.4k
      sheet.m_boundsColsMap[col]=bound;
1013
42.7k
  }
1014
44.8k
  ascFile.addPos(pos);
1015
44.8k
  ascFile.addNote(f.str().c_str());
1016
44.8k
  return true;
1017
44.9k
}
1018
1019
bool LotusSpreadsheet::readColumnSizes(std::shared_ptr<WPSStream> stream)
1020
3.19k
{
1021
3.19k
  if (!stream) return false;
1022
3.19k
  RVNGInputStreamPtr &input=stream->m_input;
1023
3.19k
  libwps::DebugFile &ascFile=stream->m_ascii;
1024
3.19k
  libwps::DebugStream f;
1025
1026
3.19k
  long pos = input->tell();
1027
3.19k
  auto type = long(libwps::read16(input));
1028
3.19k
  if (type != 0x7)
1029
0
  {
1030
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnSizes: not a column size name\n"));
1031
0
    return false;
1032
0
  }
1033
3.19k
  auto sz = long(libwps::readU16(input));
1034
3.19k
  f << "Entries(ColSize):";
1035
3.19k
  if (sz < 4 || (sz%2))
1036
1.20k
  {
1037
1.20k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnSizes: the zone is too odd\n"));
1038
1.20k
    f << "###";
1039
1.20k
    ascFile.addPos(pos);
1040
1.20k
    ascFile.addNote(f.str().c_str());
1041
1.20k
    return true;
1042
1.20k
  }
1043
1.98k
  auto sheetId=int(libwps::readU8(input));
1044
1.98k
  f << "id[sheet]=" << sheetId << ",";
1045
1.98k
  LotusSpreadsheetInternal::Spreadsheet empty, *sheet=nullptr;
1046
1.98k
  if (sheetId>=int(m_state->m_spreadsheetList.size()))
1047
336
  {
1048
336
    WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnSizes: can find spreadsheet %d\n", sheetId));
1049
336
    sheet=&empty;
1050
336
    f << "###";
1051
336
  }
1052
1.65k
  else
1053
1.65k
    sheet=&m_state->m_spreadsheetList[size_t(sheetId)];
1054
1.98k
  auto val=int(libwps::readU8(input)); // always 0?
1055
1.98k
  if (val) f << "f0=" << val << ",";
1056
1.98k
  f << "f1=" << std::hex << libwps::readU16(input) << std::dec << ","; // big number
1057
1.98k
  auto N=int((sz-4)/2);
1058
1.98k
  f << "widths=[";
1059
92.6k
  for (int i=0; i<N; ++i)
1060
90.6k
  {
1061
90.6k
    auto col=int(libwps::readU8(input));
1062
90.6k
    auto width=int(libwps::readU8(input)); // width in char, default 12...
1063
90.6k
    sheet->setColumnWidth(col, WPSColumnFormat(float(7*width)));
1064
90.6k
    f << width << "C:col" << col << ",";
1065
90.6k
  }
1066
1.98k
  f << "],";
1067
1.98k
  ascFile.addPos(pos);
1068
1.98k
  ascFile.addNote(f.str().c_str());
1069
1070
1.98k
  return true;
1071
3.19k
}
1072
1073
bool LotusSpreadsheet::readRowFormats(std::shared_ptr<WPSStream> stream)
1074
256k
{
1075
256k
  if (!stream) return false;
1076
256k
  RVNGInputStreamPtr &input=stream->m_input;
1077
256k
  libwps::DebugFile &ascFile=stream->m_ascii;
1078
256k
  libwps::DebugStream f;
1079
1080
256k
  long pos = input->tell();
1081
256k
  auto type = long(libwps::read16(input));
1082
256k
  if (type != 0x13)
1083
0
  {
1084
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: not a row definition\n"));
1085
0
    return false;
1086
0
  }
1087
256k
  auto sz = long(libwps::readU16(input));
1088
256k
  long endPos = pos+4+sz;
1089
256k
  f << "Entries(RowFormat):";
1090
256k
  if (sz<8)
1091
1.68k
  {
1092
1.68k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the zone is too short\n"));
1093
1.68k
    f << "###";
1094
1.68k
    ascFile.addPos(pos);
1095
1.68k
    ascFile.addNote(f.str().c_str());
1096
1.68k
    return true;
1097
1.68k
  }
1098
255k
  auto sheetId=int(libwps::readU8(input));
1099
255k
  auto rowType=int(libwps::readU8(input));
1100
255k
  auto row=int(libwps::readU16(input));
1101
255k
  int val;
1102
255k
  f << "sheet[id]=" << sheetId << ",";
1103
255k
  if (row) f << "row=" << row << ",";
1104
255k
  switch (rowType)
1105
255k
  {
1106
39.2k
  case 0:
1107
39.2k
  {
1108
39.2k
    f << "def,";
1109
39.2k
    size_t rowStyleId=m_state->m_rowStylesList.size();
1110
39.2k
    m_state->m_rowStylesList.resize(rowStyleId+1);
1111
1112
39.2k
    int actCell=0;
1113
39.2k
    auto &stylesList=m_state->m_rowStylesList.back();
1114
39.2k
    f << "[";
1115
752k
    while (input->tell()<endPos)
1116
722k
    {
1117
722k
      int numCell;
1118
722k
      LotusSpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
1119
722k
      if (!readRowFormat(stream, style, numCell, endPos))
1120
9.42k
      {
1121
9.42k
        f << "###";
1122
9.42k
        WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: find extra data\n"));
1123
9.42k
        break;
1124
9.42k
      }
1125
713k
      if (numCell>=1)
1126
713k
        stylesList.m_colsToStyleMap.insert
1127
713k
        (std::map<Vec2i,LotusSpreadsheetInternal::Style>::value_type
1128
713k
         (Vec2i(actCell,actCell+numCell-1),style));
1129
713k
      f << "[" << style << "]";
1130
713k
      if (numCell>1)
1131
129k
        f << "x" << numCell;
1132
713k
      f << ",";
1133
713k
      actCell+=numCell;
1134
713k
    }
1135
39.2k
    f << "],";
1136
39.2k
    m_state->m_rowSheetIdToStyleIdMap[Vec2i(row,sheetId)]=rowStyleId;
1137
39.2k
    if (actCell>256)
1138
2.33k
    {
1139
2.33k
      f << "###";
1140
2.33k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: find too much cells\n"));
1141
2.33k
    }
1142
39.2k
    break;
1143
0
  }
1144
1.74k
  case 1: // the last row definition, maybe the actual row style ?
1145
1.74k
    f << "last,";
1146
1.74k
    if (sz<12)
1147
299
    {
1148
299
      WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the size seems bad\n"));
1149
299
      f << "###sz,";
1150
299
      break;
1151
299
    }
1152
13.0k
    for (int i=0; i<8; ++i)  // f0=0|32|41|71|7e|fe,f1=0|1|40|50|c0, f2=0|4|5|b|41, f3=0|40|54|5c, f4=27, other 0
1153
11.5k
    {
1154
11.5k
      val=int(libwps::readU8(input));
1155
11.5k
      if (val)
1156
4.05k
        f << "f" << i << "=" << std::hex << val << std::dec << ",";
1157
11.5k
    }
1158
1.44k
    break;
1159
206k
  case 2:
1160
206k
  {
1161
206k
    f << "dup,";
1162
206k
    if (sz!=8)
1163
54
    {
1164
54
      WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the size seems bad\n"));
1165
54
      f << "###sz,";
1166
54
      break;
1167
54
    }
1168
206k
    auto sheetId2=int(libwps::readU8(input));
1169
206k
    if (sheetId2!=sheetId)
1170
3.35k
      f << "#sheetId2=" << sheetId2 << ",";
1171
206k
    val=int(libwps::readU8(input)); // always 0?
1172
206k
    if (val)
1173
3.72k
      f << "f0=" << val << ",";
1174
206k
    val=int (libwps::readU16(input));
1175
206k
    if (val>=row)
1176
5.03k
    {
1177
5.03k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the original row seems bad\n"));
1178
5.03k
      f << "#";
1179
5.03k
    }
1180
206k
    m_state->m_rowSheetIdToChildRowIdMap.insert
1181
206k
    (std::multimap<Vec2i,Vec2i>::value_type(Vec2i(val,sheetId2),Vec2i(row,sheetId)));
1182
206k
    f << "orig[row]=" << val << ",";
1183
206k
    break;
1184
206k
  }
1185
7.48k
  default:
1186
7.48k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: find unknown row type\n"));
1187
7.48k
    f << "###type=" << rowType << ",";
1188
7.48k
    break;
1189
255k
  }
1190
255k
  if (input->tell()!=endPos)
1191
17.3k
  {
1192
17.3k
    ascFile.addDelimiter(input->tell(),'|');
1193
17.3k
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
1194
17.3k
  }
1195
255k
  ascFile.addPos(pos);
1196
255k
  ascFile.addNote(f.str().c_str());
1197
255k
  return true;
1198
255k
}
1199
1200
bool LotusSpreadsheet::readRowFormat(std::shared_ptr<WPSStream> stream, LotusSpreadsheetInternal::Style &style, int &numCell, long endPos)
1201
722k
{
1202
722k
  if (!stream) return false;
1203
722k
  numCell=1;
1204
1205
722k
  RVNGInputStreamPtr &input=stream->m_input;
1206
722k
  libwps::DebugStream f;
1207
722k
  long actPos=input->tell();
1208
722k
  if (endPos-actPos<4)
1209
2.67k
  {
1210
2.67k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the zone seems too short\n"));
1211
2.67k
    return false;
1212
2.67k
  }
1213
1214
720k
  int value[3];
1215
2.88M
  for (int i=0; i<3; ++i)
1216
2.16M
    value[i]=(i==1) ? int(libwps::readU16(input)) : int(libwps::readU8(input));
1217
720k
  WPSFont font;
1218
720k
  if (value[2]&0x80)
1219
145k
  {
1220
145k
    if (actPos+5>endPos)
1221
6.74k
    {
1222
6.74k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the zone seems too short\n"));
1223
6.74k
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
1224
6.74k
      return false;
1225
6.74k
    }
1226
139k
    value[2]&=0x7F;
1227
139k
    numCell=1+int(libwps::readU8(input));
1228
139k
  }
1229
713k
  if ((value[0]&0x80)==0)
1230
536k
    f << "protected?,";
1231
713k
  switch ((value[0]>>4)&7)
1232
713k
  {
1233
358k
  case 0: // fixed
1234
358k
    f << "fixed,";
1235
358k
    style.setFormat(WPSCellFormat::F_NUMBER, 1);
1236
358k
    style.setDigits(value[0]&0xF);
1237
358k
    break;
1238
37.6k
  case 1: // scientific
1239
37.6k
    style.setFormat(WPSCellFormat::F_NUMBER, 2);
1240
37.6k
    style.setDigits(value[0]&0xF);
1241
37.6k
    break;
1242
30.9k
  case 2: // currency
1243
30.9k
    style.setFormat(WPSCellFormat::F_NUMBER, 4);
1244
30.9k
    style.setDigits(value[0]&0xF);
1245
30.9k
    break;
1246
26.3k
  case 3: // percent
1247
26.3k
    style.setFormat(WPSCellFormat::F_NUMBER, 3);
1248
26.3k
    style.setDigits(value[0]&0xF);
1249
26.3k
    break;
1250
41.8k
  case 4: // decimal
1251
41.8k
    style.setFormat(WPSCellFormat::F_NUMBER, 1);
1252
41.8k
    style.setDigits(value[0]&0xF);
1253
41.8k
    break;
1254
133k
  case 7:
1255
133k
    switch (value[0]&0xF)
1256
133k
    {
1257
3.76k
    case 0: // +/- : kind of bool
1258
3.76k
      style.setFormat(WPSCellFormat::F_BOOLEAN);
1259
3.76k
      f << "+/-,";
1260
3.76k
      break;
1261
21.9k
    case 1:
1262
21.9k
      style.setFormat(WPSCellFormat::F_NUMBER, 0);
1263
21.9k
      break;
1264
5.39k
    case 2:
1265
5.39k
      style.setDTFormat(WPSCellFormat::F_DATE, "%d %B %y");
1266
5.39k
      break;
1267
3.77k
    case 3:
1268
3.77k
      style.setDTFormat(WPSCellFormat::F_DATE, "%d %B");
1269
3.77k
      break;
1270
5.05k
    case 4:
1271
5.05k
      style.setDTFormat(WPSCellFormat::F_DATE, "%B %y");
1272
5.05k
      break;
1273
3.42k
    case 5:
1274
3.42k
      style.setFormat(WPSCellFormat::F_TEXT);
1275
3.42k
      break;
1276
3.74k
    case 6:
1277
3.74k
      style.setFormat(WPSCellFormat::F_TEXT);
1278
3.74k
      font.m_attributes |= WPS_HIDDEN_BIT;
1279
3.74k
      break;
1280
3.04k
    case 7:
1281
3.04k
      style.setDTFormat(WPSCellFormat::F_TIME, "%I:%M:%S%p");
1282
3.04k
      break;
1283
3.10k
    case 8:
1284
3.10k
      style.setDTFormat(WPSCellFormat::F_TIME, "%I:%M%p");
1285
3.10k
      break;
1286
4.13k
    case 9:
1287
4.13k
      style.setDTFormat(WPSCellFormat::F_DATE, "%m/%d/%y");
1288
4.13k
      break;
1289
2.82k
    case 0xa:
1290
2.82k
      style.setDTFormat(WPSCellFormat::F_DATE, "%m/%d");
1291
2.82k
      break;
1292
2.45k
    case 0xb:
1293
2.45k
      style.setDTFormat(WPSCellFormat::F_TIME, "%H:%M:%S");
1294
2.45k
      break;
1295
2.51k
    case 0xc:
1296
2.51k
      style.setDTFormat(WPSCellFormat::F_TIME, "%H:%M");
1297
2.51k
      break;
1298
2.21k
    case 0xd:
1299
2.21k
      style.setFormat(WPSCellFormat::F_TEXT);
1300
2.21k
      f << "label,";
1301
2.21k
      break;
1302
63.0k
    case 0xf: // automatic
1303
63.0k
      break;
1304
2.85k
    default:
1305
2.85k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormat: find unknown 7e format\n"));
1306
2.85k
      f << "Fo=##7e,";
1307
2.85k
      break;
1308
133k
    }
1309
133k
    break;
1310
133k
  default:
1311
84.7k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormat: find unknown %x format\n", static_cast<unsigned int>(value[0]&0x7F)));
1312
84.7k
    f << "##Fo=" << std::hex << (value[0]&0x7F) << std::dec << ",";
1313
84.7k
    break;
1314
713k
  }
1315
1316
713k
  switch (value[2]&3)
1317
713k
  {
1318
79.5k
  case 1:
1319
79.5k
    style.setHAlignment(WPSCellFormat::HALIGN_LEFT);
1320
79.5k
    break;
1321
79.5k
  case 2:
1322
79.5k
    style.setHAlignment(WPSCellFormat::HALIGN_RIGHT);
1323
79.5k
    break;
1324
107k
  case 3:
1325
107k
    style.setHAlignment(WPSCellFormat::HALIGN_CENTER);
1326
107k
    break;
1327
446k
  default: // general
1328
446k
    break;
1329
713k
  }
1330
1331
713k
  if (value[1]&1)
1332
196k
    f << "red[neg],";
1333
713k
  if (value[1]&2)
1334
192k
    f << "add[parenthesis],";
1335
  /*
1336
    Now can either find some font definitions or a type id. I am not
1337
    sure how to distinguish these two cases ; this code does not
1338
    seem robust and may fail on some files...
1339
   */
1340
713k
  int wh=(value[2]>>2);
1341
713k
  int const vers=version();
1342
713k
  if (vers==1 && (wh&0x10))
1343
86.6k
  {
1344
86.6k
    int fId=(value[1]>>6)&0x3F;
1345
86.6k
    if (fId==0)
1346
10.7k
      ;
1347
75.9k
    else if ((wh&0xf)==5)
1348
12.0k
    {
1349
      /* unsure about this code, seems to work for Lotus123 mac,
1350
         but I never find a cell style in Lotus123 pc and this
1351
         part can be called*/
1352
12.0k
      if (!m_styleManager->updateCellStyle(fId, style, font, style.m_fontType))
1353
3.88k
        f << "#";
1354
12.0k
      f << "Ce" << fId << ",";
1355
12.0k
      wh &= 0xE0;
1356
12.0k
    }
1357
63.8k
    else if ((wh&0xf)==0)
1358
7.30k
    {
1359
7.30k
      if (!m_styleManager->updateFontStyle(fId, font, style.m_fontType))
1360
5.61k
        f << "#";
1361
7.30k
      f << "FS" << fId << ",";
1362
7.30k
      wh &= 0xE0;
1363
7.30k
    }
1364
56.5k
    else
1365
56.5k
      f << "#fId=" << fId << ",";
1366
86.6k
    value[1] &= 0xF03C;
1367
86.6k
  }
1368
626k
  else if (wh&0x10)
1369
103k
  {
1370
103k
    int fId=(value[1]>>6);
1371
103k
    if (fId==0)
1372
8.74k
      ;
1373
94.5k
    else if ((wh&0xf)==0)
1374
21.5k
    {
1375
21.5k
      if (!m_styleManager->updateCellStyle(fId, style, font, style.m_fontType))
1376
9.15k
        f << "#";
1377
21.5k
      f << "Ce" << fId << ",";
1378
21.5k
      wh &= 0xE0;
1379
21.5k
    }
1380
72.9k
    else
1381
72.9k
      f << "#fId=" << fId << ",";
1382
103k
    value[1] &= 0x3C;
1383
103k
  }
1384
523k
  else
1385
523k
  {
1386
523k
    if (value[1]&0x40)
1387
50.5k
      font.m_attributes |= WPS_BOLD_BIT;
1388
523k
    if (value[1]&0x80)
1389
43.0k
      font.m_attributes |= WPS_ITALICS_BIT;
1390
523k
    if (value[1]>>11)
1391
179k
      font.m_size=(value[1]>>11);
1392
    // values[1]&0x20 is often set in this case...
1393
523k
    value[1] &= 0x033c;
1394
523k
  }
1395
713k
  if (value[1])
1396
392k
    f << "f1=" << std::hex << value[1] << std::dec << ",";
1397
713k
  if (wh)
1398
276k
    f << "wh=" << std::hex << wh << std::dec << ",";
1399
713k
  if (font.m_size<=0)
1400
522k
    font.m_size=10; // if the size is not defined, set it to default
1401
713k
  style.setFont(font);
1402
713k
  style.m_extra=f.str();
1403
713k
  return true;
1404
713k
}
1405
1406
bool LotusSpreadsheet::readCellsFormat801(std::shared_ptr<WPSStream> stream, WPSVec3i const &minC, WPSVec3i const &maxC, int subZoneId)
1407
674k
{
1408
674k
  if (!stream) return false;
1409
674k
  RVNGInputStreamPtr &input=stream->m_input;
1410
674k
  libwps::DebugFile &ascFile=stream->m_ascii;
1411
674k
  libwps::DebugStream f;
1412
1413
674k
  long pos = input->tell();
1414
674k
  auto type = long(libwps::read16(input));
1415
674k
  if (type != 0x801)
1416
0
  {
1417
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: not a cells formats definition\n"));
1418
0
    return false;
1419
0
  }
1420
674k
  auto sz = long(libwps::readU16(input));
1421
674k
  long endPos = pos+4+sz;
1422
674k
  if (!stream->checkFilePosition(endPos))
1423
0
  {
1424
0
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1425
0
    return false;
1426
0
  }
1427
674k
  f << "Entries(Zone8):";
1428
674k
  if (minC==maxC)
1429
178k
    f << "setStyle[" << minC << "],";
1430
495k
  else
1431
495k
    f << "setStyle[" << minC << "<->" << maxC << "],";
1432
674k
  LotusSpreadsheetInternal::Table123Styles *tableStyle=nullptr;
1433
674k
  int const vers=version();
1434
674k
  if (minC[0]<=maxC[0] && minC[0]>=0)
1435
597k
    tableStyle=m_state->getTablesStyle(Vec2i(minC[0],maxC[0]));
1436
1437
674k
  int val;
1438
674k
  switch (sz)
1439
674k
  {
1440
198k
  case 2: // in general the second one
1441
198k
    val=int(libwps::readU16(input));
1442
198k
    if ((val>>8)==0x50)
1443
169k
    {
1444
169k
      if (tableStyle) tableStyle->addCellStyle(Vec2i(minC[1],maxC[1]),Vec2i(minC[2],maxC[2]),(val&0xFF));
1445
169k
      f << "Ce" << (val&0xFF);
1446
169k
    }
1447
29.2k
    else
1448
29.2k
    {
1449
29.2k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: find unexpected format type\n"));
1450
29.2k
      f << "##" << std::hex << val << std::dec << ",";
1451
29.2k
    }
1452
198k
    break;
1453
244k
  case 4:   // in general the first one
1454
244k
  {
1455
244k
    LotusSpreadsheetInternal::Format123Style format;
1456
244k
    int values[4];
1457
977k
    for (int &value : values) value=int(libwps::readU8(input));
1458
244k
    if ((values[0]&0x80)==0)
1459
164k
      f << "protected[no],";
1460
79.6k
    else
1461
79.6k
      values[0] &= 0x7f;
1462
244k
    if ((values[3]&0x80) && (values[0]>>4)==0)
1463
120k
    {
1464
120k
      if (values[2]>=0x7a)
1465
2.33k
        format.setDTFormat(WPSCellFormat::F_TIME, "%I:%M:%S%p");
1466
117k
      else if (values[2]>=0x63)
1467
3.36k
        format.setDTFormat(WPSCellFormat::F_DATE, "%m/%d/%y");
1468
114k
      else
1469
114k
      {
1470
114k
        format.setFormat(WPSCellFormat::F_NUMBER, 4);
1471
114k
        format.setDigits(values[0]&0xF);
1472
114k
      }
1473
120k
      values[3]&=0x7f;
1474
120k
    }
1475
124k
    else if (values[0]!=0x7f)
1476
113k
    {
1477
113k
      switch ((values[0]>>4))
1478
113k
      {
1479
58.7k
      case 0: // fixed
1480
58.7k
        f << "fixed,";
1481
58.7k
        format.setFormat(WPSCellFormat::F_NUMBER, 1);
1482
58.7k
        format.setDigits(values[0]&0xF);
1483
58.7k
        break;
1484
2.75k
      case 1: // scientific
1485
2.75k
        format.setFormat(WPSCellFormat::F_NUMBER, 2);
1486
2.75k
        format.setDigits(values[0]&0xF);
1487
2.75k
        break;
1488
1.90k
      case 2: // currency
1489
1.90k
        format.setFormat(WPSCellFormat::F_NUMBER, 4);
1490
1.90k
        format.setDigits(values[0]&0xF);
1491
1.90k
        break;
1492
1.49k
      case 3: // percent
1493
1.49k
        format.setFormat(WPSCellFormat::F_NUMBER, 3);
1494
1.49k
        format.setDigits(values[0]&0xF);
1495
1.49k
        break;
1496
13.2k
      case 4: // decimal
1497
13.2k
        format.setFormat(WPSCellFormat::F_NUMBER, 1);
1498
13.2k
        format.setDigits(values[0]&0xF);
1499
13.2k
        break;
1500
31.4k
      case 7: // checkme
1501
31.4k
        switch (values[0]&0xF)
1502
31.4k
        {
1503
92
        case 0: // +/- : kind of bool
1504
92
          format.setFormat(WPSCellFormat::F_BOOLEAN);
1505
92
          f << "+/-,";
1506
92
          break;
1507
4.88k
        case 1:
1508
4.88k
          format.setFormat(WPSCellFormat::F_NUMBER, 0);
1509
4.88k
          break;
1510
230
        case 2:
1511
230
          format.setDTFormat(WPSCellFormat::F_DATE, "%d %B %y");
1512
230
          break;
1513
451
        case 3:
1514
451
          format.setDTFormat(WPSCellFormat::F_DATE, "%d %B");
1515
451
          break;
1516
746
        case 4:
1517
746
          format.setDTFormat(WPSCellFormat::F_DATE, "%B %y");
1518
746
          break;
1519
4.67k
        case 5:
1520
4.67k
          format.setFormat(WPSCellFormat::F_TEXT);
1521
4.67k
          break;
1522
2.30k
        case 6:
1523
2.30k
          format.setFormat(WPSCellFormat::F_TEXT);
1524
          // font.m_attributes |= WPS_HIDDEN_BIT;
1525
2.30k
          break;
1526
625
        case 7:
1527
625
          format.setDTFormat(WPSCellFormat::F_TIME, "%I:%M:%S%p");
1528
625
          break;
1529
2.93k
        case 8:
1530
2.93k
          format.setDTFormat(WPSCellFormat::F_TIME, "%I:%M%p");
1531
2.93k
          break;
1532
3.53k
        case 9:
1533
3.53k
          format.setDTFormat(WPSCellFormat::F_DATE, "%m/%d/%y");
1534
3.53k
          break;
1535
843
        case 0xa:
1536
843
          format.setDTFormat(WPSCellFormat::F_DATE, "%m/%d");
1537
843
          break;
1538
947
        case 0xb:
1539
947
          format.setDTFormat(WPSCellFormat::F_TIME, "%H:%M:%S");
1540
947
          break;
1541
2.92k
        case 0xc:
1542
2.92k
          format.setDTFormat(WPSCellFormat::F_TIME, "%H:%M");
1543
2.92k
          break;
1544
4.73k
        case 0xd:
1545
4.73k
          format.setFormat(WPSCellFormat::F_TEXT);
1546
4.73k
          f << "label,";
1547
4.73k
          break;
1548
1.59k
        default:
1549
1.59k
          WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: find unknown 7e format\n"));
1550
1.59k
          f << "Fo=##7e,";
1551
1.59k
          break;
1552
31.4k
        }
1553
31.4k
        break;
1554
31.4k
      default:
1555
3.89k
        WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: find unknown %x format\n", static_cast<unsigned int>(values[0])));
1556
3.89k
        f << "##Fo=" << std::hex << values[0] << std::dec << ",";
1557
3.89k
        break;
1558
113k
      }
1559
113k
    }
1560
244k
    f << format << ",";
1561
1562
244k
    if (values[1]&1) f << "neg[value,red],";
1563
244k
    if (values[1]&2) f << "add[parenthesis],";
1564
244k
    if (values[1]&0x10)
1565
27.5k
    {
1566
27.5k
      format.m_alignAcrossColumn=true;
1567
27.5k
      f << "align[across,column],";
1568
27.5k
    }
1569
244k
    if (values[1]&0x20) f << "hidden,";
1570
244k
    values[1] &=0xCC;
1571
977k
    for (int i=1; i<4; ++i)
1572
733k
    {
1573
733k
      if (values[i]) f << "f" << i << "=" << std::hex << values[i] << std::dec << ",";
1574
733k
    }
1575
244k
    if (tableStyle) tableStyle->addCellStyle(Vec2i(minC[1],maxC[1]),Vec2i(minC[2],maxC[2]),format);
1576
244k
    break;
1577
244k
  }
1578
188k
  case 8:   // in general the third one, this define border, ...
1579
188k
  {
1580
188k
    LotusSpreadsheetInternal::Extra123Style style;
1581
565k
    for (int i=0; i<2; ++i)
1582
376k
    {
1583
376k
      auto col=int(libwps::readU8(input));
1584
376k
      val=int(libwps::readU8(input));
1585
376k
      if ((val&0xF)==0xF) continue;
1586
367k
      f << (i==0 ?  "bordT" : "bordL") << "=[";
1587
367k
      WPSBorder border;
1588
367k
      switch (val&0xF)
1589
367k
      {
1590
57.7k
      case 0:
1591
57.7k
        border.m_style=WPSBorder::None;
1592
57.7k
        break;
1593
264k
      case 1: // single
1594
264k
        break;
1595
1.29k
      case 2:
1596
1.29k
        border.m_type=WPSBorder::Double;
1597
1.29k
        break;
1598
5.87k
      case 3:
1599
5.87k
        border.m_width=2;
1600
5.87k
        break;
1601
6.25k
      case 4: // dot[1x1]
1602
6.25k
        border.m_style=WPSBorder::Dot;
1603
6.25k
        break;
1604
2.54k
      case 5:
1605
2.54k
        border.m_style=WPSBorder::Dash;
1606
2.54k
        f << "dash[1x3],";
1607
2.54k
        break;
1608
3.38k
      case 6:
1609
3.38k
        border.m_style=WPSBorder::Dash;
1610
3.38k
        f << "dash[3x1],";
1611
3.38k
        break;
1612
2.67k
      case 7:
1613
2.67k
        border.m_style=WPSBorder::Dash;
1614
2.67k
        f << "dash[1x1,3x1],";
1615
2.67k
        break;
1616
15.5k
      case 8:
1617
15.5k
        border.m_style=WPSBorder::Dash;
1618
15.5k
        f << "dash[1x1,1x1,2x1],";
1619
15.5k
        break;
1620
7.46k
      default:
1621
7.46k
        f << "##type=" << (val&0xF) << ",";
1622
7.46k
        break;
1623
367k
      }
1624
367k
      if (!m_styleManager->getColor256(col,border.m_color))
1625
0
        f << "##colId=" << col << ",";
1626
367k
      f << border;
1627
367k
      f << "],";
1628
367k
      style.m_borders[i]=border;
1629
367k
    }
1630
188k
    f << "unk0=[";
1631
942k
    for (int i=0; i<4; ++i)
1632
753k
    {
1633
753k
      val=int(libwps::readU8(input));
1634
753k
      if (val)
1635
139k
        f <<std::hex << val << std::dec << ",";
1636
614k
      else
1637
614k
        f << "_,";
1638
753k
    }
1639
188k
    f << "],";
1640
188k
    if (tableStyle) tableStyle->addCellStyle(Vec2i(minC[1],maxC[1]),Vec2i(minC[2],maxC[2]),style);
1641
188k
    break;
1642
188k
  }
1643
25.3k
  case 12:   // column data, in general the fourst one
1644
25.3k
  {
1645
25.3k
    if (subZoneId==0) f << "col,";
1646
21.9k
    else if (subZoneId==1) f << "row,";
1647
10.1k
    else if (subZoneId!=-1)
1648
0
    {
1649
0
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: the zone8 id seems bad\n"));
1650
0
      f << "###zone15=" << subZoneId << ",";
1651
0
    }
1652
25.3k
    int width=-1;
1653
25.3k
    bool isDefault=false;
1654
25.3k
    bool isWidthDef=true;
1655
25.3k
    f << "colUnkn=[";
1656
202k
    for (int i=0; i<7; ++i)   // f1=[01][04][01], f2: big number, f3=0|1, f4=0|2d|3c|240
1657
177k
    {
1658
177k
      if (i==2 && vers>=5) continue;
1659
171k
      val=(vers<5 && (i==2||i==4)) ? int(libwps::readU8(input)) : int(libwps::readU16(input));
1660
171k
      if (i==1)
1661
25.3k
      {
1662
25.3k
        if ((val&1)==0)
1663
3.32k
        {
1664
3.32k
          isDefault=true;
1665
3.32k
          f << "no[w],";
1666
3.32k
        }
1667
25.3k
        if (val&2) f << "hidden,";
1668
25.3k
        if (val&0x20) f << "page[break],";
1669
25.3k
        if (val&0x40)
1670
4.81k
        {
1671
4.81k
          isWidthDef=false;
1672
4.81k
          f << "w[def],";
1673
4.81k
        }
1674
25.3k
        if (val&0x100)
1675
13.3k
          f << "fl100,";
1676
25.3k
        val &= 0xFE9C;
1677
25.3k
        if (val)
1678
5.91k
          f << "##fl=" << std::hex << val << std::dec << ",";
1679
25.3k
      }
1680
146k
      else if (i==3)
1681
25.3k
      {
1682
25.3k
        width=val;
1683
25.3k
        if (!isDefault)
1684
22.0k
          f << "w=" << width << ",";
1685
25.3k
      }
1686
120k
      else if (val)
1687
37.0k
        f <<std::hex << val << std::dec << ",";
1688
83.6k
      else
1689
83.6k
        f << "_,";
1690
171k
    }
1691
25.3k
    f << "],";
1692
25.3k
    if (isDefault || minC[1]<0 || width<0) break;
1693
18.0k
    if (subZoneId<0 || subZoneId>1) break;
1694
1.03G
    for (int i=minC[0]; i<=maxC[0]; ++i)
1695
1.03G
    {
1696
1.03G
      auto &sheet=m_state->getSheet(i);
1697
2.66G
      for (int c=minC[1]; c<=std::min(maxC[1], MAX_COLUMNS); ++c)
1698
1.63G
      {
1699
1.63G
        if (subZoneId==0)
1700
1.38G
        {
1701
1.38G
          WPSColumnFormat format(!isWidthDef ? 72 : vers>=5 ? float(width)/16.f : float(width));
1702
1.38G
          sheet.setColumnWidth(c, format);
1703
1.38G
        }
1704
243M
        else
1705
243M
        {
1706
243M
          WPSRowFormat format(vers>=5 ? float(width)/16.f : float(width));
1707
243M
          format.m_useOptimalHeight=!isWidthDef;
1708
243M
          sheet.setRowHeight(c, format);
1709
243M
        }
1710
1.63G
      }
1711
1.03G
    }
1712
10.1k
    break;
1713
18.0k
  }
1714
983
  case 30: // table data, rare, the last one
1715
2.94k
    for (int i=0; i<2; ++i)   // f0=71|7e
1716
1.96k
    {
1717
1.96k
      val=int(libwps::readU16(input));
1718
1.96k
      if (val)
1719
1.80k
        f << "f" << i << "=" << val << ",";
1720
1.96k
    }
1721
983
    f << "],";
1722
983
    val=int(libwps::readU16(input));
1723
983
    if ((val>>8)==0x50)
1724
346
    {
1725
346
      if (tableStyle) tableStyle->m_defaultCellId=(val&0xFF);
1726
346
      f << "Ce" << (val&0xFF) << ",";
1727
346
    }
1728
637
    else
1729
637
    {
1730
637
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: find unexpected format type\n"));
1731
637
      f << "##" << std::hex << val << std::dec << ",";
1732
637
    }
1733
983
    f << "tableUnk=[";
1734
12.7k
    for (int i=0; i<12; ++i)
1735
11.7k
    {
1736
      // f0=168|438|3000|3600, f2=f0|14a|a008|c00,f4=[02]0[04c]0,f6=0|3
1737
      // f8=[37]5d4,f10=0|4,f11=1003(L3?)
1738
11.7k
      val=int(libwps::readU16(input));
1739
11.7k
      if (val)
1740
9.67k
        f <<std::hex << val << std::dec << ",";
1741
2.11k
      else
1742
2.11k
        f << "_,";
1743
11.7k
    }
1744
983
    f << "],";
1745
983
    break;
1746
16.9k
  default:
1747
16.9k
    break;
1748
674k
  }
1749
674k
  ascFile.addPos(pos);
1750
674k
  ascFile.addNote(f.str().c_str());
1751
674k
  if (input->tell()!=endPos)
1752
5.58k
  {
1753
5.58k
    ascFile.addDelimiter(input->tell(),'|');
1754
5.58k
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
1755
5.58k
  }
1756
674k
  return true;
1757
674k
}
1758
1759
bool LotusSpreadsheet::readRowSizes(std::shared_ptr<WPSStream> stream, long endPos)
1760
1.24k
{
1761
1.24k
  if (!stream) return false;
1762
1.24k
  RVNGInputStreamPtr &input=stream->m_input;
1763
1.24k
  libwps::DebugFile &ascFile=stream->m_ascii;
1764
1.24k
  libwps::DebugStream f;
1765
1766
1.24k
  long pos = input->tell();
1767
1.24k
  long sz=endPos-pos;
1768
1.24k
  f << "Entries(RowSize):";
1769
1.24k
  if (sz<10 || (sz%8)!=2)
1770
164
  {
1771
164
    WPS_DEBUG_MSG(("LotusParser::readRowSizes: the zone size seems bad\n"));
1772
164
    f << "###";
1773
164
    ascFile.addPos(pos-6);
1774
164
    ascFile.addNote(f.str().c_str());
1775
164
    return true;
1776
164
  }
1777
1778
1.07k
  auto sheetId=int(libwps::readU8(input));
1779
1.07k
  f << "id[sheet]=" << sheetId << ",";
1780
1.07k
  LotusSpreadsheetInternal::Spreadsheet empty, *sheet=nullptr;
1781
1.07k
  if (sheetId>=int(m_state->m_spreadsheetList.size()))
1782
160
  {
1783
160
    WPS_DEBUG_MSG(("LotusSpreadsheet::readRowSizes: can find spreadsheet %d\n", sheetId));
1784
160
    sheet=&empty;
1785
160
    f << "###";
1786
160
  }
1787
918
  else
1788
918
    sheet=&m_state->m_spreadsheetList[size_t(sheetId)];
1789
1.07k
  auto val=int(libwps::readU8(input));  // always 0
1790
1.07k
  if (val) f << "f0=" << val << ",";
1791
1.07k
  ascFile.addPos(pos-6);
1792
1.07k
  ascFile.addNote(f.str().c_str());
1793
1.07k
  auto N=int(sz/8);
1794
33.4k
  for (int i=0; i<N; ++i)
1795
32.3k
  {
1796
32.3k
    pos=input->tell();
1797
32.3k
    f.str("");
1798
32.3k
    f << "RowSize-" << i << ":";
1799
32.3k
    auto row=int(libwps::readU16(input));
1800
32.3k
    f << "row=" << row << ",";
1801
32.3k
    val=int(libwps::readU16(input));
1802
32.3k
    if (val!=0xFFFF)
1803
29.3k
    {
1804
29.3k
      f << "dim=" << float(val+31)/32.f << ",";
1805
29.3k
      sheet->setRowHeight(row, WPSRowFormat(float(val+31)/32.f));
1806
29.3k
    }
1807
97.1k
    for (int j=0; j<2; ++j)
1808
64.7k
    {
1809
64.7k
      val=int(libwps::read16(input));
1810
64.7k
      if (val!=j-1)
1811
56.3k
        f << "f" << j << "=" << val <<",";
1812
64.7k
    }
1813
32.3k
    input->seek(pos+8, librevenge::RVNG_SEEK_SET);
1814
32.3k
    ascFile.addPos(pos);
1815
32.3k
    ascFile.addNote(f.str().c_str());
1816
32.3k
  }
1817
1818
1.07k
  return true;
1819
1.24k
}
1820
1821
bool LotusSpreadsheet::readSheetName(std::shared_ptr<WPSStream> stream)
1822
8.23k
{
1823
8.23k
  if (!stream) return false;
1824
8.23k
  RVNGInputStreamPtr &input=stream->m_input;
1825
8.23k
  libwps::DebugFile &ascFile=stream->m_ascii;
1826
8.23k
  libwps::DebugStream f;
1827
1828
8.23k
  long pos = input->tell();
1829
8.23k
  auto type = long(libwps::read16(input));
1830
8.23k
  if (type != 0x23)
1831
0
  {
1832
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetName: not a sheet name\n"));
1833
0
    return false;
1834
0
  }
1835
8.23k
  auto sz = long(libwps::readU16(input));
1836
8.23k
  f << "Entries(SheetName):";
1837
8.23k
  if (sz < 5)
1838
2.00k
  {
1839
2.00k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetName: sheet name is too short\n"));
1840
2.00k
    f << "###";
1841
2.00k
    ascFile.addPos(pos);
1842
2.00k
    ascFile.addNote(f.str().c_str());
1843
2.00k
    return true;
1844
2.00k
  }
1845
6.23k
  auto val=int(libwps::read16(input)); // always 14000
1846
6.23k
  if (val!=14000)
1847
5.68k
    f << "f0=" << std::hex << val << std::dec << ",";
1848
6.23k
  auto sheetId=int(libwps::readU8(input));
1849
6.23k
  f << "id[sheet]=" << sheetId << ",";
1850
6.23k
  val=int(libwps::readU8(input)); // always 0
1851
6.23k
  if (val)
1852
4.68k
    f << "f1=" << val << ",";
1853
6.23k
  std::string name;
1854
53.5k
  for (long i = 0; i < sz-4; i++)
1855
53.0k
  {
1856
53.0k
    char c = char(libwps::readU8(input));
1857
53.0k
    if (c == '\0') break;
1858
47.2k
    name.push_back(c);
1859
47.2k
  }
1860
6.23k
  f << name << ",";
1861
6.23k
  if (input->tell()!=pos+4+sz && input->tell()+1!=pos+4+sz)
1862
3.82k
  {
1863
3.82k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetName: the zone seems too short\n"));
1864
3.82k
    f << "##";
1865
3.82k
    ascFile.addDelimiter(input->tell(), '|');
1866
3.82k
  }
1867
6.23k
  if (sheetId>=m_state->getNumSheet())
1868
1.59k
  {
1869
1.59k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetName: the zone id seems bad\n"));
1870
1.59k
    f << "##id";
1871
1.59k
  }
1872
4.63k
  else if (!name.empty())
1873
3.91k
    m_state->getSheet(sheetId).m_name=libwps_tools_win::Font::unicodeString(name, m_mainParser.getDefaultFontType());
1874
6.23k
  ascFile.addPos(pos);
1875
6.23k
  ascFile.addNote(f.str().c_str());
1876
6.23k
  return true;
1877
8.23k
}
1878
1879
bool LotusSpreadsheet::readSheetHeader(std::shared_ptr<WPSStream> stream)
1880
6.90k
{
1881
6.90k
  if (!stream) return false;
1882
6.90k
  RVNGInputStreamPtr &input=stream->m_input;
1883
6.90k
  libwps::DebugFile &ascFile=stream->m_ascii;
1884
6.90k
  libwps::DebugStream f;
1885
1886
6.90k
  long pos = input->tell();
1887
6.90k
  auto type = long(libwps::read16(input));
1888
6.90k
  if (type!=0xc3)
1889
0
  {
1890
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetHeader: not a sheet header\n"));
1891
0
    return false;
1892
0
  }
1893
6.90k
  auto sz = long(libwps::readU16(input));
1894
6.90k
  f << "Entries(FMTSheetBegin):";
1895
6.90k
  if (sz != 0x22)
1896
300
  {
1897
300
    WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetHeader: the zone size seems bad\n"));
1898
300
    f << "###";
1899
300
    ascFile.addPos(pos);
1900
300
    ascFile.addNote(f.str().c_str());
1901
300
    return true;
1902
300
  }
1903
6.60k
  auto id=int(libwps::read16(input));
1904
6.60k
  if (id<0)
1905
214
  {
1906
214
    WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetHeader: the id seems bad\n"));
1907
214
    f << "###";
1908
214
    m_state->m_sheetCurrentId=-1;
1909
214
  }
1910
6.39k
  else
1911
6.39k
    m_state->m_sheetCurrentId=id;
1912
6.60k
  f << "id=" << id << ",";
1913
112k
  for (int i=0; i<16; ++i)   // always 0
1914
105k
  {
1915
105k
    auto val=int(libwps::read16(input));
1916
105k
    if (val) f << "f" << i << "=" << val << ",";
1917
105k
  }
1918
6.60k
  ascFile.addPos(pos);
1919
6.60k
  ascFile.addNote(f.str().c_str());
1920
6.60k
  return true;
1921
6.90k
}
1922
1923
bool LotusSpreadsheet::readExtraRowFormats(std::shared_ptr<WPSStream> stream)
1924
24.1k
{
1925
24.1k
  if (!stream) return false;
1926
24.1k
  RVNGInputStreamPtr &input=stream->m_input;
1927
24.1k
  libwps::DebugFile &ascFile=stream->m_ascii;
1928
24.1k
  libwps::DebugStream f;
1929
1930
24.1k
  long pos = input->tell();
1931
24.1k
  auto type = long(libwps::read16(input));
1932
24.1k
  if (type!=0xc5)
1933
0
  {
1934
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: not a sheet header\n"));
1935
0
    return false;
1936
0
  }
1937
24.1k
  auto sz = long(libwps::readU16(input));
1938
24.1k
  f << "Entries(FMTRowForm):";
1939
24.1k
  if (sz < 9 || (sz%5)!=4)
1940
1.42k
  {
1941
1.42k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: the zone size seems bad\n"));
1942
1.42k
    f << "###";
1943
1.42k
    ascFile.addPos(pos);
1944
1.42k
    ascFile.addNote(f.str().c_str());
1945
1.42k
    return true;
1946
1.42k
  }
1947
22.7k
  auto row=int(libwps::readU16(input));
1948
22.7k
  f << "row=" << row << ",";
1949
1950
22.7k
  auto &sheet=m_state->getSheet(m_state->m_sheetCurrentId);
1951
22.7k
  auto val=int(libwps::readU8(input));
1952
22.7k
  sheet.setRowHeight(row, WPSRowFormat(float(val)));
1953
22.7k
  if (val!=14) f << "height=" << val << ",";
1954
22.7k
  val=int(libwps::readU8(input)); // 10|80
1955
22.7k
  if (val) f << "f0=" << std::hex << val << std::dec << ",";
1956
22.7k
  ascFile.addPos(pos);
1957
22.7k
  ascFile.addNote(f.str().c_str());
1958
1959
22.7k
  LotusSpreadsheetInternal::ExtraRowStyles badRow;
1960
22.7k
  auto *rowStyles=&badRow;
1961
22.7k
  if (sheet.m_rowToExtraStyleMap.find(row)==sheet.m_rowToExtraStyleMap.end())
1962
12.5k
  {
1963
12.5k
    sheet.m_rowToExtraStyleMap[row]=LotusSpreadsheetInternal::ExtraRowStyles();
1964
12.5k
    rowStyles=&sheet.m_rowToExtraStyleMap.find(row)->second;
1965
12.5k
  }
1966
10.1k
  else
1967
10.1k
  {
1968
10.1k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: row %d is already defined\n", row));
1969
10.1k
  }
1970
22.7k
  auto N=int(sz/5);
1971
22.7k
  int begPos=0;
1972
271k
  for (int i=0; i<N; ++i)
1973
248k
  {
1974
248k
    pos=input->tell();
1975
248k
    f.str("");
1976
248k
    f << "FMTRowForm-" << i << ":";
1977
248k
    LotusSpreadsheetInternal::ExtraStyle style;
1978
248k
    val=style.m_format=int(libwps::readU8(input));
1979
    // find also f[8-c]ffffffXX, which seems to have a different meaning
1980
248k
    if ((val>>4)==0xf) f << "#";
1981
248k
    if (val&0x7) f << "font[id]=" << (val&0x7) << ","; // useMe
1982
248k
    if (val&0x8) f << "bold,";
1983
248k
    if (val&0x10) f << "italic,";
1984
248k
    if (val&0x20) f << "underline,";
1985
248k
    val &= 0xC0;
1986
248k
    if (val) f << "fl=" << std::hex << val << std::dec << ",";
1987
248k
    style.m_flag=val=int(libwps::readU8(input));
1988
248k
    if (val&0x20) f << "special,";
1989
248k
    if (!m_styleManager->getColor8(val&7, style.m_color))
1990
0
    {
1991
0
      WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: can not read a color\n"));
1992
0
      f << "##colId=" << (val&7) << ",";
1993
0
    }
1994
248k
    else if (!style.m_color.isBlack()) f << "col=" << style.m_color << ",";
1995
248k
    val &= 0xD8;
1996
248k
    if (val) f << "fl1=" << std::hex << val << std::dec << ",";
1997
248k
    val=int(libwps::readU8(input));
1998
248k
    if (val&7)
1999
143k
    {
2000
143k
      if ((val&7)==7) // checkMe
2001
69.6k
        style.m_backColor=WPSColor::black();
2002
74.3k
      else if (!m_styleManager->getColor8(val&7, style.m_backColor))
2003
0
      {
2004
0
        WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: can not read a color\n"));
2005
0
        f << "##colId=" << (val&7) << ",";
2006
0
      }
2007
74.3k
      else
2008
74.3k
        f << "col[back]=" << style.m_backColor << ",";
2009
143k
    }
2010
248k
    if (val&0x10) f << "shadow2";
2011
248k
    if (val&0x20) f << "shadow,";
2012
248k
    val &= 0xD8;
2013
248k
    if (val) f << "f0=" << std::hex << val << std::dec << ",";
2014
248k
    style.m_borders=val=int(libwps::readU8(input));
2015
248k
    if (val) f << "border=" << std::hex << val << std::dec << ",";
2016
248k
    val=int(libwps::readU8(input));
2017
248k
    if (val) f << "dup=" << val << ",";
2018
248k
    rowStyles->m_colsToStyleMap[Vec2i(begPos, begPos+val)]=style;
2019
248k
    begPos+=1+val;
2020
248k
    ascFile.addPos(pos);
2021
248k
    ascFile.addNote(f.str().c_str());
2022
248k
    input->seek(pos+5, librevenge::RVNG_SEEK_SET);
2023
248k
  }
2024
22.7k
  if (begPos!=256)
2025
22.5k
  {
2026
22.5k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: the number of columns for row %d seems bad\n", row));
2027
22.5k
  }
2028
22.7k
  return true;
2029
24.1k
}
2030
2031
////////////////////////////////////////////////////////////
2032
// Cell
2033
////////////////////////////////////////////////////////////
2034
bool LotusSpreadsheet::readCellName(std::shared_ptr<WPSStream> stream)
2035
13.1k
{
2036
13.1k
  if (!stream) return false;
2037
13.1k
  RVNGInputStreamPtr &input=stream->m_input;
2038
13.1k
  libwps::DebugFile &ascFile=stream->m_ascii;
2039
13.1k
  libwps::DebugStream f;
2040
2041
13.1k
  long pos = input->tell();
2042
13.1k
  auto type = long(libwps::read16(input));
2043
13.1k
  if (type!=9)
2044
0
  {
2045
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::readCellName: not a cell name cell\n"));
2046
0
    return false;
2047
0
  }
2048
13.1k
  auto sz = long(libwps::readU16(input));
2049
13.1k
  long endPos=pos+4+sz;
2050
13.1k
  f << "Entries(CellName):";
2051
13.1k
  if (sz < 0x1a)
2052
1.32k
  {
2053
1.32k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readCellName: the zone is too short\n"));
2054
1.32k
    f << "###";
2055
1.32k
    ascFile.addPos(pos);
2056
1.32k
    ascFile.addNote(f.str().c_str());
2057
1.32k
    return true;
2058
1.32k
  }
2059
11.8k
  auto val=int(libwps::read16(input)); // 0 or 1
2060
11.8k
  if (val)
2061
6.62k
    f << "f0=" << val << ",";
2062
11.8k
  std::string name("");
2063
75.7k
  for (int i=0; i<16; ++i)
2064
75.1k
  {
2065
75.1k
    auto c=char(libwps::readU8(input));
2066
75.1k
    if (!c) break;
2067
63.9k
    name += c;
2068
63.9k
  }
2069
11.8k
  f << name << ",";
2070
11.8k
  input->seek(pos+4+18, librevenge::RVNG_SEEK_SET);
2071
11.8k
  LotusSpreadsheetInternal::CellsList cells;
2072
35.4k
  for (int i=0; i<2; ++i)
2073
23.6k
  {
2074
23.6k
    auto row=int(libwps::readU16(input));
2075
23.6k
    auto sheetId=int(libwps::readU8(input));
2076
23.6k
    auto col=int(libwps::readU8(input));
2077
23.6k
    if (i==0)
2078
11.8k
      cells.m_positions.setMin(Vec2i(col,row));
2079
11.8k
    else
2080
11.8k
      cells.m_positions.setMax(Vec2i(col,row));
2081
23.6k
    cells.m_ids[i]=sheetId;
2082
23.6k
  }
2083
11.8k
  f << cells << ",";
2084
11.8k
  if (m_state->m_nameToCellsMap.find(name)!=m_state->m_nameToCellsMap.end())
2085
4.50k
  {
2086
4.50k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readCellName: cell with name %s already exists\n", name.c_str()));
2087
4.50k
    f << "##name=" << name << ",";
2088
4.50k
  }
2089
7.32k
  else
2090
7.32k
    m_state->m_nameToCellsMap[name]=cells;
2091
11.8k
  std::string note("");
2092
11.8k
  auto remain=int(endPos-input->tell());
2093
22.7k
  for (int i=0; i<remain; ++i)
2094
15.0k
  {
2095
15.0k
    auto c=char(libwps::readU8(input));
2096
15.0k
    if (!c) break;
2097
10.8k
    note+=c;
2098
10.8k
  }
2099
11.8k
  if (!note.empty())
2100
3.54k
    f << "note=" << note << ",";
2101
11.8k
  if (input->tell()!=endPos)
2102
4.00k
    ascFile.addDelimiter(input->tell(),'|');
2103
2104
11.8k
  ascFile.addPos(pos);
2105
11.8k
  ascFile.addNote(f.str().c_str());
2106
11.8k
  return true;
2107
13.1k
}
2108
2109
bool LotusSpreadsheet::readCell(std::shared_ptr<WPSStream> stream)
2110
211k
{
2111
211k
  if (!stream) return false;
2112
211k
  RVNGInputStreamPtr &input=stream->m_input;
2113
211k
  libwps::DebugFile &ascFile=stream->m_ascii;
2114
211k
  libwps::DebugStream f;
2115
2116
211k
  long pos = input->tell();
2117
211k
  auto type = long(libwps::read16(input));
2118
211k
  std::string what("");
2119
211k
  if (type == 0x16)
2120
46.5k
    what="TextCell";
2121
165k
  else if (type == 0x17)
2122
9.18k
    what="Doub10Cell";
2123
155k
  else if (type == 0x18)
2124
13.1k
    what="DoubU16Cell";
2125
142k
  else if (type == 0x19)
2126
14.7k
    what="Doub10FormCell";
2127
127k
  else if (type == 0x1a) // checkme
2128
19.0k
    what="TextFormCell";
2129
108k
  else if (type == 0x25)
2130
59.8k
    what="DoubU32Cell";
2131
49.0k
  else if (type == 0x26)
2132
4.70k
    what="CommentCell";
2133
44.3k
  else if (type == 0x27)
2134
4.02k
    what="Doub8Cell";
2135
40.2k
  else if (type == 0x28)
2136
40.2k
    what="Doub8FormCell";
2137
0
  else
2138
0
  {
2139
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: not a cell's cell\n"));
2140
0
    return false;
2141
0
  }
2142
2143
211k
  auto sz = long(libwps::readU16(input));
2144
211k
  if (sz < 5)
2145
15.2k
  {
2146
15.2k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the zone is too short\n"));
2147
15.2k
    f << "Entries(" << what << "):###";
2148
15.2k
    ascFile.addPos(pos);
2149
15.2k
    ascFile.addNote(f.str().c_str());
2150
15.2k
    return true;
2151
15.2k
  }
2152
196k
  long endPos=pos+4+sz;
2153
196k
  auto row=int(libwps::readU16(input));
2154
196k
  auto sheetId=int(libwps::readU8(input));
2155
196k
  auto col=int(libwps::readU8(input));
2156
196k
  if (sheetId) f << "sheet[id]=" << sheetId << ",";
2157
2158
196k
  LotusSpreadsheetInternal::Spreadsheet empty, *sheet=nullptr;
2159
196k
  if (sheetId>=int(m_state->m_spreadsheetList.size()))
2160
22.6k
  {
2161
22.6k
    WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can find spreadsheet %d\n", sheetId));
2162
22.6k
    sheet=&empty;
2163
22.6k
    f << "###";
2164
22.6k
  }
2165
173k
  else
2166
173k
    sheet=&m_state->m_spreadsheetList[size_t(sheetId)];
2167
2168
196k
  auto &cell=sheet->getCell(stream->m_input, Vec2i(col, row));
2169
196k
  switch (type)
2170
196k
  {
2171
45.5k
  case 0x16:
2172
58.4k
  case 0x1a:
2173
62.2k
  case 0x26:   // comment
2174
62.2k
  {
2175
62.2k
    std::string text("");
2176
62.2k
    long begText=input->tell();
2177
580k
    for (long i=4; i<sz; ++i)
2178
574k
    {
2179
574k
      auto c=char(libwps::readU8(input));
2180
574k
      if (!c) break;
2181
518k
      if (i==4)
2182
55.3k
      {
2183
55.3k
        bool done=true;
2184
55.3k
        if (c=='\'') cell.m_hAlignement=WPSCellFormat::HALIGN_DEFAULT; // sometimes followed by 0x7C
2185
34.1k
        else if (c=='\\') cell.m_hAlignement=WPSCellFormat::HALIGN_LEFT;
2186
33.7k
        else if (c=='^') cell.m_hAlignement=WPSCellFormat::HALIGN_CENTER;
2187
25.7k
        else if (c=='\"') cell.m_hAlignement=WPSCellFormat::HALIGN_RIGHT;
2188
15.1k
        else done=false;
2189
55.3k
        if (done)
2190
40.2k
        {
2191
40.2k
          ++begText;
2192
40.2k
          continue;
2193
40.2k
        }
2194
55.3k
      }
2195
478k
      text += c;
2196
478k
    }
2197
62.2k
    f << "\"" << getDebugStringForText(text) << "\",";
2198
62.2k
    WPSEntry entry;
2199
62.2k
    entry.setBegin(begText);
2200
62.2k
    entry.setEnd(endPos);
2201
62.2k
    if (type==0x16)
2202
45.5k
    {
2203
45.5k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_TEXT;
2204
45.5k
      cell.m_content.m_textEntry=entry;
2205
45.5k
    }
2206
16.6k
    else if (type==0x1a)
2207
12.8k
    {
2208
12.8k
      if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_FORMULA)
2209
11.1k
        cell.m_content.m_contentType=WKSContentListener::CellContent::C_TEXT;
2210
12.8k
      cell.m_content.m_textEntry=entry;
2211
12.8k
    }
2212
3.81k
    else if (type==0x26)
2213
3.81k
      cell.m_comment=entry;
2214
0
    else
2215
0
    {
2216
0
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: find unexpected type %x\n", unsigned(type)));
2217
0
      f << "###type";
2218
0
    }
2219
2220
62.2k
    if (input->tell()!=endPos && input->tell()+1!=endPos)
2221
8.53k
    {
2222
8.53k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the string zone seems too short\n"));
2223
8.53k
      f << "###";
2224
8.53k
    }
2225
62.2k
    break;
2226
58.4k
  }
2227
8.73k
  case 0x17:
2228
8.73k
  {
2229
8.73k
    if (sz!=14)
2230
7.90k
    {
2231
7.90k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the double10 zone seems too short\n"));
2232
7.90k
      f << "###";
2233
7.90k
    }
2234
8.73k
    double res;
2235
8.73k
    bool isNaN;
2236
8.73k
    if (!libwps::readDouble10(input, res, isNaN))
2237
424
    {
2238
424
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read a double10 zone\n"));
2239
424
      f << "###";
2240
424
      break;
2241
424
    }
2242
8.31k
    if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_FORMULA)
2243
8.27k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
2244
8.31k
    cell.m_content.setValue(res);
2245
8.31k
    break;
2246
8.73k
  }
2247
12.7k
  case 0x18:
2248
12.7k
  {
2249
12.7k
    if (sz!=6)
2250
2.47k
    {
2251
2.47k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the uint16 zone seems too short\n"));
2252
2.47k
      f << "###";
2253
2.47k
    }
2254
12.7k
    double res;
2255
12.7k
    bool isNaN;
2256
12.7k
    if (!libwps::readDouble2Inv(input, res, isNaN))
2257
5
    {
2258
5
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read a uint16 zone\n"));
2259
5
      f << "###";
2260
5
      break;
2261
5
    }
2262
12.7k
    if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_FORMULA)
2263
11.8k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
2264
12.7k
    cell.m_content.setValue(res);
2265
12.7k
    break;
2266
12.7k
  }
2267
14.3k
  case 0x19:
2268
14.3k
  {
2269
14.3k
    if (sz<=14)
2270
409
    {
2271
409
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the double10+formula zone seems too short\n"));
2272
409
      f << "###";
2273
409
    }
2274
14.3k
    double res;
2275
14.3k
    bool isNaN;
2276
14.3k
    if (!libwps::readDouble10(input, res, isNaN))
2277
563
    {
2278
563
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read double10+formula for zone\n"));
2279
563
      f << "###";
2280
563
      break;
2281
563
    }
2282
13.7k
    cell.m_content.m_contentType=WKSContentListener::CellContent::C_FORMULA;
2283
13.7k
    cell.m_content.setValue(res);
2284
13.7k
    ascFile.addDelimiter(input->tell(),'|');
2285
13.7k
    std::string error;
2286
13.7k
    if (!readFormula(*stream, endPos, sheetId, false, cell.m_content.m_formula, error))
2287
7.58k
    {
2288
7.58k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
2289
7.58k
      ascFile.addDelimiter(input->tell()-1, '#');
2290
7.58k
      if (error.length()) f << error;
2291
7.58k
      break;
2292
7.58k
    }
2293
6.16k
    if (error.length()) f << error;
2294
6.16k
    if (input->tell()+1>=endPos)
2295
5.69k
      break;
2296
472
    static bool first=true;
2297
472
    if (first)
2298
4
    {
2299
4
      first=false;
2300
4
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: find err message for double10+formula\n"));
2301
4
    }
2302
    // find in one file "Formula failed to convert"
2303
472
    error="";
2304
472
    auto remain=int(endPos-input->tell());
2305
14.7k
    for (int i=0; i<remain; ++i) error += char(libwps::readU8(input));
2306
472
    f << "#err[msg]=" << error << ",";
2307
472
    break;
2308
6.16k
  }
2309
58.5k
  case 0x25:
2310
58.5k
  {
2311
58.5k
    if (sz!=8)
2312
1.31k
    {
2313
1.31k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the uint32 zone seems too short\n"));
2314
1.31k
      f << "###";
2315
1.31k
    }
2316
58.5k
    double res;
2317
58.5k
    bool isNaN;
2318
58.5k
    if (!libwps::readDouble4Inv(input, res, isNaN))
2319
19
    {
2320
19
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read a uint32 zone\n"));
2321
19
      f << "###";
2322
19
      break;
2323
19
    }
2324
58.5k
    if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_FORMULA)
2325
58.0k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
2326
58.5k
    cell.m_content.setValue(res);
2327
58.5k
    break;
2328
58.5k
  }
2329
2.79k
  case 0x27:
2330
2.79k
  {
2331
2.79k
    if (sz!=12)
2332
2.48k
    {
2333
2.48k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the double8 zone seems too short\n"));
2334
2.48k
      f << "###";
2335
2.48k
    }
2336
2.79k
    double res;
2337
2.79k
    bool isNaN;
2338
2.79k
    if (!libwps::readDouble8(input, res, isNaN))
2339
1.65k
    {
2340
1.65k
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read a double8 zone\n"));
2341
1.65k
      f << "###";
2342
1.65k
      break;
2343
1.65k
    }
2344
1.14k
    if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_FORMULA)
2345
1.08k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
2346
1.14k
    cell.m_content.setValue(res);
2347
1.14k
    break;
2348
2.79k
  }
2349
36.9k
  case 0x28:
2350
36.9k
  {
2351
36.9k
    if (sz<=12)
2352
726
    {
2353
726
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the double8 formula zone seems too short\n"));
2354
726
      f << "###";
2355
726
    }
2356
36.9k
    double res;
2357
36.9k
    bool isNaN;
2358
36.9k
    if (!libwps::readDouble8(input, res, isNaN))
2359
642
    {
2360
642
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read a double8 formula zone\n"));
2361
642
      f << "###";
2362
642
      break;
2363
642
    }
2364
36.3k
    cell.m_content.m_contentType=WKSContentListener::CellContent::C_FORMULA;
2365
36.3k
    cell.m_content.setValue(res);
2366
36.3k
    ascFile.addDelimiter(input->tell(),'|');
2367
36.3k
    std::string error;
2368
36.3k
    if (!readFormula(*stream, endPos, sheetId, true, cell.m_content.m_formula, error))
2369
17.9k
    {
2370
17.9k
      cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
2371
17.9k
      ascFile.addDelimiter(input->tell()-1, '#');
2372
17.9k
    }
2373
18.4k
    else if (input->tell()+1<endPos)
2374
489
    {
2375
      // often end with another bytes 03, probably for alignement
2376
489
      WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: find extra data for double8 formula zone\n"));
2377
489
      f << "###extra";
2378
489
    }
2379
36.3k
    if (error.length()) f << error;
2380
36.3k
    break;
2381
36.9k
  }
2382
0
  default:
2383
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: oops find unimplemented type\n"));
2384
0
    break;
2385
196k
  }
2386
196k
  std::string extra=f.str();
2387
196k
  f.str("");
2388
196k
  f << "Entries(" << what << "):" << cell << "," << extra;
2389
196k
  if (input->tell()!=endPos)
2390
49.9k
    ascFile.addDelimiter(input->tell(),'|');
2391
196k
  ascFile.addPos(pos);
2392
196k
  ascFile.addNote(f.str().c_str());
2393
2394
196k
  return true;
2395
196k
}
2396
2397
////////////////////////////////////////////////////////////
2398
// filter
2399
////////////////////////////////////////////////////////////
2400
2401
////////////////////////////////////////////////////////////
2402
// report
2403
////////////////////////////////////////////////////////////
2404
2405
////////////////////////////////////////////////////////////
2406
// send data
2407
////////////////////////////////////////////////////////////
2408
void LotusSpreadsheet::sendSpreadsheet(int sheetId)
2409
2.27M
{
2410
2.27M
  if (!m_listener)
2411
0
  {
2412
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::sendSpreadsheet: I can not find the listener\n"));
2413
0
    return;
2414
0
  }
2415
2.27M
  if (sheetId<0||sheetId>=m_state->getNumSheet())
2416
0
  {
2417
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::sendSpreadsheet: the sheet %d seems bad\n", sheetId));
2418
0
    return;
2419
0
  }
2420
2.27M
  auto &sheet = m_state->getSheet(sheetId);
2421
2.27M
  m_listener->openSheet(sheet.getWidths(), getSheetName(sheetId));
2422
2.27M
  m_mainParser.sendGraphics(sheetId);
2423
2.27M
  sheet.compressRowHeights();
2424
  /* create a set to know which row needed to be send, each value of
2425
     the set corresponding to a position where the rows change
2426
     excepted the last position */
2427
2.27M
  std::set<int> newRowSet;
2428
2.27M
  newRowSet.insert(0);
2429
2.27M
  std::map<Vec2i, LotusSpreadsheetInternal::Cell>::const_iterator cIt;
2430
2.27M
  int prevRow=-1;
2431
2.39M
  for (cIt=sheet.m_positionToCellMap.begin(); cIt!=sheet.m_positionToCellMap.end(); ++cIt)
2432
114k
  {
2433
114k
    if (prevRow==cIt->first[1])
2434
78.9k
      continue;
2435
35.1k
    prevRow=cIt->first[1];
2436
35.1k
    newRowSet.insert(prevRow);
2437
35.1k
    newRowSet.insert(prevRow+1);
2438
35.1k
  }
2439
2.27M
  size_t numRowStyle=m_state->m_rowStylesList.size();
2440
2.27M
  for (auto const &rIt : sheet.m_rowToStyleIdMap)
2441
16.0k
  {
2442
16.0k
    Vec2i const &rows=rIt.first;
2443
16.0k
    size_t listId=rIt.second;
2444
16.0k
    if (listId>=numRowStyle)
2445
0
    {
2446
0
      WPS_DEBUG_MSG(("LotusSpreadsheet::sendSpreadsheet: can not find list %d\n", int(listId)));
2447
0
      continue;
2448
0
    }
2449
16.0k
    newRowSet.insert(rows[0]);
2450
16.0k
    newRowSet.insert(rows[1]+1);
2451
16.0k
  }
2452
2.27M
  for (auto const &rIt : sheet.m_rowHeightMap)
2453
51.7k
  {
2454
51.7k
    Vec2i const &rows=rIt.first;
2455
51.7k
    newRowSet.insert(rows[0]);
2456
51.7k
    newRowSet.insert(rows[1]+1);
2457
51.7k
  }
2458
2.27M
  for (auto const &rIt : sheet.m_rowToExtraStyleMap)
2459
3.98k
  {
2460
3.98k
    if (rIt.second.empty()) continue;
2461
3.67k
    newRowSet.insert(rIt.first);
2462
3.67k
    newRowSet.insert(rIt.first+1);
2463
3.67k
  }
2464
2465
2.27M
  LotusSpreadsheetInternal::Table123Styles const *table123Styles=m_state->getTableStyle(sheetId);
2466
2.27M
  if (table123Styles)
2467
435k
  {
2468
435k
    for (auto const &rIt : table123Styles->m_rowsToColsToCellIdMap)
2469
621k
    {
2470
621k
      Vec2i const &rows=rIt.first;
2471
621k
      newRowSet.insert(rows[0]);
2472
621k
      newRowSet.insert(rows[1]+1);
2473
621k
    }
2474
435k
    for (auto const &rIt : table123Styles->m_rowsToColsToExtraStyleMap)
2475
344k
    {
2476
344k
      Vec2i const &rows=rIt.first;
2477
344k
      newRowSet.insert(rows[0]);
2478
344k
      newRowSet.insert(rows[1]+1);
2479
344k
    }
2480
435k
    for (auto const &rIt : table123Styles->m_rowsToColsToFormatStyleMap)
2481
804k
    {
2482
804k
      Vec2i const &rows = rIt.first;
2483
804k
      newRowSet.insert(rows[0]);
2484
804k
      newRowSet.insert(rows[1]+1);
2485
804k
    }
2486
435k
  }
2487
3.61M
  for (auto sIt=newRowSet.begin(); sIt!=newRowSet.end();)
2488
3.61M
  {
2489
3.61M
    int row=*(sIt++);
2490
3.61M
    if (row<0)
2491
248k
    {
2492
248k
      WPS_DEBUG_MSG(("LotusSpreadsheet::sendSpreadsheet: find a negative row %d\n", row));
2493
248k
      continue;
2494
248k
    }
2495
3.36M
    if (sIt==newRowSet.end())
2496
2.27M
      break;
2497
1.08M
    m_listener->openSheetRow(sheet.getRowHeight(row), *sIt-row);
2498
1.08M
    sendRowContent(sheet, row, table123Styles);
2499
1.08M
    m_listener->closeSheetRow();
2500
1.08M
  }
2501
2.27M
  m_listener->closeSheet();
2502
2.27M
}
2503
2504
void LotusSpreadsheet::sendRowContent(LotusSpreadsheetInternal::Spreadsheet const &sheet, int row, LotusSpreadsheetInternal::Table123Styles const *table123Styles)
2505
1.08M
{
2506
1.08M
  if (m_listener.get() == nullptr)
2507
0
  {
2508
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::sendRowContent: I can not find the listener\n"));
2509
0
    return;
2510
0
  }
2511
2512
  // create a set of column we need to generate
2513
1.08M
  std::set<int> newColSet;
2514
1.08M
  newColSet.insert(0);
2515
2516
1.08M
  auto cIt=sheet.m_positionToCellMap.lower_bound(Vec2i(-1, row));
2517
1.08M
  bool checkCell=cIt!=sheet.m_positionToCellMap.end() && cIt->first[1]==row;
2518
1.08M
  if (checkCell)
2519
35.1k
  {
2520
149k
    for (; cIt!=sheet.m_positionToCellMap.end() && cIt->first[1]==row ; ++cIt)
2521
114k
    {
2522
114k
      newColSet.insert(cIt->first[0]);
2523
114k
      newColSet.insert(cIt->first[0]+1);
2524
114k
    }
2525
35.1k
    cIt=sheet.m_positionToCellMap.lower_bound(Vec2i(-1, row));
2526
35.1k
  }
2527
2528
1.08M
  LotusSpreadsheetInternal::RowStyles const *styles=nullptr;
2529
1.08M
  int styleId=sheet.getRowStyleId(row);
2530
1.08M
  if (styleId>=int(m_state->m_rowStylesList.size()))
2531
0
  {
2532
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::sendRowContent: I can not row style %d\n", styleId));
2533
0
  }
2534
1.08M
  else if (styleId>=0)
2535
16.4k
    styles=&m_state->m_rowStylesList[size_t(styleId)];
2536
1.08M
  std::map<Vec2i, LotusSpreadsheetInternal::Style>::const_iterator sIt;
2537
1.08M
  if (styles)
2538
16.4k
  {
2539
353k
    for (sIt=styles->m_colsToStyleMap.begin(); sIt!=styles->m_colsToStyleMap.end(); ++sIt)
2540
337k
    {
2541
337k
      newColSet.insert(sIt->first[0]);
2542
337k
      newColSet.insert(sIt->first[1]+1);
2543
337k
    }
2544
16.4k
    sIt=styles->m_colsToStyleMap.begin();
2545
16.4k
    if (sIt==styles->m_colsToStyleMap.end())
2546
926
      styles=nullptr;
2547
16.4k
  }
2548
2549
1.08M
  LotusSpreadsheetInternal::ExtraRowStyles const *extraStyles=nullptr;
2550
1.08M
  if (sheet.m_rowToExtraStyleMap.find(row)!=sheet.m_rowToExtraStyleMap.end())
2551
3.97k
    extraStyles=&sheet.m_rowToExtraStyleMap.find(row)->second;
2552
1.08M
  std::map<Vec2i, LotusSpreadsheetInternal::ExtraStyle>::const_iterator eIt;
2553
1.08M
  if (extraStyles)
2554
3.97k
  {
2555
208k
    for (eIt=extraStyles->m_colsToStyleMap.begin(); eIt!=extraStyles->m_colsToStyleMap.end(); ++eIt)
2556
204k
    {
2557
204k
      if (eIt->second.empty()) continue;
2558
120k
      newColSet.insert(eIt->first[0]);
2559
120k
      newColSet.insert(eIt->first[1]+1);
2560
120k
    }
2561
3.97k
    eIt=extraStyles->m_colsToStyleMap.begin();
2562
3.97k
    if (eIt==extraStyles->m_colsToStyleMap.end())
2563
0
      extraStyles=nullptr;
2564
3.97k
  }
2565
2566
1.08M
  auto const defFontType=m_mainParser.getDefaultFontType();
2567
1.08M
  LotusSpreadsheetInternal::Style defaultStyle(defFontType);
2568
2569
1.08M
  std::map<Vec2i, LotusSpreadsheetInternal::Style> colToCellIdMap;
2570
1.08M
  std::map<Vec2i, LotusSpreadsheetInternal::Style>::const_iterator c123It;
2571
1.08M
  std::map<Vec2i, LotusSpreadsheetInternal::Extra123Style> colToExtraStyleMap;
2572
1.08M
  std::map<Vec2i, LotusSpreadsheetInternal::Extra123Style>::const_iterator e123It;
2573
1.08M
  std::map<Vec2i, LotusSpreadsheetInternal::Format123Style> colToFormatStyleMap;
2574
1.08M
  std::map<Vec2i, LotusSpreadsheetInternal::Format123Style>::const_iterator f123It;
2575
2576
1.08M
  std::map<int,int> potentialMergeMap;
2577
1.08M
  if (table123Styles)
2578
989k
  {
2579
989k
    for (auto const &rIt : table123Styles->m_rowsToColsToCellIdMap)
2580
9.63M
    {
2581
9.63M
      if (rIt.first[0]>row || rIt.first[1]<row) continue;
2582
715k
      for (auto const &colIt : rIt.second)
2583
1.19M
      {
2584
1.19M
        LotusSpreadsheetInternal::Style style(defFontType);
2585
1.19M
        WPSFont font;
2586
1.19M
        if (!m_styleManager->updateCellStyle(colIt.second, style, font, style.m_fontType))
2587
577k
          continue;
2588
615k
        style.setFont(font);
2589
615k
        colToCellIdMap.insert(std::map<Vec2i, LotusSpreadsheetInternal::Style>::value_type(colIt.first,style));
2590
615k
        newColSet.insert(colIt.first[0]);
2591
615k
        newColSet.insert(colIt.first[1]+1);
2592
615k
      }
2593
715k
    }
2594
989k
    for (auto const &rIt : table123Styles->m_rowsToColsToExtraStyleMap)
2595
8.37M
    {
2596
8.37M
      if (rIt.first[0]>row || rIt.first[1]<row) continue;
2597
410k
      for (auto const &colIt : rIt.second)
2598
454k
      {
2599
454k
        colToExtraStyleMap.insert(std::map<Vec2i, LotusSpreadsheetInternal::Extra123Style>::value_type(colIt.first,colIt.second));
2600
454k
        newColSet.insert(colIt.first[0]);
2601
454k
        newColSet.insert(colIt.first[1]+1);
2602
454k
      }
2603
410k
    }
2604
989k
    for (auto const &rIt : table123Styles->m_rowsToColsToFormatStyleMap)
2605
13.2M
    {
2606
13.2M
      if (rIt.first[0]>row || rIt.first[1]<row) continue;
2607
895k
      for (auto const &colIt : rIt.second)
2608
1.39M
      {
2609
1.39M
        colToFormatStyleMap.insert(std::map<Vec2i, LotusSpreadsheetInternal::Format123Style>::value_type(colIt.first,colIt.second));
2610
1.39M
        newColSet.insert(colIt.first[0]);
2611
1.39M
        newColSet.insert(colIt.first[1]+1);
2612
1.39M
        if (colIt.first[0]!=colIt.first[1] && colIt.second.m_alignAcrossColumn)
2613
755k
          potentialMergeMap[colIt.first[0]]=colIt.first[1]+1;
2614
1.39M
      }
2615
895k
    }
2616
989k
    c123It=colToCellIdMap.begin();
2617
989k
    e123It=colToExtraStyleMap.begin();
2618
989k
    f123It=colToFormatStyleMap.begin();
2619
989k
    if (table123Styles->m_defaultCellId>=0)
2620
5.57k
    {
2621
5.57k
      WPSFont font;
2622
5.57k
      if (m_styleManager->updateCellStyle(table123Styles->m_defaultCellId, defaultStyle, font, defaultStyle.m_fontType))
2623
4.06k
        defaultStyle.setFont(font);
2624
5.57k
    }
2625
989k
    if (colToCellIdMap.empty() && colToExtraStyleMap.empty() && colToFormatStyleMap.empty())
2626
150k
      table123Styles=nullptr;
2627
989k
  }
2628
2629
1.08M
  LotusSpreadsheetInternal::Cell emptyCell;
2630
3.70M
  for (auto colIt=newColSet.begin(); colIt!=newColSet.end();)
2631
3.70M
  {
2632
3.70M
    int const col=*colIt;
2633
3.70M
    ++colIt;
2634
3.70M
    if (colIt==newColSet.end())
2635
1.08M
      break;
2636
2.61M
    int const endCol=*colIt;
2637
2.61M
    if (styles)
2638
340k
    {
2639
652k
      while (sIt->first[1] < col)
2640
313k
      {
2641
313k
        ++sIt;
2642
313k
        if (sIt==styles->m_colsToStyleMap.end())
2643
588
        {
2644
588
          styles=nullptr;
2645
588
          break;
2646
588
        }
2647
313k
      }
2648
340k
    }
2649
2.61M
    if (extraStyles)
2650
153k
    {
2651
351k
      while (eIt->first[1] < col)
2652
198k
      {
2653
198k
        ++eIt;
2654
198k
        if (eIt==extraStyles->m_colsToStyleMap.end())
2655
664
        {
2656
664
          extraStyles=nullptr;
2657
664
          break;
2658
664
        }
2659
198k
      }
2660
153k
    }
2661
2.61M
    auto style=styles ? sIt->second : defaultStyle;
2662
2.61M
    bool hasStyle=styles!=nullptr;
2663
2.61M
    if (table123Styles)
2664
1.92M
    {
2665
2.30M
      while (c123It!=colToCellIdMap.end() && c123It->first[1] < col)
2666
379k
        ++c123It;
2667
1.92M
      if (c123It!=colToCellIdMap.end() && c123It->first[0] <= col && c123It->first[1] >= col)
2668
248k
      {
2669
248k
        style=c123It->second;
2670
248k
        hasStyle=true;
2671
248k
      }
2672
2673
2.05M
      while (e123It!=colToExtraStyleMap.end() && e123It->first[1] < col)
2674
130k
        ++e123It;
2675
1.92M
      if (e123It!=colToExtraStyleMap.end() && e123It->first[0] <= col && e123It->first[1] >= col)
2676
245k
      {
2677
245k
        e123It->second.update(style);
2678
245k
        hasStyle=true;
2679
245k
      }
2680
2.68M
      while (f123It!=colToFormatStyleMap.end() && f123It->first[1] < col)
2681
758k
        ++f123It;
2682
1.92M
      if (f123It!=colToFormatStyleMap.end() && f123It->first[0] <= col && f123It->first[1] >= col)
2683
656k
      {
2684
656k
        f123It->second.update(style);
2685
656k
        hasStyle=true;
2686
656k
      }
2687
1.92M
      if (c123It==colToCellIdMap.end() && e123It==colToExtraStyleMap.end() && f123It==colToFormatStyleMap.end())
2688
59.9k
        table123Styles=nullptr;
2689
1.92M
    }
2690
2691
2.61M
    if (extraStyles && !eIt->second.empty())
2692
130k
    {
2693
130k
      eIt->second.update(style);
2694
130k
      hasStyle=true;
2695
130k
    }
2696
2.61M
    bool hasCell=false;
2697
2.61M
    if (checkCell)
2698
152k
    {
2699
234k
      while (cIt->first[1]==row && cIt->first[0]<col)
2700
82.8k
      {
2701
82.8k
        ++cIt;
2702
82.8k
        if (cIt==sheet.m_positionToCellMap.end())
2703
957
        {
2704
957
          checkCell=false;
2705
957
          break;
2706
957
        }
2707
82.8k
      }
2708
152k
      if (checkCell && cIt->first[1]!=row) checkCell=false;
2709
152k
      hasCell=checkCell && cIt->first[0]==col;
2710
152k
    }
2711
2.61M
    if (!hasCell && !hasStyle) continue;
2712
1.46M
    if (!hasCell) emptyCell.setPosition(Vec2i(col, row));
2713
1.46M
    bool canMerge=false;
2714
1.46M
    if (hasCell && potentialMergeMap.find(col)!=potentialMergeMap.end())
2715
1.82k
    {
2716
1.82k
      int newEndCol=potentialMergeMap.find(col)->second;
2717
1.82k
      auto newCIt=cIt;
2718
1.82k
      if (newCIt!=sheet.m_positionToCellMap.end()) ++newCIt;
2719
1.82k
      canMerge=(newCIt==sheet.m_positionToCellMap.end() || newCIt->first[1]!=row || newCIt->first[0]>=newEndCol);
2720
1.82k
    }
2721
1.46M
    if (canMerge)
2722
1.28k
    {
2723
1.28k
      int newEndCol=potentialMergeMap.find(col)->second;
2724
11.8k
      while (colIt!=newColSet.end() && *colIt<newEndCol)
2725
10.5k
        ++colIt;
2726
1.28k
      auto cell=cIt->second;
2727
1.28k
      cell.setNumSpannedCells(Vec2i(newEndCol-col,1));
2728
1.28k
      sendCellContent(cell, style, 1);
2729
1.28k
    }
2730
1.46M
    else
2731
1.46M
      sendCellContent(hasCell ? cIt->second : emptyCell, style, endCol-col);
2732
1.46M
  }
2733
1.08M
}
2734
2735
void LotusSpreadsheet::sendCellContent(LotusSpreadsheetInternal::Cell const &cell,
2736
                                       LotusSpreadsheetInternal::Style const &style,
2737
                                       int numRepeated)
2738
1.46M
{
2739
1.46M
  if (m_listener.get() == nullptr)
2740
0
  {
2741
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::sendCellContent: I can not find the listener\n"));
2742
0
    return;
2743
0
  }
2744
2745
1.46M
  LotusSpreadsheetInternal::Style cellStyle(style);
2746
1.46M
  if (cell.m_hAlignement!=WPSCellFormat::HALIGN_DEFAULT)
2747
12.7k
    cellStyle.setHAlignment(cell.m_hAlignement);
2748
2749
1.46M
  auto fontType = cellStyle.m_fontType;
2750
2751
1.46M
  m_listener->setFont(cellStyle.getFont());
2752
2753
1.46M
  LotusSpreadsheetInternal::Cell finalCell(cell);
2754
1.46M
  finalCell.WPSCellFormat::operator=(cellStyle);
2755
1.46M
  WKSContentListener::CellContent content(cell.m_content);
2756
1.46M
  for (auto &f : content.m_formula)
2757
137k
  {
2758
137k
    if (f.m_type!=WKSContentListener::FormulaInstruction::F_Text)
2759
133k
      continue;
2760
3.69k
    std::string &text=f.m_content;
2761
3.69k
    librevenge::RVNGString finalString=libwps_tools_win::Font::unicodeString(text, fontType);
2762
3.69k
    text=finalString.cstr();
2763
3.69k
  }
2764
1.46M
  m_listener->openSheetCell(finalCell, content, numRepeated);
2765
2766
1.46M
  if (cell.m_input && cell.m_content.m_textEntry.valid())
2767
37.8k
  {
2768
37.8k
    RVNGInputStreamPtr input=cell.m_input;
2769
37.8k
    input->seek(cell.m_content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
2770
37.8k
    sendText(input, cell.m_content.m_textEntry.end(), cellStyle);
2771
37.8k
  }
2772
1.46M
  if (cell.m_comment.valid())
2773
2.23k
  {
2774
2.23k
    WPSSubDocumentPtr subdoc(new LotusSpreadsheetInternal::SubDocument
2775
2.23k
                             (cell.m_input, *this, cell.m_comment));
2776
2.23k
    m_listener->insertComment(subdoc);
2777
2.23k
  }
2778
1.46M
  m_listener->closeSheetCell();
2779
1.46M
}
2780
2781
////////////////////////////////////////////////////////////
2782
// formula
2783
////////////////////////////////////////////////////////////
2784
bool LotusSpreadsheet::parseVariable(std::string const &variable, WKSContentListener::FormulaInstruction &instr)
2785
35.3k
{
2786
35.3k
  std::regex exp("<<([^>]+)>>([^:]+):([A-Z]+)([0-9]+)");
2787
35.3k
  std::regex exp2("<<([^>]+)>>([^:]+):([A-Z]+)([0-9]+)\\.\\.([^:]+):([A-Z]+)([0-9]+)");
2788
35.3k
  std::smatch base_match;
2789
35.3k
  size_t dim=0;
2790
35.3k
  if (std::regex_match(variable,base_match,exp) && base_match.size() == 5)
2791
1.03k
    dim=1;
2792
34.3k
  else if (std::regex_match(variable,base_match,exp2) && base_match.size() == 8)
2793
853
  {
2794
853
    if (base_match[2].str()!=base_match[5].str())
2795
852
      instr.m_sheetName[1]=base_match[5].str().c_str();
2796
853
    dim=2;
2797
853
  }
2798
33.4k
  else // can also be variable, <<File>>variable, db.field ?
2799
33.4k
    return false;
2800
1.88k
  instr.m_fileName=base_match[1].str().c_str();
2801
1.88k
  instr.m_sheetName[0]=base_match[2].str().c_str();
2802
1.88k
  instr.m_type=dim==1 ? WKSContentListener::FormulaInstruction::F_Cell : WKSContentListener::FormulaInstruction::F_CellList;
2803
4.26k
  for (size_t d=0; d<dim; ++d)
2804
2.72k
  {
2805
2.72k
    int col=0;
2806
2.72k
    std::string sCol=base_match[3+3*d].str();
2807
2.72k
    for (auto c:sCol)
2808
7.32k
    {
2809
7.32k
      if (col > (std::numeric_limits<int>::max() - int(c-'A')) / 26)
2810
195
      {
2811
195
        WPS_DEBUG_MSG(("LotusSpreadsheet::parseVariable: oops the column seems bad\n"));
2812
195
        return false;
2813
195
      }
2814
7.12k
      col=26*col+int(c-'A');
2815
7.12k
    }
2816
2.53k
    int row=0;
2817
2.53k
    std::string sRow=base_match[4+3*d].str();
2818
2.53k
    for (auto c:sRow)
2819
7.99k
    {
2820
7.99k
      if (row > (std::numeric_limits<int>::max() - int(c-'0')) / 10)
2821
150
      {
2822
150
        WPS_DEBUG_MSG(("LotusSpreadsheet::parseVariable: oops the row seems bad\n"));
2823
150
        return false;
2824
150
      }
2825
7.84k
      row=10*row+int(c-'0');
2826
7.84k
    }
2827
2.38k
    instr.m_position[d]=Vec2i(col,row-1);
2828
2.38k
    instr.m_positionRelative[d]=Vec2b(true,true);
2829
2.38k
  }
2830
1.54k
  return true;
2831
1.88k
}
2832
2833
bool LotusSpreadsheet::readCell(WPSStream &stream, int sId, bool isList, WKSContentListener::FormulaInstruction &instr)
2834
46.5k
{
2835
46.5k
  RVNGInputStreamPtr &input=stream.m_input;
2836
46.5k
  instr=WKSContentListener::FormulaInstruction();
2837
46.5k
  instr.m_type=isList ? WKSContentListener::FormulaInstruction::F_CellList :
2838
46.5k
               WKSContentListener::FormulaInstruction::F_Cell;
2839
46.5k
  auto flags=int(libwps::readU8(input));
2840
65.5k
  for (int i=0; i<2; ++i)
2841
56.0k
  {
2842
56.0k
    auto row=int(libwps::readU16(input));
2843
56.0k
    auto sheetId=int(libwps::readU8(input));
2844
56.0k
    auto col=int(libwps::readU8(input));
2845
56.0k
    instr.m_position[i]=Vec2i(col, row);
2846
56.0k
    int wh=(i==0) ? (flags&0xF) : (flags>>4);
2847
56.0k
    instr.m_positionRelative[i]=Vec2b((wh&1)!=0, (wh&2)!=0);
2848
56.0k
    if (sheetId!=sId)
2849
32.4k
      instr.m_sheetName[i]=getSheetName(sheetId);
2850
56.0k
    if (!isList) break;
2851
56.0k
  }
2852
46.5k
  return true;
2853
46.5k
}
2854
2855
namespace LotusSpreadsheetInternal
2856
{
2857
struct Functions
2858
{
2859
  char const *m_name;
2860
  int m_arity;
2861
};
2862
2863
static Functions const s_listFunctions[] =
2864
{
2865
  { "", 0} /*SPEC: number*/, {"", 0}/*SPEC: cell*/, {"", 0}/*SPEC: cells*/, {"=", 1} /*SPEC: end of formula*/,
2866
  { "(", 1} /* SPEC: () */, {"", 0}/*SPEC: number*/, { "", 0} /*SPEC: text*/, {"", 0}/*name reference*/,
2867
  { "", 0}/* SPEC: abs name ref*/, {"", 0}/* SPEC: err range ref*/, { "", 0}/* SPEC: err cell ref*/, {"", 0}/* SPEC: err constant*/,
2868
  { "", -2} /* unused*/, { "", -2} /*unused*/, {"-", 1}, {"+", 2},
2869
2870
  { "-", 2},{ "*", 2},{ "/", 2},{ "^", 2},
2871
  { "=", 2},{ "<>", 2},{ "<=", 2},{ ">=", 2},
2872
  { "<", 2},{ ">", 2},{ "And", 2},{ "Or", 2},
2873
  { "Not", 1}, { "+", 1}, { "&", 2}, { "NA", 0} /* not applicable*/,
2874
2875
  { "NA", 0} /* Error*/,{ "Abs", 1},{ "Int", 1},{ "Sqrt", 1},
2876
  { "Log10", 1},{ "Ln", 1},{ "Pi", 0},{ "Sin", 1},
2877
  { "Cos", 1},{ "Tan", 1},{ "Atan2", 2},{ "Atan", 1},
2878
  { "Asin", 1},{ "Acos", 1},{ "Exp", 1},{ "Mod", 2},
2879
2880
  { "Choose", -1},{ "IsNa", 1},{ "IsError", 1},{ "False", 0},
2881
  { "True", 0},{ "Rand", 0},{ "Date", 3},{ "Now", 0},
2882
  { "PMT", 3} /*BAD*/,{ "PV", 3} /*BAD*/,{ "FV", 3} /*BAD*/,{ "IF", 3},
2883
  { "Day", 1},{ "Month", 1},{ "Year", 1},{ "Round", 2},
2884
2885
  { "Time", 3},{ "Hour", 1},{ "Minute", 1},{ "Second", 1},
2886
  { "IsNumber", 1},{ "IsText", 1},{ "Len", 1},{ "Value", 1},
2887
  { "Text", 2}/* or fixed*/, { "Mid", 3}, { "Char", 1},{ "Ascii", 1},
2888
  { "Find", 3},{ "DateValue", 1} /*checkme*/,{ "TimeValue", 1} /*checkme*/,{ "CellPointer", 1} /*checkme*/,
2889
2890
  { "Sum", -1},{ "Average", -1},{ "COUNT", -1},{ "Min", -1},
2891
  { "Max", -1},{ "VLookUp", 3},{ "NPV", 2}, { "Var", -1},
2892
  { "StDev", -1},{ "IRR", 2} /*BAD*/, { "HLookup", 3},{ "DSum", 3},
2893
  { "DAvg", 3},{ "DCount", 3},{ "DMin", 3},{ "DMax", 3},
2894
2895
  { "DVar", 3},{ "DStd", 3},{ "Index", 3}, { "Columns", 1},
2896
  { "Rows", 1},{ "Rept", 2},{ "Upper", 1},{ "Lower", 1},
2897
  { "Left", 2},{ "Right", 2},{ "Replace", 4}, { "Proper", 1},
2898
  { "Cell", 2} /*checkme*/,{ "Trim", 1},{ "Clean", 1} /*UNKN*/,{ "T", 1},
2899
2900
  { "IsNonText", 1},{ "Exact", 2},{ "", -2} /*App not implemented*/,{ "", 3} /*UNKN*/,
2901
  { "Rate", 3} /*BAD*/,{ "TERM", 3}, { "CTERM", 3}, { "SLN", 3},
2902
  { "SYD", 4},{ "DDB", 4},{ "SplFunc", -1} /*SplFunc*/,{ "Sheets", 1},
2903
  { "Info", 1},{ "SumProduct", -1},{ "IsRange", 1},{ "DGet", -1},
2904
2905
  { "DQuery", -1},{ "Coord", 4}, { "", -2} /*reserved*/, { "Today", 0},
2906
  { "Vdb", -1},{ "Dvars", -1},{ "Dstds", -1},{ "Vars", -1},
2907
  { "Stds", -1},{ "D360", 2},{ "", -2} /*reserved*/,{ "IsApp", 0},
2908
  { "IsAaf", -1},{ "Weekday", 1},{ "DateDiff", 3},{ "Rank", -1},
2909
2910
  { "NumberString", 2},{ "DateString", 1}, { "Decimal", 1}, { "Hex", 1},
2911
  { "Db", 4},{ "PMTI", 4},{ "SPI", 4},{ "Fullp", 1},
2912
  { "Halfp", 1},{ "PureAVG", -1},{ "PureCount", -1},{ "PureMax", -1},
2913
  { "PureMin", -1},{ "PureSTD", -1},{ "PureVar", -1},{ "PureSTDS", -1},
2914
2915
  { "PureVars", -1},{ "PMT2", 3}, { "PV2", 3}, { "FV2", 3},
2916
  { "TERM2", 3},{ "", -2} /*UNKN*/,{ "D360", 2},{ "", -2} /*UNKN*/,
2917
  { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/, { "", -2} /*UNKN*/,
2918
  { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
2919
};
2920
}
2921
2922
bool LotusSpreadsheet::readFormula(WPSStream &stream, long endPos, int sheetId, bool newFormula,
2923
                                   std::vector<WKSContentListener::FormulaInstruction> &formula, std::string &error)
2924
50.0k
{
2925
50.0k
  int const vers=version();
2926
50.0k
  RVNGInputStreamPtr &input=stream.m_input;
2927
50.0k
  formula.resize(0);
2928
50.0k
  error = "";
2929
50.0k
  long pos = input->tell();
2930
50.0k
  if (endPos - pos < 1) return false;
2931
2932
49.4k
  std::stringstream f;
2933
49.4k
  std::vector<std::vector<WKSContentListener::FormulaInstruction> > stack;
2934
49.4k
  bool ok = true;
2935
345k
  while (long(input->tell()) != endPos)
2936
344k
  {
2937
344k
    double val = 0.0;
2938
344k
    bool isNaN;
2939
344k
    pos = input->tell();
2940
344k
    if (pos > endPos) return false;
2941
343k
    auto wh = int(libwps::readU8(input));
2942
343k
    int arity = 0;
2943
343k
    WKSContentListener::FormulaInstruction instr;
2944
343k
    switch (wh)
2945
343k
    {
2946
30.5k
    case 0x0:
2947
30.5k
      if ((!newFormula && (endPos-pos<11 || !libwps::readDouble10(input, val, isNaN))) ||
2948
28.9k
              (newFormula && (endPos-pos<9 || !libwps::readDouble8(input, val, isNaN))))
2949
7.38k
      {
2950
7.38k
        f.str("");
2951
7.38k
        f << "###number";
2952
7.38k
        error=f.str();
2953
7.38k
        ok = false;
2954
7.38k
        break;
2955
7.38k
      }
2956
23.1k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Double;
2957
23.1k
      instr.m_doubleValue=val;
2958
23.1k
      break;
2959
38.0k
    case 0x1:
2960
38.0k
    {
2961
38.0k
      if (endPos-pos<6 || !readCell(stream, sheetId, false, instr))
2962
949
      {
2963
949
        f.str("");
2964
949
        f << "###cell short";
2965
949
        error=f.str();
2966
949
        ok = false;
2967
949
        break;
2968
949
      }
2969
37.0k
      break;
2970
38.0k
    }
2971
37.0k
    case 0x2:
2972
9.82k
    {
2973
9.82k
      if (endPos-pos<10 || !readCell(stream, sheetId, true, instr))
2974
331
      {
2975
331
        f.str("");
2976
331
        f << "###list cell short";
2977
331
        error=f.str();
2978
331
        ok = false;
2979
331
        break;
2980
331
      }
2981
9.48k
      break;
2982
9.82k
    }
2983
25.3k
    case 0x5:
2984
25.3k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Double;
2985
25.3k
      if ((!newFormula && (endPos-pos<3 || !libwps::readDouble2Inv(input, val, isNaN))) ||
2986
25.2k
              (newFormula && (endPos-pos<5 || !libwps::readDouble4Inv(input, val, isNaN))))
2987
446
      {
2988
446
        WPS_DEBUG_MSG(("LotusSpreadsheet::readFormula: can read a uint16/32 zone\n"));
2989
446
        f << "###uint16/32";
2990
446
        error=f.str();
2991
446
        break;
2992
446
      }
2993
24.9k
      instr.m_doubleValue=val;
2994
24.9k
      break;
2995
5.47k
    case 0x6:
2996
5.47k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Text;
2997
25.4k
      while (!input->isEnd())
2998
25.4k
      {
2999
25.4k
        if (input->tell() >= endPos)
3000
78
        {
3001
78
          ok=false;
3002
78
          break;
3003
78
        }
3004
25.3k
        auto c = char(libwps::readU8(input));
3005
25.3k
        if (c==0) break;
3006
19.9k
        instr.m_content += c;
3007
19.9k
      }
3008
5.47k
      break;
3009
22.0k
    case 0x7:
3010
45.7k
    case 0x8:
3011
45.7k
    {
3012
45.7k
      std::string variable("");
3013
280k
      while (!input->isEnd())
3014
280k
      {
3015
280k
        if (input->tell() >= endPos)
3016
232
        {
3017
232
          ok=false;
3018
232
          break;
3019
232
        }
3020
280k
        auto c = char(libwps::readU8(input));
3021
280k
        if (c==0) break;
3022
234k
        variable += c;
3023
234k
      }
3024
45.7k
      if (!ok)
3025
232
        break;
3026
45.5k
      if (m_state->m_nameToCellsMap.find(variable)==m_state->m_nameToCellsMap.end())
3027
35.3k
      {
3028
35.3k
        if (parseVariable(variable, instr)) break;
3029
        // can also be a database field, ...
3030
33.8k
        WPS_DEBUG_MSG(("LotusSpreadsheet::readFormula: can not find variable %s\n", variable.c_str()));
3031
33.8k
        f << "##variable=" << variable << ",";
3032
33.8k
        error=f.str();
3033
33.8k
        instr.m_type=WKSContentListener::FormulaInstruction::F_Text;
3034
33.8k
        instr.m_content = variable;
3035
33.8k
        break;
3036
35.3k
      }
3037
10.2k
      auto const &cells=m_state->m_nameToCellsMap.find(variable)->second;
3038
10.2k
      instr.m_position[0]=cells.m_positions[0];
3039
10.2k
      instr.m_position[1]=cells.m_positions[1];
3040
10.2k
      instr.m_positionRelative[0]=instr.m_positionRelative[1]=Vec2b(wh==7,wh==7);
3041
30.6k
      for (int i=0; i<2; ++i)
3042
20.4k
      {
3043
20.4k
        if (cells.m_ids[i] != sheetId)
3044
17.3k
          instr.m_sheetName[i]=getSheetName(cells.m_ids[i]);
3045
20.4k
      }
3046
10.2k
      instr.m_type=cells.m_positions[0]==cells.m_positions[1] ?
3047
1.47k
                   WKSContentListener::FormulaInstruction::F_Cell :
3048
10.2k
                   WKSContentListener::FormulaInstruction::F_CellList;
3049
10.2k
      break;
3050
45.5k
    }
3051
188k
    default:
3052
188k
      if (wh >= 0xb0 || LotusSpreadsheetInternal::s_listFunctions[wh].m_arity == -2)
3053
6.72k
      {
3054
6.72k
        f.str("");
3055
6.72k
        f << "##Funct" << std::hex << wh;
3056
6.72k
        error=f.str();
3057
6.72k
        ok = false;
3058
6.72k
        break;
3059
6.72k
      }
3060
182k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
3061
182k
      instr.m_content=LotusSpreadsheetInternal::s_listFunctions[wh].m_name;
3062
182k
      ok=!instr.m_content.empty();
3063
182k
      arity = LotusSpreadsheetInternal::s_listFunctions[wh].m_arity;
3064
182k
      if (arity == -1)
3065
32.8k
        arity = int(libwps::read8(input));
3066
182k
      if (wh==0x7a)   // special Spell function
3067
963
      {
3068
963
        auto sSz=int(libwps::readU16(input));
3069
963
        if (input->tell()+sSz>endPos || (vers>=3 && sSz<2))
3070
306
        {
3071
306
          WPS_DEBUG_MSG(("LotusSpreadsheet::readFormula: can not find spell function length\n"));
3072
306
          f << "###spell[length]=" << sSz << ",";
3073
306
          error = f.str();
3074
306
          ok=false;
3075
306
        }
3076
963
        if (vers>=3)   // 1801
3077
770
        {
3078
770
          f << "f0=" << std::dec << libwps::readU16(input) << std::dec << ",";
3079
770
          sSz-=2;
3080
770
        }
3081
963
        WKSContentListener::FormulaInstruction lastArg;
3082
963
        lastArg.m_type=WKSContentListener::FormulaInstruction::F_Text;
3083
74.5k
        for (int i=0; i<sSz; ++i)
3084
74.3k
        {
3085
74.3k
          auto c = char(libwps::readU8(input));
3086
74.3k
          if (c==0) break;
3087
73.6k
          lastArg.m_content += c;
3088
73.6k
        }
3089
963
        std::vector<WKSContentListener::FormulaInstruction> child;
3090
963
        child.push_back(lastArg);
3091
963
        stack.push_back(child);
3092
963
        ++arity;
3093
963
        break;
3094
963
      }
3095
181k
      break;
3096
343k
    }
3097
3098
343k
    if (!ok) break;
3099
327k
    std::vector<WKSContentListener::FormulaInstruction> child;
3100
327k
    if (instr.m_type!=WKSContentListener::FormulaInstruction::F_Function)
3101
145k
    {
3102
145k
      child.push_back(instr);
3103
145k
      stack.push_back(child);
3104
145k
      continue;
3105
145k
    }
3106
181k
    size_t numElt = stack.size();
3107
181k
    if (int(numElt) < arity)
3108
5.70k
    {
3109
5.70k
      f.str("");
3110
5.70k
      f << instr.m_content << "[##" << arity << "]";
3111
5.70k
      error=f.str();
3112
5.70k
      ok = false;
3113
5.70k
      break;
3114
5.70k
    }
3115
    //
3116
    // first treat the special cases
3117
    //
3118
175k
    if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && instr.m_content=="TERM")
3119
3.77k
    {
3120
      // @TERM(pmt,pint,fv) -> NPER(pint,-pmt,pv=0,fv)
3121
3.77k
      auto pmt=stack[size_t(int(numElt)-3)];
3122
3.77k
      auto pint=stack[size_t(int(numElt)-2)];
3123
3.77k
      auto fv=stack[size_t(int(numElt)-1)];
3124
3125
3.77k
      stack.resize(size_t(++numElt));
3126
      // pint
3127
3.77k
      stack[size_t(int(numElt)-4)]=pint;
3128
      //-pmt
3129
3.77k
      auto &node=stack[size_t(int(numElt)-3)];
3130
3.77k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
3131
3.77k
      instr.m_content="-";
3132
3.77k
      node.resize(0);
3133
3.77k
      node.push_back(instr);
3134
3.77k
      instr.m_content="(";
3135
3.77k
      node.push_back(instr);
3136
3.77k
      node.insert(node.end(), pmt.begin(), pmt.end());
3137
3.77k
      instr.m_content=")";
3138
3.77k
      node.push_back(instr);
3139
      //pv=zero
3140
3.77k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
3141
3.77k
      instr.m_longValue=0;
3142
3.77k
      stack[size_t(int(numElt)-2)].resize(0);
3143
3.77k
      stack[size_t(int(numElt)-2)].push_back(instr);
3144
      //fv
3145
3.77k
      stack[size_t(int(numElt)-1)]=fv;
3146
3.77k
      arity=4;
3147
3.77k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
3148
3.77k
      instr.m_content="NPER";
3149
3.77k
    }
3150
172k
    else if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && instr.m_content=="CTERM")
3151
4.47k
    {
3152
      // @CTERM(pint,fv,pv) -> NPER(pint,pmt=0,-pv,fv)
3153
4.47k
      auto pint=stack[size_t(int(numElt)-3)];
3154
4.47k
      auto fv=stack[size_t(int(numElt)-2)];
3155
4.47k
      auto pv=stack[size_t(int(numElt)-1)];
3156
4.47k
      stack.resize(size_t(++numElt));
3157
      // pint
3158
4.47k
      stack[size_t(int(numElt)-4)]=pint;
3159
      // pmt=0
3160
4.47k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
3161
4.47k
      instr.m_longValue=0;
3162
4.47k
      stack[size_t(int(numElt)-3)].resize(0);
3163
4.47k
      stack[size_t(int(numElt)-3)].push_back(instr);
3164
      // -pv
3165
4.47k
      auto &node=stack[size_t(int(numElt)-2)];
3166
4.47k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
3167
4.47k
      instr.m_content="-";
3168
4.47k
      node.resize(0);
3169
4.47k
      node.push_back(instr);
3170
4.47k
      instr.m_content="(";
3171
4.47k
      node.push_back(instr);
3172
4.47k
      node.insert(node.end(), pv.begin(), pv.end());
3173
4.47k
      instr.m_content=")";
3174
4.47k
      node.push_back(instr);
3175
3176
      //fv
3177
4.47k
      stack[size_t(int(numElt)-1)]=fv;
3178
4.47k
      arity=4;
3179
4.47k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
3180
4.47k
      instr.m_content="NPER";
3181
4.47k
    }
3182
3183
175k
    if ((instr.m_content[0] >= 'A' && instr.m_content[0] <= 'Z') || instr.m_content[0] == '(')
3184
113k
    {
3185
113k
      if (instr.m_content[0] != '(')
3186
111k
        child.push_back(instr);
3187
3188
113k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
3189
113k
      instr.m_content="(";
3190
113k
      child.push_back(instr);
3191
690k
      for (int i = 0; i < arity; i++)
3192
577k
      {
3193
577k
        if (i)
3194
497k
        {
3195
497k
          instr.m_content=";";
3196
497k
          child.push_back(instr);
3197
497k
        }
3198
577k
        auto const &node=stack[size_t(int(numElt)-arity+i)];
3199
577k
        child.insert(child.end(), node.begin(), node.end());
3200
577k
      }
3201
113k
      instr.m_content=")";
3202
113k
      child.push_back(instr);
3203
3204
113k
      stack.resize(size_t(int(numElt)-arity+1));
3205
113k
      stack[size_t(int(numElt)-arity)] = child;
3206
113k
      continue;
3207
113k
    }
3208
62.5k
    if (arity==1)
3209
29.8k
    {
3210
29.8k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
3211
29.8k
      stack[numElt-1].insert(stack[numElt-1].begin(), instr);
3212
29.8k
      if (wh==3) break;
3213
4.41k
      continue;
3214
29.8k
    }
3215
32.6k
    if (arity==2)
3216
32.6k
    {
3217
32.6k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
3218
32.6k
      stack[numElt-2].push_back(instr);
3219
32.6k
      stack[numElt-2].insert(stack[numElt-2].end(), stack[numElt-1].begin(), stack[numElt-1].end());
3220
32.6k
      stack.resize(numElt-1);
3221
32.6k
      continue;
3222
32.6k
    }
3223
0
    ok=false;
3224
0
    error = "### unexpected arity";
3225
0
    break;
3226
32.6k
  }
3227
3228
49.2k
  if (!ok) ;
3229
27.1k
  else if (stack.size()==1 && stack[0].size()>1 && stack[0][0].m_content=="=")
3230
24.5k
  {
3231
24.5k
    formula.insert(formula.begin(),stack[0].begin()+1,stack[0].end());
3232
24.5k
    return true;
3233
24.5k
  }
3234
2.57k
  else
3235
2.57k
    error = "###stack problem";
3236
3237
24.6k
  static bool first = true;
3238
24.6k
  if (first)
3239
5
  {
3240
5
    WPS_DEBUG_MSG(("LotusSpreadsheet::readFormula: I can not read some formula\n"));
3241
5
    first = false;
3242
5
  }
3243
3244
24.6k
  f.str("");
3245
1.97M
  for (size_t i = 0; i < stack.size(); ++i)
3246
1.95M
  {
3247
1.95M
    if (i) f << "##";
3248
1.95M
    for (auto const &j : stack[i])
3249
852k
      f << j << ",";
3250
1.95M
  }
3251
24.6k
  f << error;
3252
24.6k
  error = f.str();
3253
24.6k
  return false;
3254
49.2k
}
3255
3256
// ------------------------------------------------------------
3257
// zone 1b
3258
// ------------------------------------------------------------
3259
bool LotusSpreadsheet::readSheetName1B(std::shared_ptr<WPSStream> stream, long endPos)
3260
543
{
3261
543
  if (!stream) return false;
3262
543
  RVNGInputStreamPtr &input=stream->m_input;
3263
543
  libwps::DebugFile &ascFile=stream->m_ascii;
3264
543
  libwps::DebugStream f;
3265
3266
543
  long pos = input->tell();
3267
543
  long sz=endPos-pos;
3268
543
  f << "Entries(SheetName):";
3269
543
  if (sz<3)
3270
57
  {
3271
57
    WPS_DEBUG_MSG(("LotusParser::readSheetName1B: the zone size seems bad\n"));
3272
57
    f << "###";
3273
57
    ascFile.addPos(pos-6);
3274
57
    ascFile.addNote(f.str().c_str());
3275
57
    return true;
3276
57
  }
3277
486
  auto sheetId=int(libwps::readU16(input));
3278
486
  f << "id=" << sheetId << ",";
3279
486
  std::string name;
3280
4.08k
  for (long i=2; i<sz; ++i)
3281
4.00k
  {
3282
4.00k
    char c = char(libwps::readU8(input));
3283
4.00k
    if (c == '\0') break;
3284
3.60k
    name.push_back(c);
3285
3.60k
  }
3286
486
  f << name << ",";
3287
486
  if (sheetId<0||sheetId>=m_state->getNumSheet())
3288
211
  {
3289
211
    WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetName: the zone id seems bad\n"));
3290
211
    f << "##id";
3291
211
  }
3292
275
  else if (!name.empty())
3293
232
    m_state->getSheet(sheetId).m_name=libwps_tools_win::Font::unicodeString(name, m_mainParser.getDefaultFontType());
3294
486
  ascFile.addPos(pos-6);
3295
486
  ascFile.addNote(f.str().c_str());
3296
486
  return true;
3297
543
}
3298
3299
bool LotusSpreadsheet::readNote(std::shared_ptr<WPSStream> stream, long endPos)
3300
340
{
3301
340
  if (!stream) return false;
3302
340
  RVNGInputStreamPtr &input=stream->m_input;
3303
340
  libwps::DebugFile &ascFile=stream->m_ascii;
3304
340
  libwps::DebugStream f;
3305
3306
340
  long pos = input->tell();
3307
340
  long sz=endPos-pos;
3308
340
  f << "Entries(Note):";
3309
340
  if (sz<4)
3310
151
  {
3311
151
    WPS_DEBUG_MSG(("LotusParser::readNote: the zone size seems bad\n"));
3312
151
    f << "###";
3313
151
    ascFile.addPos(pos-6);
3314
151
    ascFile.addNote(f.str().c_str());
3315
151
    return true;
3316
151
  }
3317
189
  static bool first=true;
3318
189
  if (first)
3319
4
  {
3320
4
    first=false;
3321
4
    WPS_DEBUG_MSG(("LotusParser::readNote: this spreadsheet contains some notes, but there is no code to retrieve them\n"));
3322
4
  }
3323
189
  f << "id=" << int(libwps::readU8(input)) << ",";
3324
567
  for (int i=0; i<2; ++i)   // f0=1, f1=2|4
3325
378
  {
3326
378
    auto val=int(libwps::readU8(input));
3327
378
    if (val!=i+1) f << "f" << i << "=" << val << ",";
3328
378
  }
3329
189
  std::string text;
3330
8.17k
  for (long i=0; i<sz-3; ++i) text+=char(libwps::readU8(input));
3331
189
  f << getDebugStringForText(text) << ",";
3332
189
  ascFile.addPos(pos-6);
3333
189
  ascFile.addNote(f.str().c_str());
3334
189
  return true;
3335
340
}
3336
3337
//////////////////////////////////////////////////////////////////////
3338
// formatted text
3339
//////////////////////////////////////////////////////////////////////
3340
void LotusSpreadsheet::sendText(RVNGInputStreamPtr &input, long endPos, LotusSpreadsheetInternal::Style const &style) const
3341
37.8k
{
3342
37.8k
  if (!input || !m_listener)
3343
0
  {
3344
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: can not find the listener\n"));
3345
0
    return;
3346
0
  }
3347
37.8k
  libwps_tools_win::Font::Type fontType = style.m_fontType;
3348
37.8k
  WPSFont font=style.getFont();
3349
37.8k
  m_listener->setFont(font);
3350
37.8k
  bool prevEOL=false;
3351
37.8k
  std::string text;
3352
1.28M
  while (!input->isEnd())
3353
1.28M
  {
3354
1.28M
    long pos=input->tell();
3355
1.28M
    auto c=pos>=endPos ? '\0' : char(libwps::readU8(input));
3356
1.28M
    if ((c==0 || c==1 || c==0xa || c==0xd) && !text.empty())
3357
165k
    {
3358
165k
      m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, fontType));
3359
165k
      text.clear();
3360
165k
    }
3361
1.28M
    if (pos>=endPos) break;
3362
1.24M
    switch (c)
3363
1.24M
    {
3364
43.8k
    case 0x1:
3365
43.8k
      if (pos+1>=endPos)
3366
176
      {
3367
176
        WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: can not read the escape value\n"));
3368
176
        break;
3369
176
      }
3370
43.6k
      c=char(libwps::readU8(input));
3371
43.6k
      switch (c)
3372
43.6k
      {
3373
3.25k
      case 0x1e:
3374
3.25k
        if (pos+2>=endPos)
3375
39
        {
3376
39
          WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: can not read the escape value\n"));
3377
39
          break;
3378
39
        }
3379
3.21k
        c=char(libwps::readU8(input));
3380
3.21k
        switch (c)
3381
3.21k
        {
3382
328
        case 'b': // bold
3383
328
          font.m_attributes |= WPS_BOLD_BIT;
3384
328
          m_listener->setFont(font);
3385
328
          break;
3386
394
        case 'i': // italic
3387
394
          font.m_attributes |= WPS_ITALICS_BIT;
3388
394
          m_listener->setFont(font);
3389
394
          break;
3390
2.49k
        default:
3391
2.49k
          if (c>='0' && c<='7')
3392
983
          {
3393
983
            if (pos+3>=endPos)
3394
18
            {
3395
18
              WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: can not read the escape value\n"));
3396
18
              break;
3397
18
            }
3398
965
            auto c2=char(libwps::readU8(input));
3399
965
            if (c2=='c' && m_styleManager->getColor8(c-'0', font.m_color))
3400
427
              m_listener->setFont(font);
3401
538
            else if (c2!='F')   // F for font?
3402
509
            {
3403
509
              WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: unknown int sequence\n"));
3404
509
              break;
3405
509
            }
3406
456
            break;
3407
965
          }
3408
1.51k
          WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: unknown sequence\n"));
3409
1.51k
          break;
3410
3.21k
        }
3411
3.21k
        break;
3412
3.21k
      case 0x1f: // end
3413
448
        font=style.getFont();
3414
448
        m_listener->setFont(font);
3415
448
        break;
3416
613
      case ';': // unknown, ie. the text can begin with 27013b in some mac file
3417
613
        break;
3418
39.3k
      default:
3419
39.3k
        WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: unknown debut sequence\n"));
3420
39.3k
        break;
3421
43.6k
      }
3422
43.6k
      break;
3423
43.6k
    case 0xd:
3424
8.25k
      m_listener->insertEOL();
3425
8.25k
      prevEOL=true;
3426
8.25k
      break;
3427
3.95k
    case 0xa:
3428
3.95k
      if (!prevEOL)
3429
3.41k
      {
3430
3.41k
        WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: find 0xa without 0xd\n"));
3431
3.41k
      }
3432
3.95k
      prevEOL=false;
3433
3.95k
      break;
3434
1.18M
    default:
3435
1.18M
      if (c)
3436
892k
        text.push_back(c);
3437
1.18M
      break;
3438
1.24M
    }
3439
1.24M
  }
3440
37.8k
}
3441
3442
void LotusSpreadsheet::sendTextNote(RVNGInputStreamPtr &input, WPSEntry const &entry) const
3443
2.23k
{
3444
2.23k
  if (!input || !m_listener)
3445
0
  {
3446
0
    WPS_DEBUG_MSG(("LotusSpreadsheet::sendTextNote: can not find the listener\n"));
3447
0
    return;
3448
0
  }
3449
2.23k
  bool prevEOL=false;
3450
2.23k
  auto fontType=m_mainParser.getDefaultFontType();
3451
2.23k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
3452
2.23k
  long endPos=entry.end();
3453
2.23k
  std::string text;
3454
75.3k
  while (!input->isEnd())
3455
75.2k
  {
3456
75.2k
    long pos=input->tell();
3457
75.2k
    auto c=pos>=endPos ? '\0' : char(libwps::readU8(input));
3458
75.2k
    if ((c==0 || c==0xa || c==0xd) && !text.empty())
3459
10.5k
    {
3460
10.5k
      m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, fontType));
3461
10.5k
      text.clear();
3462
10.5k
    }
3463
75.2k
    if (pos>=endPos) break;
3464
73.1k
    switch (c)
3465
73.1k
    {
3466
3.31k
    case 0xd:
3467
3.31k
      m_listener->insertEOL();
3468
3.31k
      prevEOL=true;
3469
3.31k
      break;
3470
1.52k
    case 0xa:
3471
1.52k
      if (!prevEOL)
3472
1.38k
      {
3473
1.38k
        WPS_DEBUG_MSG(("LotusSpreadsheet::sendTextNote: find 0xa without 0xd\n"));
3474
1.38k
      }
3475
1.52k
      prevEOL=false;
3476
1.52k
      break;
3477
68.2k
    default:
3478
68.2k
      if (c)
3479
45.8k
        text.push_back(c);
3480
68.2k
      break;
3481
73.1k
    }
3482
73.1k
  }
3483
2.23k
}
3484
3485
std::string LotusSpreadsheet::getDebugStringForText(std::string const &text)
3486
62.4k
{
3487
62.4k
  std::string res;
3488
62.4k
  size_t len=text.length();
3489
538k
  for (size_t i=0; i<len; ++i)
3490
476k
  {
3491
476k
    char c=text[i];
3492
476k
    switch (c)
3493
476k
    {
3494
8.06k
    case 0x1:
3495
8.06k
      if (i+1>=len)
3496
661
      {
3497
661
        WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: can not read the escape value\n"));
3498
661
        res+= "[##escape]";
3499
661
        break;
3500
661
      }
3501
7.40k
      c=text[++i];
3502
7.40k
      switch (c)
3503
7.40k
      {
3504
3.55k
      case 0x1e:
3505
3.55k
        if (i+1>=len)
3506
1.34k
        {
3507
1.34k
          WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: can not read the escape value\n"));
3508
1.34k
          res+= "[##escape1]";
3509
1.34k
          break;
3510
1.34k
        }
3511
2.20k
        c=text[++i];
3512
2.20k
        switch (c)
3513
2.20k
        {
3514
200
        case 'b': // bold
3515
358
        case 'i': // italic
3516
358
          res+= std::string("[") + c + "]";
3517
358
          break;
3518
1.84k
        default:
3519
1.84k
          if (c>='0' && c<='8')
3520
710
          {
3521
710
            if (i+1>=len)
3522
54
            {
3523
54
              WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: can not read the escape value\n"));
3524
54
              res+= "[##escape1]";
3525
54
              break;
3526
54
            }
3527
656
            char c2=text[++i];
3528
656
            if (c2!='c' && c2!='F')   // color and F for font?
3529
350
            {
3530
350
              WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: unknown int sequence\n"));
3531
350
              res+= std::string("[##") + c + c2 + "]";
3532
350
              break;
3533
350
            }
3534
306
            res += std::string("[") + c + c2 + "]";
3535
306
            break;
3536
656
          }
3537
1.13k
          WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: unknown sequence\n"));
3538
1.13k
          res+= std::string("[##") + c + "]";
3539
1.13k
          break;
3540
2.20k
        }
3541
2.20k
        break;
3542
2.20k
      case 0x1f: // end
3543
177
        res+= "[^]";
3544
177
        break;
3545
      // case ';': ?
3546
3.67k
      default:
3547
3.67k
        WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: unknown debut sequence\n"));
3548
3.67k
        res+= std::string("[##") +c+ "]";
3549
3.67k
        break;
3550
7.40k
      }
3551
7.40k
      break;
3552
7.40k
    case 0xd:
3553
3.78k
      res+="\\n";
3554
3.78k
      break;
3555
464k
    default:
3556
464k
      res+=c;
3557
464k
      break;
3558
476k
    }
3559
476k
  }
3560
62.4k
  return res;
3561
62.4k
}
3562
3563
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */