Coverage Report

Created: 2026-09-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/MsWksSSParser.cxx
Line
Count
Source
1
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3
/* libmwaw
4
* Version: MPL 2.0 / LGPLv2+
5
*
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 2.0 (the "License"); you may not use this file except in compliance with
8
* the License or as specified alternatively below. You may obtain a copy of
9
* the License at http://www.mozilla.org/MPL/
10
*
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
14
* License.
15
*
16
* Major Contributor(s):
17
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20
* Copyright (C) 2006, 2007 Andrew Ziem
21
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22
*
23
*
24
* All Rights Reserved.
25
*
26
* For minor contributions see the git repository.
27
*
28
* Alternatively, the contents of this file may be used under the terms of
29
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30
* in which case the provisions of the LGPLv2+ are applicable
31
* instead of those above.
32
*/
33
34
#include <iomanip>
35
#include <iostream>
36
#include <limits>
37
#include <sstream>
38
39
#include <librevenge/librevenge.h>
40
41
42
#include "MWAWCell.hxx"
43
#include "MWAWFont.hxx"
44
#include "MWAWFontConverter.hxx"
45
#include "MWAWHeader.hxx"
46
#include "MWAWOLEParser.hxx"
47
#include "MWAWPrinter.hxx"
48
#include "MWAWSpreadsheetListener.hxx"
49
#include "MWAWSubDocument.hxx"
50
51
#include "MsWksGraph.hxx"
52
#include "MsWks3Text.hxx"
53
#include "MsWksDocument.hxx"
54
55
#include "MsWksSSParser.hxx"
56
57
/** Internal: the structures of a MsWksSSParser */
58
namespace MsWksSSParserInternal
59
{
60
/** a cell of a MsWksSSParser */
61
class Cell final : public MWAWCell
62
{
63
public:
64
  /// constructor
65
  Cell()
66
8.62k
    : m_content()
67
8.62k
    , m_noteId(0)
68
8.62k
  {
69
8.62k
  }
70
9.49k
  Cell(Cell const &)=default;
71
  Cell &operator=(Cell const &)=default;
72
4.31k
  Cell &operator=(Cell &&)=default;
73
  //! destructor
74
  ~Cell() final;
75
  //! returns true if the cell do contain any content
76
  bool isEmpty() const
77
4.27k
  {
78
4.27k
    return m_content.empty() && !hasBorders() && !m_noteId;
79
4.27k
  }
80
81
  //! operator<<
82
  friend std::ostream &operator<<(std::ostream &o, Cell const &cell);
83
84
  //! the cell content
85
  MWAWCellContent m_content;
86
87
  /** the note id */
88
  int m_noteId;
89
};
90
91
//! operator<<
92
std::ostream &operator<<(std::ostream &o, Cell const &cell)
93
0
{
94
0
  o << static_cast<MWAWCell const &>(cell);
95
96
0
  if (cell.m_noteId) o << ", noteId=" << cell.m_noteId;
97
0
  return o;
98
0
}
99
100
/** the spreadsheet of a of a MsWksSSParser */
101
class Spreadsheet
102
{
103
public:
104
  //! constructor
105
  Spreadsheet()
106
34.5k
    : m_font(3,12)
107
34.5k
    , m_widthCols()
108
34.5k
    , m_cells()
109
34.5k
    , m_listPageBreaks()
110
34.5k
    , m_idNoteMap()
111
34.5k
    , m_name("Sheet0")
112
34.5k
  {
113
34.5k
  }
114
  //! convert the m_widthCols in a vector of of point size
115
  std::vector<float> convertInPoint(std::vector<int> const &list, float defSize) const
116
265
  {
117
265
    auto numCols=size_t(getRightBottomPosition()[0]+1);
118
265
    std::vector<float> res;
119
265
    res.resize(numCols);
120
13.2k
    for (size_t i = 0; i < numCols; i++) {
121
12.9k
      if (i>=list.size() || list[i] < 0) res[i] = defSize;
122
8.03k
      else res[i] = float(list[i]);
123
12.9k
    }
124
265
    return res;
125
265
  }
126
127
  /** the default font */
128
  MWAWFont m_font;
129
  /** the column size in pixels(?) */
130
  std::vector<int> m_widthCols;
131
  /** the list of not empty cells */
132
  std::vector<Cell> m_cells;
133
  /** the list of page break */
134
  std::vector<int> m_listPageBreaks;
135
  /** a map id->note content */
136
  std::map<int, MWAWEntry> m_idNoteMap;
137
  /** the spreadsheet name */
138
  std::string m_name;
139
protected:
140
  /** returns the last Right Bottom cell position */
141
  MWAWVec2i getRightBottomPosition() const
142
265
  {
143
265
    int maxX = 0, maxY = 0;
144
4.23k
    for (auto const &cell : m_cells) {
145
4.23k
      MWAWVec2i const &p = cell.position();
146
4.23k
      if (p[0] > maxX) maxX = p[0];
147
4.23k
      if (p[1] > maxY) maxY = p[1];
148
4.23k
    }
149
265
    return MWAWVec2i(maxX, maxY);
150
265
  }
151
};
152
153
Cell::~Cell()
154
18.1k
{
155
18.1k
}
156
157
////////////////////////////////////////
158
//! Internal: the state of a MsWksSSParser
159
struct State {
160
  //! constructor
161
  State()
162
30.8k
    : m_spreadsheet()
163
30.8k
    , m_actPage(0)
164
30.8k
    , m_numPages(0)
165
30.8k
    , m_pageLength(-1)
166
30.8k
  {
167
30.8k
  }
168
169
  /** the spreadsheet */
170
  Spreadsheet m_spreadsheet;
171
  int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
172
173
  //! the page length in point (if known)
174
  int m_pageLength;
175
};
176
177
////////////////////////////////////////
178
//! Internal: the subdocument of a MsWksSSParser
179
class SubDocument final : public MWAWSubDocument
180
{
181
public:
182
  SubDocument(MsWksSSParser &pars, MWAWInputStreamPtr const &input, int zoneId)
183
1.37k
    : MWAWSubDocument(&pars, input, MWAWEntry())
184
1.37k
    ,m_id(zoneId)
185
1.37k
  {
186
1.37k
  }
187
188
  //! destructor
189
0
  ~SubDocument() final {}
190
191
  //! operator!=
192
  bool operator!=(MWAWSubDocument const &doc) const final;
193
194
  //! the parser function
195
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
196
197
protected:
198
  /** the subdocument id*/
199
  int m_id;
200
};
201
202
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
203
1.37k
{
204
1.37k
  if (!listener.get()) {
205
0
    MWAW_DEBUG_MSG(("MsWksSSParserInternal::SubDocument::parse: no listener\n"));
206
0
    return;
207
0
  }
208
1.37k
  auto *parser = dynamic_cast<MsWksSSParser *>(m_parser);
209
1.37k
  if (!parser) {
210
0
    MWAW_DEBUG_MSG(("MsWksSSParserInternal::SubDocument::parse: no parser\n"));
211
0
    return;
212
0
  }
213
1.37k
  long pos = m_input->tell();
214
1.37k
  parser->sendNote(m_id);
215
1.37k
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
216
1.37k
}
217
218
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
219
0
{
220
0
  if (MWAWSubDocument::operator!=(doc)) return true;
221
0
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
222
0
  if (!sDoc) return true;
223
0
  if (m_id != sDoc->m_id) return true;
224
0
  return false;
225
0
}
226
}
227
228
229
////////////////////////////////////////////////////////////
230
// constructor/destructor, ...
231
////////////////////////////////////////////////////////////
232
MsWksSSParser::MsWksSSParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
233
13.5k
  : MWAWSpreadsheetParser(input, rsrcParser, header)
234
13.5k
  , m_state()
235
13.5k
  , m_listZones()
236
13.5k
  , m_document()
237
13.5k
{
238
13.5k
  MWAWInputStreamPtr mainInput=input;
239
13.5k
  if (!input) return;
240
13.5k
  if (input->isStructured()) {
241
664
    MWAWInputStreamPtr mainOle = input->getSubStreamByName("MN0");
242
664
    if (mainOle)
243
664
      mainInput=mainOle;
244
664
  }
245
13.5k
  m_document.reset(new MsWksDocument(mainInput, *this));
246
13.5k
  init();
247
13.5k
}
248
249
MsWksSSParser::~MsWksSSParser()
250
13.5k
{
251
13.5k
}
252
253
void MsWksSSParser::init()
254
13.5k
{
255
13.5k
  resetSpreadsheetListener();
256
13.5k
  setAsciiName("main-1");
257
258
13.5k
  m_state.reset(new MsWksSSParserInternal::State);
259
260
  // reduce the margin (in case, the page is not defined)
261
13.5k
  getPageSpan().setMargins(0.1);
262
13.5k
}
263
264
////////////////////////////////////////////////////////////
265
// the parser
266
////////////////////////////////////////////////////////////
267
void MsWksSSParser::parse(librevenge::RVNGSpreadsheetInterface *docInterface)
268
3.74k
{
269
3.74k
  if (!m_document || !m_document->getInput() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
270
3.74k
  bool ok = true;
271
3.74k
  try {
272
#ifdef DEBUG
273
    MWAWInputStreamPtr &input= getInput();
274
    if (input->isStructured()) {
275
      std::shared_ptr<MWAWOLEParser> oleParser(new MWAWOLEParser("MN0", getParserState()->m_fontConverter, 3));
276
      oleParser->parse(input);
277
    }
278
#endif
279
    // create the asciiFile
280
3.74k
    m_document->initAsciiFile(asciiName());
281
282
3.74k
    checkHeader(nullptr);
283
3.74k
    createZones();
284
3.74k
    ok=!m_state->m_spreadsheet.m_cells.empty();
285
3.74k
    if (ok) {
286
265
      createDocument(docInterface);
287
265
      sendSpreadsheet();
288
265
    }
289
3.74k
  }
290
3.74k
  catch (...) {
291
0
    MWAW_DEBUG_MSG(("MsWksSSParser::parse: exception catched when parsing\n"));
292
0
    ok = false;
293
0
  }
294
3.74k
  m_document->ascii().reset();
295
296
3.74k
  resetSpreadsheetListener();
297
3.74k
  if (!ok) throw(libmwaw::ParseException());
298
3.74k
}
299
300
void MsWksSSParser::sendNote(int noteId)
301
1.37k
{
302
1.37k
  MWAWListenerPtr listener=getSpreadsheetListener();
303
1.37k
  if (!listener||
304
1.37k
      m_state->m_spreadsheet.m_idNoteMap.find(noteId)==m_state->m_spreadsheet.m_idNoteMap.end()) {
305
1.34k
    MWAW_DEBUG_MSG(("MsWksSSParser::sendNote: can not send note %d\n", noteId));
306
1.34k
    return;
307
1.34k
  }
308
25
  MWAWEntry const &entry=m_state->m_spreadsheet.m_idNoteMap.find(noteId)->second;
309
25
  int const vers=version();
310
25
  if (!entry.valid()) return;
311
25
  MWAWInputStreamPtr input = m_document->getInput();
312
25
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
313
1.30k
  while (1) {
314
1.30k
    long pos=input->tell();
315
1.30k
    if (input->isEnd() || pos>=entry.end()) return;
316
1.27k
    auto c = static_cast<int>(input->readULong(1));
317
1.27k
    switch (c) {
318
0
    case 0x9:
319
0
      listener->insertTab();
320
0
      break;
321
1
    case 0x10: // cursor pos
322
1
    case 0x11:
323
1
      break;
324
1.27k
    default:
325
1.27k
      if (c >= 0x15 && c <= 0x19 && vers >= 3) {
326
10
        int sz = (c==0x19) ? 0 : (c == 0x18) ? 1 : 2;
327
10
        if (sz && pos+1+sz <=  entry.end())
328
10
          input->seek(sz, librevenge::RVNG_SEEK_CUR);
329
10
        switch (c) {
330
0
        case 0x19:
331
0
          listener->insertField(MWAWField(MWAWField::Title));
332
0
          break;
333
4
        case 0x18:
334
4
          listener->insertField(MWAWField(MWAWField::PageNumber));
335
4
          break;
336
3
        case 0x16:
337
3
          listener->insertField(MWAWField(MWAWField::Time));
338
3
          break;
339
0
        case 0x17: // id = 0 : short date ; id=9 : long date
340
0
          listener->insertField(MWAWField(MWAWField::Date));
341
0
          break;
342
3
        case 0x15:
343
3
          MWAW_DEBUG_MSG(("MsWksSSParser::sendNote: find unknown field type 0x15\n"));
344
3
          break;
345
0
        default:
346
0
          break;
347
10
        }
348
10
      }
349
1.26k
      else if (c <= 0x1f) {
350
840
        MWAW_DEBUG_MSG(("MsWksSSParser::sendNote: find char=%x\n", static_cast<unsigned int>(c)));
351
840
      }
352
425
      else
353
425
        listener->insertCharacter(static_cast<unsigned char>(c), input, entry.end());
354
1.27k
      break;
355
1.27k
    }
356
357
1.27k
  }
358
25
}
359
360
////////////////////////////////////////////////////////////
361
// create the document
362
////////////////////////////////////////////////////////////
363
void MsWksSSParser::createDocument(librevenge::RVNGSpreadsheetInterface *documentInterface)
364
265
{
365
265
  if (!documentInterface) return;
366
265
  if (getSpreadsheetListener()) {
367
0
    MWAW_DEBUG_MSG(("MsWksSSParser::createDocument: listener already exist\n"));
368
0
    return;
369
0
  }
370
371
265
  std::vector<MWAWPageSpan> pageList;
372
265
  m_state->m_actPage = 0;
373
265
  m_document->getPageSpanList(pageList, m_state->m_numPages);
374
265
  MWAWSpreadsheetListenerPtr listen(new MWAWSpreadsheetListener(*getParserState(), pageList, documentInterface));
375
265
  setSpreadsheetListener(listen);
376
265
  listen->startDocument();
377
  // time to send page information the graph parser and the text parser
378
265
  m_document->getGraphParser()->setPageLeftTop
379
265
  (MWAWVec2f(72.f *float(getPageSpan().getMarginLeft()),
380
265
             72.f *float(getPageSpan().getMarginTop())+m_document->getHeaderFooterHeight(true)));
381
265
}
382
383
384
////////////////////////////////////////////////////////////
385
//
386
// Intermediate level
387
//
388
////////////////////////////////////////////////////////////
389
bool MsWksSSParser::createZones()
390
3.74k
{
391
3.74k
  if (getInput()->isStructured())
392
141
    m_document->createOLEZones(getInput());
393
3.74k
  MWAWInputStreamPtr input = m_document->getInput();
394
3.74k
  const int vers=version();
395
3.74k
  long pos;
396
3.74k
  if (vers>2) {
397
2.91k
    pos = input->tell();
398
2.91k
    if (!m_document->readDocumentInfo(0x9a))
399
1.11k
      m_document->getInput()->seek(pos, librevenge::RVNG_SEEK_SET);
400
2.91k
    pos = input->tell();
401
2.91k
    if (m_document->hasHeader() && !m_document->readGroupHeaderFooter(true,99))
402
637
      input->seek(pos, librevenge::RVNG_SEEK_SET);
403
2.91k
    pos = input->tell();
404
2.91k
    if (m_document->hasFooter() && !m_document->readGroupHeaderFooter(false,99))
405
1.87k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
406
2.91k
  }
407
3.74k
  readSSheetZone();
408
409
3.74k
  auto &typeZoneMap=m_document->getTypeZoneMap();
410
  // fixme: getNewZoneId here
411
3.74k
  int mainId=MsWksDocument::Z_MAIN;
412
3.74k
  typeZoneMap.insert(std::multimap<int, MsWksDocument::Zone>::value_type
413
3.74k
                     (MsWksDocument::Z_MAIN,MsWksDocument::Zone(MsWksDocument::Z_MAIN, mainId)));
414
3.74k
  pos = input->tell();
415
3.74k
  libmwaw::DebugFile &ascFile = m_document->ascii();
416
3.74k
  if (input->isEnd() || input->readLong(2)!=0)
417
2.26k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
418
1.48k
  else {
419
1.48k
    MWAWEntry group;
420
1.48k
    group.setId(mainId);
421
1.48k
    group.setName("RBDR");
422
1.48k
    if (!m_document->m_graphParser->readRB(input,group,1)) {
423
1.30k
      MWAW_DEBUG_MSG(("MsWksSSParser::createZones: can not read RBDR group\n"));
424
1.30k
      ascFile.addPos(pos+2);
425
1.30k
      ascFile.addNote("Entries(RBDR):###");
426
1.30k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
427
1.30k
    }
428
1.48k
  }
429
430
  // normally, the file is now parsed, let check for potential remaining zones
431
35.4k
  while (!input->isEnd()) {
432
35.2k
    MWAW_DEBUG_MSG(("MsWksSSParser::createZones: find some unexpected data\n"));
433
35.2k
    pos = input->tell();
434
35.2k
    MsWksDocument::Zone unknown;
435
35.2k
    if (!m_document->readZone(unknown) || input->tell() <= pos) {
436
3.53k
      ascFile.addPos(pos);
437
3.53k
      ascFile.addNote("Entries(End)");
438
3.53k
      ascFile.addPos(pos+100);
439
3.53k
      ascFile.addNote("_");
440
3.53k
      break;
441
3.53k
    }
442
35.2k
  }
443
444
  // ok, prepare the data
445
3.74k
  m_state->m_numPages = 1;
446
3.74k
  std::vector<int> linesH, pagesH;
447
3.74k
  m_document->getGraphParser()->computePositions(mainId, linesH, pagesH);
448
3.74k
  return true;
449
3.74k
}
450
451
////////////////////////////////////////////////////////////
452
// read a spreadsheet zone
453
////////////////////////////////////////////////////////////
454
bool MsWksSSParser::readSSheetZone()
455
3.74k
{
456
3.74k
  auto &sheet=m_state->m_spreadsheet;
457
3.74k
  sheet=MsWksSSParserInternal::Spreadsheet();
458
3.74k
  int const vers=version();
459
460
3.74k
  MWAWInputStreamPtr input=m_document->getInput();
461
3.74k
  libmwaw::DebugFile &ascFile=m_document->ascii();
462
3.74k
  long pos = input->tell(), sheetDebPos=pos;
463
3.74k
  if (!input->checkPosition(pos+0x30+0x30+0x408)) {
464
2.85k
    MWAW_DEBUG_MSG(("MsWksSSParser::readSSheetZone:Can not read part A/B\n"));
465
2.85k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
466
2.85k
    return false;
467
2.85k
  }
468
469
883
  ascFile.addPos(pos);
470
883
  libmwaw::DebugStream f, f2;
471
883
  f << "Entries(SSheet): v0=["; // always 8, -1 ?
472
2.64k
  for (int i = 0; i < 2; i++)
473
1.76k
    f << input->readLong(1) << ",";
474
883
  f << "],";
475
476
883
  int N[2]; // max col, max row
477
1.76k
  for (auto &n : N) n = static_cast<int>(input->readLong(2));
478
883
  f << "maxCol=" << N[0] << ","  << "maxRow=" << N[1] << ",";
479
480
883
  f << "unk0=" << std::hex << input->readULong(4) << std::dec << ","; // big number
481
482
883
  int zoneSize[3];
483
883
  zoneSize[0] = static_cast<int>(input->readLong(2));
484
883
  if (zoneSize[0]) f << "zone[SheetF]=" << std::hex << zoneSize[0] << std::dec << ",";
485
883
  auto numPageBreak = static_cast<int>(input->readLong(2));
486
883
  if (numPageBreak) f << "nPgBreak=" << numPageBreak << ",";
487
883
  auto numColBreak = static_cast<int>(input->readLong(2));
488
883
  if (numColBreak) f << "nColBreak=" << numColBreak << ",";
489
883
  zoneSize[1]= static_cast<int>(input->readLong(2));
490
883
  if (zoneSize[1]) f << "zone[DocInfo]=" << zoneSize[1] << ",";
491
883
  f << "unk1=" << std::hex << input->readULong(4) << std::dec << ","; // big number
492
883
  f << std::dec;
493
494
883
  int val;
495
2.64k
  for (int i = 0; i < 2; i++) {
496
1.76k
    val = static_cast<int>(input->readLong(2));
497
1.76k
    if (val)
498
579
      f << "w" << i << "=" << val << ",";
499
1.76k
  }
500
883
  f << "v2=["; // [_,_,_,_,_,_,] or [_,1,3,_,1,3,] or [_,5,5,_,4,2,]
501
6.18k
  for (int i = 0; i < 6; i++) {
502
5.29k
    val = static_cast<int>(input->readLong(1));
503
5.29k
    if (val)
504
2.10k
      f << val << ",";
505
3.19k
    else
506
3.19k
      f << "_,";
507
5.29k
  }
508
883
  f << "]";
509
883
  f << ", v3=["; // [_,_,_,_,] or [_,_,_,1,] or [_,_,_,11,]
510
4.41k
  for (int i = 0; i < 4; i++) {
511
3.53k
    val = static_cast<int>(input->readLong(2));
512
3.53k
    if (val)
513
1.57k
      f << val << ",";
514
1.95k
    else
515
1.95k
      f << "_,";
516
3.53k
  }
517
883
  f << "]";
518
519
883
  f << ", v4=[" << std::hex;
520
4.41k
  for (int i = 0; i < 4; i++) {
521
3.53k
    val = static_cast<int>(input->readLong(2));
522
3.53k
    if (val)
523
1.35k
      f << val << ",";
524
2.17k
    else
525
2.17k
      f << "_,";
526
3.53k
  }
527
883
  f << "]" << std::dec;
528
529
883
  ascFile.addNote(f.str().c_str());
530
531
883
  pos = input->tell();
532
883
  f.str("");
533
883
  ascFile.addPos(pos);
534
883
  f << "SSheetB: ";
535
536
883
  std::vector<int> colPos;
537
538
883
  int prevW = -1;
539
883
  bool ok = true;
540
80.2k
  for (int i = 0; i < 257; i++) {
541
79.9k
    auto col = static_cast<int>(input->readULong(2));
542
79.9k
    if ((col & 0xFFF) != i+1) {
543
575
      ok = false;
544
575
      break;
545
575
    }
546
79.4k
    auto w = static_cast<int>(input->readLong(2));
547
79.4k
    if (w > 0) {
548
79.3k
      if (prevW > w && i < 200) {
549
7
        ok = false;
550
7
        break;
551
7
      }
552
79.2k
      if (prevW > w) w = prevW;
553
78.6k
      else prevW = w;
554
79.2k
    }
555
79.3k
    colPos.push_back(w);
556
79.3k
  }
557
558
883
  if (!ok) {
559
582
    MWAW_DEBUG_MSG(("MsWksSSParser::readSSheetZone:Can not read part B\n"));
560
582
    input->seek(pos, librevenge::RVNG_SEEK_SET);
561
582
    return false;
562
582
  }
563
564
301
  f << "lastN=" << input->readULong(2);
565
301
  f << ", lastV=" << input->readULong(2);
566
301
  f << ", width=[";
567
301
  std::vector<int> &colSize = sheet.m_widthCols;
568
301
  colSize.resize(256);
569
77.6k
  for (size_t i = 0; i < 257; i++) {
570
77.3k
    int cPos = colPos[i];
571
77.3k
    int lastPos = (i == 0) ? 0 : colPos[i-1];
572
77.3k
    if (cPos == 0) f << "_";
573
77.3k
    else if (cPos == -1) {
574
1
      f << "Inf";
575
1
      cPos = 0;
576
1
    }
577
77.3k
    else f << cPos-lastPos;
578
579
77.3k
    if (i< 256) {
580
77.0k
      if (cPos) colSize[i]=cPos-lastPos;
581
7
      else colSize[i]=0;
582
77.0k
    }
583
584
77.3k
    f << ",";
585
77.3k
  }
586
301
  f << "]" << std::dec;
587
301
  ascFile.addNote(f.str().c_str());
588
589
301
  pos = input->tell();
590
301
  f.str("");
591
301
  ascFile.addPos(pos);
592
301
  long endPos=pos+256;
593
301
  f << "SSheetC:";
594
301
  int numCharts=0;
595
435
  for (int i=0; i < 8; ++i) {
596
    // the chart names in v2, can we have more than 8 charts ?
597
424
    long cPos=input->tell();
598
424
    val = static_cast<int>(input->readLong(2));
599
424
    auto sSz=static_cast<int>(input->readULong(1));
600
424
    if (!sSz || sSz>32-7) {
601
290
      input->seek(cPos, librevenge::RVNG_SEEK_SET);
602
290
      break;
603
290
    }
604
134
    ++numCharts;
605
134
    f << "Chart" << i << "=[";
606
134
    if (val) f << "unkn=" << val << ",";
607
134
    std::string name("");
608
1.45k
    for (int c=0; c<sSz; ++c)
609
1.32k
      name += char(input->readULong(1));
610
134
    f << name << ",";
611
134
    if ((sSz%2)==0)   input->seek(1, librevenge::RVNG_SEEK_CUR);
612
134
    f << "id=" << std::hex << input->readULong(4) << std::dec << "],";
613
134
    input->seek(cPos+32, librevenge::RVNG_SEEK_SET);
614
134
  }
615
301
  int numRemains=int(endPos-input->tell())/2;
616
36.6k
  for (int i = 0; i < numRemains; i++) {
617
36.3k
    val = static_cast<int>(input->readLong(2));
618
36.3k
    if (val) f << "f" << i << "=" << val << ",";
619
36.3k
  }
620
301
  ascFile.addNote(f.str().c_str());
621
301
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
622
623
301
  pos = input->tell();
624
301
  f.str("");
625
301
  ascFile.addPos(pos);
626
627
301
  if (!input->checkPosition(pos+52)) {
628
1
    MWAW_DEBUG_MSG(("MsWksSSParser::readSSheetZone:Can not read part D\n"));
629
1
    input->seek(pos, librevenge::RVNG_SEEK_SET);
630
1
    return false;
631
1
  }
632
633
300
  f << "SSheetD:";
634
1.80k
  for (int i = 0; i < 5; i++) {
635
1.50k
    val = static_cast<int>(input->readLong(2));
636
1.50k
    if (i==3) {
637
300
      if (val==44) continue;
638
27
      f << "unkn=" << val << ",";
639
27
    }
640
1.20k
    else if (val)
641
290
      f << "f" << i << "=" << val << ",";
642
1.50k
  }
643
300
  f << "dim?=["; // in general 1,1,1,1 but can be X,X,1,1
644
1.50k
  for (int i = 0; i < 4; i++)
645
1.20k
    f << input->readLong(2) << ",";
646
300
  f << "]";
647
648
  // always g0=3ffe, g1=100 ?
649
900
  for (int i = 0; i < 2; i++) {
650
600
    f << ", g" << i << "="  << std::hex << input->readLong(2) << std::dec;
651
600
  }
652
300
  auto fId = static_cast<int>(input->readLong(2));
653
300
  auto fSize = static_cast<int>(input->readLong(2));
654
300
  sheet.m_font = MWAWFont(fId, float(fSize));
655
300
  f << ", [" <<  sheet.m_font.getDebugString(getParserState()->m_fontConverter) << "]";
656
  // , [0,{33|255},] ?
657
300
  f << ", unk=[" << std::dec;
658
900
  for (int i = 0; i < 2; i++)
659
600
    f << input->readLong(2) << ",";
660
300
  f << "]";
661
300
  int numRemain=int(sheetDebPos+m_document->getLengthOfFileHeader3()-input->tell())/2;
662
5.53k
  for (int i = 0; i < numRemain; i++) {
663
5.23k
    val = static_cast<int>(input->readLong(2));
664
5.23k
    if (val) f << "f" << i << "=" << val << ",";
665
5.23k
  }
666
300
  ascFile.addNote(f.str().c_str());
667
300
  input->seek(sheetDebPos+m_document->getLengthOfFileHeader3(), librevenge::RVNG_SEEK_SET);
668
669
  // part E
670
300
  MWAWVec2i cellPos(0,0);
671
1.49k
  while (1) {
672
1.49k
    if (cellPos[1] == N[1]) break;
673
674
1.44k
    long posRow = input->tell();
675
1.44k
    unsigned long length = input->readULong(4);
676
1.44k
    if (length & 0xF0000000L) {
677
371
      int nRow = length & 0x0FFFFFFFL;
678
371
      if ((length >> 28) != 8 || std::numeric_limits<int>::max() - cellPos[1] < nRow) {
679
27
        input->seek(posRow, librevenge::RVNG_SEEK_SET);
680
27
        break;
681
27
      }
682
344
      f.str("");
683
344
      f << "SSheetE:skipRow=" << std::hex << nRow;
684
344
      cellPos[1]+=nRow;
685
344
      ascFile.addPos(posRow);
686
344
      ascFile.addNote(f.str().c_str());
687
344
      continue;
688
371
    }
689
1.07k
    long endRow = posRow+long(length)+4;
690
691
1.07k
    if (!input->checkPosition(endRow)) {
692
30
      MWAW_DEBUG_MSG(("MsWksSSParser::readSSheetZone:Can not read part E(row)\n"));
693
30
      input->seek(posRow, librevenge::RVNG_SEEK_SET);
694
30
      return false;
695
30
    }
696
697
1.04k
    f.str("");
698
1.04k
    cellPos[0]=0;
699
1.04k
    f << "SSheetE(Row" << std::dec << cellPos[1]+1 << "): ";
700
12.6k
    while (input->tell() < endRow-1) {
701
11.6k
      pos = input->tell();
702
11.6k
      auto sz = static_cast<int>(input->readULong(1));
703
704
11.6k
      if (sz == 0xFE) { // skip next cell
705
228
        auto numC = static_cast<int>(input->readULong(1));
706
228
        f2.str("");
707
228
        f2 << "SSheetE:skipC=" << numC;
708
228
        cellPos[0]+=numC;
709
228
        ascFile.addPos(pos);
710
228
        ascFile.addNote(f2.str().c_str());
711
228
        continue;
712
228
      }
713
714
11.4k
      if (sz == 0) {
715
7.13k
        ascFile.addPos(pos);
716
7.13k
        ascFile.addNote("SSheetE:empty");
717
7.13k
        ++cellPos[0];
718
7.13k
        continue;
719
7.13k
      }
720
4.31k
      MsWksSSParserInternal::Cell cell;
721
4.31k
      if (!readCell(sz, cellPos, cell)) {
722
35
        input->seek(pos, librevenge::RVNG_SEEK_SET);
723
35
        break;
724
35
      }
725
4.27k
      if (!cell.isEmpty())
726
4.23k
        sheet.m_cells.push_back(cell);
727
4.27k
      ++cellPos[0];
728
4.27k
    }
729
730
1.04k
    ++cellPos[1];
731
1.04k
    if (input->tell() != endRow-1) {
732
173
      MWAW_DEBUG_MSG(("MsWksSSParser::readSSheetZone:Can not read part E(cells)\n"));
733
173
      input->seek(posRow, librevenge::RVNG_SEEK_SET);
734
173
      return false;
735
173
    }
736
872
    ascFile.addDelimiter(endRow-1,'|');
737
872
    val = static_cast<int>(input->readULong(1));
738
872
    if (val == 0) f << "*";
739
850
    else if (val != 0xFF)
740
19
      f << "##end=" << std::hex << val << std::dec;
741
742
872
    ascFile.addPos(posRow);
743
872
    ascFile.addNote(f.str().c_str());
744
872
    if (val == 0)
745
22
      break;
746
872
  }
747
748
97
  pos = input->tell();
749
97
  if (!input->checkPosition(pos+zoneSize[0])) {
750
3
    input->seek(pos, librevenge::RVNG_SEEK_SET);
751
3
    MWAW_DEBUG_MSG(("MsWksSSParser::readSSheetZone:Can not read part F\n"));
752
3
    return false;
753
3
  }
754
755
94
  f.str("");
756
94
  f << "SSheetF[A]:";
757
94
  ascFile.addPos(pos);
758
94
  ascFile.addNote(f.str().c_str());
759
94
  input->seek(pos+zoneSize[0]-10, librevenge::RVNG_SEEK_SET);
760
761
94
  pos = input->tell();
762
94
  f.str("");
763
94
  f << "SSheetF[B]:" << std::dec;
764
470
  for (int i = 0; i < 4; i++) {
765
376
    val = static_cast<int>(input->readLong(2));
766
376
    if (val) f << "f" << i << "=" << val << ",";
767
376
  }
768
94
  val = static_cast<int>(input->readULong(2));
769
94
  if (val) f << std::hex << "fl=" << val << "," << std::dec;
770
94
  ascFile.addPos(pos);
771
94
  ascFile.addNote(f.str().c_str());
772
773
94
  if (numPageBreak || numColBreak) {
774
16
    pos=input->tell();
775
16
    f.str("");
776
16
    f << "SSheetG:";
777
16
    if (numPageBreak > 0) {
778
11
      sheet.m_listPageBreaks.resize(size_t(numPageBreak));
779
11
      f << "pgBreak=[";
780
88.4k
      for (auto &pgBreak : sheet.m_listPageBreaks) {
781
88.4k
        pgBreak = static_cast<int>(input->readULong(2));
782
88.4k
        f << pgBreak << ",";
783
88.4k
      }
784
11
      f << "],";
785
11
    }
786
    // columns/notes break?
787
16
    if (numColBreak) {
788
14
      f << "colBreak=[";
789
97.9k
      for (int i = 0; i < numColBreak; i++)
790
97.9k
        f << input->readULong(2) << ",";
791
14
      f << "],";
792
14
    }
793
16
    ascFile.addPos(pos);
794
16
    ascFile.addNote(f.str().c_str());
795
16
  }
796
797
94
  if (zoneSize[1]>0) {
798
    // only version<=2 seems to contains the print data with size 0x15e
799
9
    pos = input->tell();
800
9
    MWAWEntry zone;
801
9
    if (!m_document->readDocumentInfo(zoneSize[1])) {
802
9
      input->seek(pos+zoneSize[1], librevenge::RVNG_SEEK_SET);
803
9
      MWAW_DEBUG_MSG(("MsWksSSParser::readSSheetZone:can not read print info\n"));
804
9
      ascii().addPos(pos);
805
9
      ascii().addNote("SSheet[printInfo]:###");
806
9
    }
807
9
  }
808
809
94
  if (vers==2) {
810
31
    for (int i=0; i < numCharts; ++i) {
811
4
      pos=input->tell();
812
4
      if (!input->checkPosition(pos+256)) {
813
1
        MWAW_DEBUG_MSG(("MsWksSSParser::readSSheetZone:can not find chart %d data\n", i));
814
1
        input->seek(pos, librevenge::RVNG_SEEK_SET);
815
1
        return false;
816
1
      }
817
3
      f.str("");
818
3
      f << "Entries(Chart)[" << i << "]:";
819
3
      ascFile.addPos(pos);
820
3
      ascFile.addNote(f.str().c_str());
821
3
      input->seek(pos+256, librevenge::RVNG_SEEK_SET);
822
3
    }
823
28
  }
824
825
93
  long HDebPos = input->tell();
826
93
  f.str("");
827
93
  f << "SSheetH:" << std::dec;
828
93
  int numNote = 0;
829
119
  while (1) {
830
119
    pos = input->tell();
831
119
    auto idNote = static_cast<int>(input->readLong(2));
832
119
    if (idNote == 0) break;
833
65
    if (idNote != ++numNote) {
834
35
      input->seek(pos, librevenge::RVNG_SEEK_CUR);
835
35
      MWAW_DEBUG_MSG(("MsWksSSParser::readSSheetZone:Pb when reading a note\n"));
836
35
      return false;
837
35
    }
838
    // a pascal string
839
30
    auto pSz = static_cast<int>(input->readLong(2));
840
30
    MWAWEntry note;
841
30
    note.setBegin(input->tell());
842
30
    note.setLength(pSz);
843
30
    std::string str="";
844
15.3k
    for (int s = 0; s < pSz; s++)
845
15.3k
      str += char(input->readLong(1));
846
30
    if (input->tell() != pos+4+pSz) {
847
4
      input->seek(pos, librevenge::RVNG_SEEK_CUR);
848
4
      MWAW_DEBUG_MSG(("MsWksSSParser::readSSheetZone:Pb when reading a note\n"));
849
4
      return false;
850
4
    }
851
26
    if (pSz) {
852
25
      sheet.m_idNoteMap[idNote] = note;
853
25
      f << "Note" << idNote << "=\"" << str << "\",";
854
25
    }
855
26
  }
856
857
54
  ascFile.addPos(HDebPos);
858
54
  ascFile.addNote(f.str().c_str());
859
54
  return true;
860
93
}
861
862
bool MsWksSSParser::readCell(int sz, MWAWVec2i const &cellPos, MsWksSSParserInternal::Cell &cell)
863
4.31k
{
864
4.31k
  int const vers=version();
865
4.31k
  cell = MsWksSSParserInternal::Cell();
866
4.31k
  cell.setPosition(cellPos);
867
4.31k
  if (sz == 0xFF || sz < 5) return false;
868
869
4.28k
  MWAWInputStreamPtr input=m_document->getInput();
870
4.28k
  long debPos = input->tell();
871
872
4.28k
  libmwaw::DebugFile &ascFile=m_document->ascii();
873
4.28k
  libmwaw::DebugStream f;
874
4.28k
  long endPos = debPos+sz-5;
875
4.28k
  input->seek(endPos+5, librevenge::RVNG_SEEK_SET);
876
4.28k
  if (input->tell() !=endPos+5) return false;
877
878
  // read the flag
879
4.27k
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
880
4.27k
  int fl[5];
881
21.3k
  for (auto &flag : fl) flag = static_cast<int>(input->readULong(1));
882
4.27k
  MWAWCell::Format format;
883
4.27k
  MWAWCellContent &content=cell.m_content;
884
4.27k
  int style = 0, align = 0, subformat = 0;
885
4.27k
  if (vers >= 3) {
886
3.20k
    style = (fl[4]>>1) & 0x1f;
887
3.20k
    align = (fl[4]>>6);
888
3.20k
    fl[4] &= 1;
889
3.20k
    subformat = fl[2] >> 4;
890
3.20k
    cell.setBorders(fl[2] & 0xF, MWAWBorder()); // checkme
891
3.20k
    fl[2] = 0;
892
3.20k
  }
893
1.07k
  else {
894
1.07k
    style = fl[4] & 0x18;
895
1.07k
    align = (fl[4]>>5)&3;
896
1.07k
    subformat= (fl[4]&7);
897
1.07k
    fl[4] &= 0x80;
898
1.07k
  }
899
4.27k
  switch (align) {
900
1.22k
  case 0:
901
1.22k
    cell.setHAlignment(MWAWCell::HALIGN_LEFT);
902
1.22k
    break;
903
340
  case 1:
904
340
    cell.setHAlignment(MWAWCell::HALIGN_CENTER);
905
340
    break;
906
287
  case 2:
907
287
    cell.setHAlignment(MWAWCell::HALIGN_RIGHT);
908
287
    break;
909
0
  default:
910
2.42k
  case 3:
911
2.42k
    break; // default
912
4.27k
  }
913
4.27k
  cell.m_noteId = fl[1] & 0xf;
914
4.27k
  fl[1] >>= 4;
915
4.27k
  int type = fl[3] >> 4;
916
4.27k
  format.m_digits=(fl[3] & 0xf);
917
4.27k
  fl[3] &= 1;
918
919
4.27k
  MWAWFont font=m_state->m_spreadsheet.m_font;
920
4.27k
  MWAWColor col;
921
4.27k
  if (vers>=3&&m_document->getColor(fl[0],col,3)) {
922
3.20k
    font.setColor(col);
923
3.20k
    fl[0]=0;
924
3.20k
  }
925
4.27k
  uint32_t fflags = 0;
926
4.27k
  if (style) {
927
1.87k
    if (style & 1) fflags |= MWAWFont::shadowBit;
928
1.87k
    if (style & 2) fflags |= MWAWFont::embossBit;
929
1.87k
    if (style & 4) fflags |= MWAWFont::italicBit;
930
1.87k
    if (style & 8) font.setUnderlineStyle(MWAWFont::Line::Simple);
931
1.87k
    if (style & 16) fflags |= MWAWFont::boldBit;
932
1.87k
  }
933
4.27k
  font.setFlags(fflags);
934
4.27k
  cell.setFont(font);
935
936
4.27k
  content.m_contentType=MWAWCellContent::C_NONE;
937
4.27k
  if (type & 2) { // checkme: is this also ok for v2?
938
663
    cell.setProtected(true);
939
663
    type &= 0xFD;
940
663
  }
941
4.27k
  switch (type>>2) {
942
2.38k
  case 0:
943
2.38k
    content.m_contentType=MWAWCellContent::C_TEXT;
944
2.38k
    format.m_format=MWAWCell::F_TEXT;
945
2.38k
    break;
946
1.35k
  case 1:
947
1.35k
    format.m_format=MWAWCell::F_NUMBER;
948
1.35k
    content.m_contentType=MWAWCellContent::C_NUMBER;
949
1.35k
    break;
950
211
  case 3:
951
211
    f << "type" << type << ",";
952
211
    MWAW_FALLTHROUGH;
953
544
  case 2: //number general
954
544
    format.m_format=MWAWCell::F_NUMBER;
955
544
    content.m_contentType=MWAWCellContent::C_FORMULA;
956
544
    break;
957
0
  default:
958
0
    break;
959
4.27k
  }
960
4.27k
  if (vers <= 2 && format.m_format==MWAWCell::F_NUMBER) {
961
409
    switch (subformat) {
962
146
    case 0:
963
146
      format.m_numberFormat=MWAWCell::F_NUMBER_DECIMAL;
964
146
      break;
965
28
    case 1:
966
28
      format.m_numberFormat=MWAWCell::F_NUMBER_CURRENCY;
967
28
      break;
968
19
    case 2:
969
19
      format.m_numberFormat=MWAWCell::F_NUMBER_PERCENT;
970
19
      break;
971
77
    case 4:
972
77
      format.m_numberFormat=MWAWCell::F_NUMBER_SCIENTIFIC;
973
77
      break;
974
15
    case 5:
975
67
    case 6:
976
67
      format.m_format=MWAWCell::F_DATE;
977
67
      break;
978
28
    case 7:
979
28
      format.m_format=MWAWCell::F_TIME;
980
28
      break;
981
44
    case 3: // generic
982
44
    default:
983
44
      format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC;
984
44
      break;
985
409
    }
986
409
  }
987
3.86k
  else if (vers>2) {
988
3.20k
    if (type&1) {
989
492
      format.m_format= (subformat < 4) ? MWAWCell::F_TIME : MWAWCell::F_DATE;
990
492
      fl[3] = 0;
991
492
    }
992
3.20k
    switch (format.m_format) {
993
1.20k
    case  MWAWCell::F_NUMBER:
994
1.20k
      format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC;
995
1.20k
      switch (subformat) {
996
674
      case 0:
997
674
        break;
998
109
      case 1:
999
109
        format.m_numberFormat=MWAWCell::F_NUMBER_DECIMAL;
1000
109
        break;
1001
78
      case 2:
1002
78
        format.m_numberFormat=MWAWCell::F_NUMBER_CURRENCY;
1003
78
        break;
1004
57
      case 3:
1005
57
        format.m_thousandHasSeparator=true;
1006
57
        break;
1007
22
      case 4:
1008
22
        format.m_numberFormat=MWAWCell::F_NUMBER_CURRENCY;
1009
22
        format.m_thousandHasSeparator=true;
1010
22
        break;
1011
70
      case 5:
1012
70
        format.m_numberFormat=MWAWCell::F_NUMBER_SCIENTIFIC;
1013
70
        break;
1014
76
      case 6:
1015
76
        format.m_numberFormat=MWAWCell::F_NUMBER_PERCENT;
1016
76
        break;
1017
5
      case 7:
1018
5
        format.m_numberFormat=MWAWCell::F_NUMBER_PERCENT;
1019
5
        format.m_thousandHasSeparator=true;
1020
5
        break;
1021
115
      default:
1022
115
        f << ",subform==##unkn" << subformat;
1023
115
        break;
1024
1.20k
      }
1025
1.20k
      break;
1026
1.20k
    case MWAWCell::F_TIME:
1027
251
      if (subformat >= 0 && subformat < 4) {
1028
251
        static char const* const wh[]= {"%I:%M:%S %p", "%I:%M %p", "%H:%M:%S", "%H:%M"};
1029
251
        format.m_DTFormat=wh[subformat];
1030
251
      }
1031
0
      else
1032
0
        f << ",subform==##unkn" << subformat;
1033
251
      break;
1034
241
    case MWAWCell::F_DATE:
1035
241
      switch (subformat) {
1036
49
      case 4:
1037
58
      case 5:
1038
78
      case 6:
1039
111
      case 7:
1040
116
      case 8: {
1041
116
        static char const* const wh[]= {"%m/%d/%y", "%b %d, %Y", "%b, %d", "%b, %Y", "%a, %d %b, %Y" };
1042
116
        format.m_DTFormat=wh[subformat-4];
1043
116
        break;
1044
111
      }
1045
6
      case 10:
1046
11
      case 11:
1047
11
        format.m_DTFormat="%B %d %Y";
1048
11
        break;
1049
22
      case 12:
1050
54
      case 13:
1051
54
        format.m_DTFormat="%A, %B %d, %Y";
1052
54
        break;
1053
60
      default:
1054
60
        f << ",subform==##unkn" << subformat;
1055
60
        break;
1056
241
      }
1057
241
      break;
1058
1.50k
    case MWAWCell::F_TEXT:
1059
1.50k
      break;
1060
0
    case MWAWCell::F_BOOLEAN:
1061
0
    case MWAWCell::F_UNKNOWN:
1062
0
      MWAW_DEBUG_MSG(("MsWksSSParser::readCell: unexpected format\n"));
1063
0
      break;
1064
#if !defined(__clang__)
1065
    default:
1066
      f << ",subform==##unkn" << subformat;
1067
      break;
1068
#endif
1069
3.20k
    }
1070
3.20k
  }
1071
4.27k
  long pos = debPos;
1072
4.27k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1073
1074
4.27k
  if (pos != endPos) {
1075
4.13k
    bool ok = true;
1076
4.13k
    switch (content.m_contentType) {
1077
2.24k
    case MWAWCellContent::C_TEXT: {
1078
2.24k
      content.m_textEntry.setBegin(pos);
1079
2.24k
      content.m_textEntry.setEnd(endPos);
1080
2.24k
      if (content.m_textEntry.length()>=0) {
1081
2.24k
        std::string text("");
1082
56.4k
        for (long i=0; i <content.m_textEntry.length(); ++i)
1083
54.2k
          text += char(input->readULong(1));
1084
2.24k
        f << text << ",";
1085
2.24k
      }
1086
0
      else
1087
0
        f << "###text,";
1088
2.24k
      break;
1089
0
    }
1090
1.34k
    case MWAWCellContent::C_NUMBER: {
1091
1.34k
      double val;
1092
1.34k
      bool isNan;
1093
1.34k
      std::string str;
1094
1.34k
      if (!m_document->readDBNumber(endPos, val, isNan, str)) {
1095
230
        ok=false;
1096
230
        f << "###number";
1097
230
      }
1098
1.11k
      else
1099
1.11k
        content.setValue(val);
1100
1.34k
      f << val;
1101
1.34k
      if (!str.empty()) f << "[" << str << "],";
1102
1.34k
      break;
1103
0
    }
1104
544
    case MWAWCellContent::C_FORMULA: {
1105
544
      std::string extra("");
1106
544
      if (!m_document->readFormula(endPos, content, extra)) {
1107
278
        ok=false;
1108
278
        f << "###";
1109
278
      }
1110
544
      f << extra;
1111
544
      break;
1112
0
    }
1113
0
    case MWAWCellContent::C_NONE:
1114
0
      break;
1115
0
    case MWAWCellContent::C_UNKNOWN:
1116
#if !defined(__clang__)
1117
    default:
1118
#endif
1119
0
      ok = false;
1120
4.13k
    }
1121
4.13k
    if (!ok)
1122
508
      f << ",###pb";
1123
4.13k
  }
1124
4.27k
  cell.setFormat(format);
1125
  // change the reference date from 1/1/1904 to 1/1/1900
1126
4.27k
  if (format.m_format==MWAWCell::F_DATE && content.isValueSet())
1127
116
    content.setValue(content.m_value+1460.);
1128
1129
4.27k
  std::string extra = f.str();
1130
4.27k
  f.str("");
1131
4.27k
  f << "SSheetE:" << cell << ",unk=[" << std::hex;
1132
21.3k
  for (auto flag : fl) {
1133
21.3k
    if (!flag) f << "_,";
1134
3.81k
    else f << flag << ",";
1135
21.3k
  }
1136
4.27k
  f << "]" << std::dec;
1137
4.27k
  if (extra.size()) f << ", extra=" << extra;
1138
1139
1140
4.27k
  pos = input->tell();
1141
4.27k
  if (pos != endPos) {
1142
664
    ascFile.addDelimiter(pos,'[');
1143
664
    ascFile.addDelimiter(endPos,']');
1144
664
  }
1145
1146
4.27k
  ascFile.addPos(debPos-1);
1147
4.27k
  ascFile.addNote(f.str().c_str());
1148
4.27k
  input->seek(endPos+5, librevenge::RVNG_SEEK_SET);
1149
4.27k
  return true;
1150
4.27k
}
1151
1152
////////////////////////////////////////////////////////////
1153
////////////////////////////////////////////////////////////
1154
bool MsWksSSParser::sendSpreadsheet()
1155
265
{
1156
265
  MWAWSpreadsheetListenerPtr listener=getSpreadsheetListener();
1157
265
  MWAWInputStreamPtr &input= m_document->getInput();
1158
265
  if (!listener) {
1159
0
    MWAW_DEBUG_MSG(("MsWksSSParser::sendSpreadsheet: I can not find the listener\n"));
1160
0
    return false;
1161
0
  }
1162
265
  auto &sheet = m_state->m_spreadsheet;
1163
1164
265
  int prevRow = -1;
1165
265
  listener->openSheet(sheet.convertInPoint(sheet.m_widthCols,76), librevenge::RVNG_POINT, std::vector<int>(), sheet.m_name);
1166
  // fixme: this sends the pictures present in the main page, but not the picture present in header/footer
1167
265
  auto zone=m_document->getZone(MsWksDocument::Z_MAIN);
1168
265
  m_document->getGraphParser()->sendAll(zone.m_zoneId, true);
1169
1170
4.23k
  for (auto const &cell : sheet.m_cells) {
1171
    // FIXME: find the row height...
1172
4.23k
    if (cell.position()[1]>prevRow+1) {
1173
206
      if (prevRow != -1) listener->closeSheetRow();
1174
206
      int numRepeat=cell.position()[1]-1-prevRow;
1175
206
      listener->openSheetRow(16, librevenge::RVNG_POINT, numRepeat);
1176
206
      prevRow+=numRepeat;
1177
206
    }
1178
4.23k
    if (cell.position()[1] > prevRow) {
1179
1.03k
      if (prevRow != -1) listener->closeSheetRow();
1180
1.03k
      ++prevRow;
1181
1.03k
      listener->openSheetRow(16, librevenge::RVNG_POINT);
1182
1.03k
    }
1183
4.23k
    listener->openSheetCell(cell, cell.m_content);
1184
4.23k
    if (cell.m_content.m_textEntry.valid()) {
1185
2.24k
      listener->setFont(cell.isFontSet() ? cell.getFont() : sheet.m_font);
1186
2.24k
      input->seek(cell.m_content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
1187
56.4k
      while (!input->isEnd() && input->tell()<cell.m_content.m_textEntry.end()) {
1188
54.2k
        auto c=static_cast<unsigned char>(input->readULong(1));
1189
54.2k
        if (c==0xd)
1190
1.68k
          listener->insertEOL();
1191
52.5k
        else
1192
52.5k
          listener->insertCharacter(c);
1193
54.2k
      }
1194
2.24k
    }
1195
4.23k
    if (cell.m_noteId>0) {
1196
1.37k
      MWAWSubDocumentPtr subDoc(new MsWksSSParserInternal::SubDocument(*this, input, cell.m_noteId));
1197
1.37k
      listener->insertComment(subDoc);
1198
1.37k
    }
1199
4.23k
    listener->closeSheetCell();
1200
4.23k
  }
1201
265
  if (prevRow!=-1) listener->closeSheetRow();
1202
265
  listener->closeSheet();
1203
265
  return true;
1204
265
}
1205
1206
////////////////////////////////////////////////////////////
1207
//
1208
// Low level
1209
//
1210
////////////////////////////////////////////////////////////
1211
1212
////////////////////////////////////////////////////////////
1213
// read the header
1214
////////////////////////////////////////////////////////////
1215
bool MsWksSSParser::checkHeader(MWAWHeader *header, bool strict)
1216
17.2k
{
1217
17.2k
  *m_state = MsWksSSParserInternal::State();
1218
17.2k
  if (!m_document || !m_document->checkHeader3(header, strict)) return false;
1219
13.3k
  if (m_document->getKind() != MWAWDocument::MWAW_K_SPREADSHEET)
1220
12
    return false;
1221
13.3k
#ifndef DEBUG
1222
13.3k
  if (version() == 1)
1223
300
    return false;
1224
13.0k
#endif
1225
13.0k
  return true;
1226
13.3k
}
1227
1228
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: