Coverage Report

Created: 2026-04-29 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/MultiplanParser.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 <cmath>
35
#include <cstring>
36
#include <iomanip>
37
#include <iostream>
38
#include <limits>
39
#include <map>
40
#include <set>
41
#include <sstream>
42
43
#include <librevenge/librevenge.h>
44
45
#include "MWAWCell.hxx"
46
#include "MWAWFont.hxx"
47
#include "MWAWFontConverter.hxx"
48
#include "MWAWHeader.hxx"
49
#include "MWAWParagraph.hxx"
50
#include "MWAWPosition.hxx"
51
#include "MWAWSpreadsheetListener.hxx"
52
#include "MWAWSubDocument.hxx"
53
54
#include "MultiplanParser.hxx"
55
56
/** Internal: the structures of a MultiplanParser */
57
namespace MultiplanParserInternal
58
{
59
////////////////////////////////////////
60
//! Internal: the state of a MultiplanParser
61
struct State {
62
  //! constructor
63
  State()
64
12.8k
    : m_font()
65
12.8k
    , m_maximumCell()
66
12.8k
    , m_columnPositions()
67
12.8k
    , m_cellPositions()
68
12.8k
    , m_cellPositionsSet()
69
12.8k
    , m_posToLinkMap()
70
12.8k
    , m_posToNameMap()
71
12.8k
    , m_posToSharedDataSeen()
72
12.8k
  {
73
12.8k
  }
74
  //! returns the column width in point
75
  std::vector<float> getColumnsWidth() const;
76
  //! the default font
77
  MWAWFont m_font;
78
  //! the maximumCell
79
  MWAWVec2i m_maximumCell;
80
  //! the columns begin position in point
81
  std::vector<int> m_columnPositions;
82
  //! the header/footer/printer message entries
83
  MWAWEntry m_hfpEntries[3];
84
  //! the positions of each cell: a vector for each row
85
  std::vector<std::vector<int> > m_cellPositions;
86
  //! the list of all position (use for checking)
87
  std::set<int> m_cellPositionsSet;
88
  //! the different main spreadsheet zones
89
  MWAWEntry m_entries[9];
90
  //! the list of link instruction
91
  std::map<int, MWAWCellContent::FormulaInstruction> m_posToLinkMap;
92
  //! the map name's pos to name's cell instruction
93
  std::map<int, MWAWCellContent::FormulaInstruction> m_posToNameMap;
94
  //! a set a shared data already seen
95
  std::set<int> m_posToSharedDataSeen;
96
};
97
98
std::vector<float> State::getColumnsWidth() const
99
1.82k
{
100
1.82k
  std::vector<float> res;
101
1.82k
  bool first=true;
102
1.82k
  int lastPos=0;
103
1.82k
  float const defWidth=64.f;
104
116k
  for (auto p : m_columnPositions) {
105
116k
    if (first) {
106
1.82k
      first=false;
107
1.82k
      continue;
108
1.82k
    }
109
114k
    if (p<lastPos)
110
17.0k
      res.push_back(defWidth);
111
97.5k
    else
112
97.5k
      res.push_back(float(p-lastPos));
113
114k
    lastPos=p;
114
114k
  }
115
1.82k
  if (res.size()<64) res.resize(64, defWidth);
116
1.82k
  return res;
117
1.82k
}
118
119
////////////////////////////////////////
120
//! Internal: the subdocument of a MultiplanParserInternal
121
class SubDocument final : public MWAWSubDocument
122
{
123
public:
124
  SubDocument(MultiplanParser &parser, MWAWInputStreamPtr const &input, MWAWEntry const &entry)
125
3.12k
    : MWAWSubDocument(&parser, input, entry)
126
3.12k
    , m_multiParser(parser)
127
3.12k
  {
128
3.12k
  }
129
130
  //! destructor
131
0
  ~SubDocument() final {}
132
133
  //! operator!=
134
  bool operator!=(MWAWSubDocument const &doc) const final;
135
136
  //! the parser function
137
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
138
139
protected:
140
  /** the main parser */
141
  MultiplanParser &m_multiParser;
142
};
143
144
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
145
3.12k
{
146
3.12k
  if (!listener.get()) {
147
0
    MWAW_DEBUG_MSG(("MultiplanParser::SubDocument::parse: no listener\n"));
148
0
    return;
149
0
  }
150
151
3.12k
  long pos = m_input->tell();
152
3.12k
  m_multiParser.sendText(m_zone);
153
3.12k
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
154
3.12k
}
155
156
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
157
0
{
158
0
  if (MWAWSubDocument::operator!=(doc)) return true;
159
0
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
160
0
  if (!sDoc) return true;
161
0
  if (&m_multiParser != &sDoc->m_multiParser) return true;
162
0
  return false;
163
0
}
164
}
165
166
////////////////////////////////////////////////////////////
167
// constructor/destructor, ...
168
////////////////////////////////////////////////////////////
169
MultiplanParser::MultiplanParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
170
5.49k
  : MWAWSpreadsheetParser(input, rsrcParser, header)
171
5.49k
  , m_state(new MultiplanParserInternal::State)
172
5.49k
{
173
5.49k
  setAsciiName("main-1");
174
  // reduce the margin (in case, the page is not defined)
175
5.49k
  getPageSpan().setMargins(0.1);
176
5.49k
}
177
178
MultiplanParser::~MultiplanParser()
179
5.49k
{
180
5.49k
}
181
182
////////////////////////////////////////////////////////////
183
// the parser
184
////////////////////////////////////////////////////////////
185
void MultiplanParser::parse(librevenge::RVNGSpreadsheetInterface *docInterface)
186
1.82k
{
187
1.82k
  if (!getInput().get() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
188
1.82k
  bool ok = true;
189
1.82k
  try {
190
    // create the asciiFile
191
1.82k
    ascii().setStream(getInput());
192
1.82k
    ascii().open(asciiName());
193
1.82k
    checkHeader(nullptr);
194
1.82k
    ok = createZones();
195
1.82k
    if (ok) {
196
1.82k
      createDocument(docInterface);
197
1.82k
      sendSpreadsheet();
198
1.82k
    }
199
1.82k
  }
200
1.82k
  catch (...) {
201
0
    MWAW_DEBUG_MSG(("MultiplanParser::parse: exception catched when parsing\n"));
202
0
    ok = false;
203
0
  }
204
205
1.82k
  ascii().reset();
206
1.82k
  resetSpreadsheetListener();
207
1.82k
  if (!ok) throw(libmwaw::ParseException());
208
1.82k
}
209
210
////////////////////////////////////////////////////////////
211
// create the document
212
////////////////////////////////////////////////////////////
213
void MultiplanParser::createDocument(librevenge::RVNGSpreadsheetInterface *documentInterface)
214
1.82k
{
215
1.82k
  if (!documentInterface) return;
216
1.82k
  if (getSpreadsheetListener()) {
217
0
    MWAW_DEBUG_MSG(("MultiplanParser::createDocument: listener already exist\n"));
218
0
    return;
219
0
  }
220
221
  // create the page list
222
1.82k
  MWAWPageSpan ps(getPageSpan());
223
1.82k
  ps.setPageSpan(1);
224
5.46k
  for (int i=0; i<2; ++i) {
225
3.64k
    if (!m_state->m_hfpEntries[i].valid()) continue;
226
3.12k
    MWAWHeaderFooter header(i==0 ? MWAWHeaderFooter::HEADER :  MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
227
3.12k
    header.m_subDocument.reset
228
3.12k
    (new MultiplanParserInternal::SubDocument
229
3.12k
     (*this, getInput(), m_state->m_hfpEntries[i]));
230
3.12k
    ps.setHeaderFooter(header);
231
3.12k
  }
232
1.82k
  std::vector<MWAWPageSpan> pageList(1,ps);
233
  //
234
1.82k
  MWAWSpreadsheetListenerPtr listen(new MWAWSpreadsheetListener(*getParserState(), pageList, documentInterface));
235
1.82k
  setSpreadsheetListener(listen);
236
1.82k
  listen->startDocument();
237
1.82k
}
238
239
240
////////////////////////////////////////////////////////////
241
//
242
// Intermediate level
243
//
244
////////////////////////////////////////////////////////////
245
bool MultiplanParser::createZones()
246
1.82k
{
247
1.82k
  if (!readPrinterMessage() || !readZoneB()) return false;
248
1.82k
  if (!readColumnsPos() || !readPrinterInfo()) return false;
249
1.82k
  if (!readHeaderFooter() || !readZoneC()) return false;
250
1.82k
  if (!readZonesList()) return false;
251
1.82k
  MWAWInputStreamPtr input = getInput();
252
1.82k
  if (!input->isEnd()) {
253
1.39k
    MWAW_DEBUG_MSG(("MultiplanParser::createZones: find extra data\n"));
254
1.39k
    ascii().addPos(input->tell());
255
1.39k
    ascii().addNote("Entries(Unknown):###extra");
256
1.39k
  }
257
1.82k
  return true;
258
1.82k
}
259
260
////////////////////////////////////////////////////////////
261
//
262
// Low level
263
//
264
////////////////////////////////////////////////////////////
265
266
////////////////////////////////////////////////////////////
267
// spreadsheet
268
////////////////////////////////////////////////////////////
269
bool MultiplanParser::readHeaderFooter()
270
1.82k
{
271
1.82k
  MWAWInputStreamPtr input = getInput();
272
1.82k
  long pos = input->tell();
273
1.82k
  if (!input->checkPosition(pos+2*256)) {
274
0
    MWAW_DEBUG_MSG(("MultiplanParser::readHeaderFooter: the zone seems too short\n"));
275
0
    return false;
276
0
  }
277
1.82k
  libmwaw::DebugStream f;
278
5.46k
  for (int i=0; i<2; ++i) {
279
3.64k
    pos = input->tell();
280
3.64k
    f.str("");
281
3.64k
    f << "Entries(HF)[" << (i==0 ? "header" : "footer") << "]:";
282
3.64k
    int sSz=int(input->readULong(1));
283
3.64k
    m_state->m_hfpEntries[i].setBegin(pos+1);
284
3.64k
    m_state->m_hfpEntries[i].setLength(sSz);
285
3.64k
    std::string name;
286
445k
    for (int c=0; c<sSz; ++c) name+=char(input->readULong(1));
287
3.64k
    f << name;
288
3.64k
    ascii().addPos(pos);
289
3.64k
    ascii().addNote(f.str().c_str());
290
3.64k
    input->seek(pos+256, librevenge::RVNG_SEEK_SET);
291
3.64k
  }
292
1.82k
  return true;
293
1.82k
}
294
295
bool MultiplanParser::readPrinterInfo()
296
1.82k
{
297
1.82k
  MWAWInputStreamPtr input = getInput();
298
1.82k
  long pos = input->tell();
299
1.82k
  if (!input->checkPosition(pos+0xbc)) {
300
0
    MWAW_DEBUG_MSG(("MultiplanParser::readPrinterInfo: the zone seems too short\n"));
301
0
    return false;
302
0
  }
303
1.82k
  libmwaw::DebugStream f;
304
1.82k
  f << "Entries(PrinterInfo):";
305
1.82k
  int val=int(input->readULong(2));
306
1.82k
  if (val!=0x7fff) f << "f0=" << val << ",";
307
1.82k
  val=int(input->readULong(2));
308
1.82k
  if (val) f << "f1=" << val << ",";
309
1.82k
  f << "left[margin]=" << input->readULong(1) << ",";
310
1.82k
  f << "width=" << input->readULong(1) << ",";
311
1.82k
  f << "right[margin]=" << input->readULong(1) << ",";
312
1.82k
  f << "length=" << input->readULong(1) << ",";
313
  // then 0 and a string?
314
1.82k
  ascii().addDelimiter(input->tell(),'|');
315
1.82k
  ascii().addPos(pos);
316
1.82k
  ascii().addNote(f.str().c_str());
317
1.82k
  input->seek(pos+130, librevenge::RVNG_SEEK_SET);
318
319
1.82k
  pos=input->tell();
320
1.82k
  f.str("");
321
1.82k
  f << "PrinterInfo[II]:";
322
1.82k
  f << "row[pbBreak]=[";
323
60.0k
  for (int i=0; i<32; ++i) {
324
58.2k
    val=int(input->readULong(1));
325
58.2k
    if (!val) continue;
326
276k
    for (int d=0, depl=1; d<8; ++d, depl<<=1) {
327
246k
      if (val&depl) f << i*8+d << ",";
328
246k
    }
329
30.7k
  }
330
1.82k
  f << "],";
331
1.82k
  f << "col[pbBreak]=[";
332
16.3k
  for (int i=0; i<8; ++i) {
333
14.5k
    val=int(input->readULong(1));
334
14.5k
    if (!val) continue;
335
54.1k
    for (int d=0, depl=1; d<8; ++d, depl<<=1) {
336
48.1k
      if (val&depl) f << i*8+d << ",";
337
48.1k
    }
338
6.01k
  }
339
1.82k
  f << "],";
340
14.5k
  for (int i=0; i<7; ++i) {
341
12.7k
    val=int(input->readULong(2));
342
12.7k
    int const expected[]= {0x48,0x48,0x36,0x36,1,1,0};
343
12.7k
    if (val==expected[i]) continue;
344
9.23k
    if (i==4) {
345
1.38k
      if (val==0)
346
550
        f << "print[col,row,number]=no,";
347
831
      else
348
831
        f << "##print[col,row,number]=" << val << ",";
349
1.38k
    }
350
7.85k
    else
351
7.85k
      f << "g" << i << "=" << val << ",";
352
9.23k
  }
353
1.82k
  m_state->m_font.setId(int(input->readULong(2)));
354
1.82k
  m_state->m_font.setSize(float(input->readULong(2)));
355
1.82k
  f << "font=[" << m_state->m_font.getDebugString(getFontConverter()) << "],";
356
1.82k
  ascii().addPos(pos);
357
1.82k
  ascii().addNote(f.str().c_str());
358
1.82k
  input->seek(pos+58, librevenge::RVNG_SEEK_SET);
359
1.82k
  return true;
360
1.82k
}
361
362
bool MultiplanParser::readPrinterMessage()
363
1.82k
{
364
1.82k
  MWAWInputStreamPtr input = getInput();
365
1.82k
  long pos = input->tell();
366
1.82k
  if (!input->checkPosition(pos+256)) {
367
0
    MWAW_DEBUG_MSG(("MultiplanParser::readPrinterMessage: the zone seems too short\n"));
368
0
    return false;
369
0
  }
370
1.82k
  libmwaw::DebugStream f;
371
1.82k
  f << "Entries(HF)[printerMessage]:";
372
1.82k
  int sSz=int(input->readULong(1));
373
1.82k
  m_state->m_hfpEntries[2].setBegin(pos+1);
374
1.82k
  m_state->m_hfpEntries[2].setLength(sSz);
375
1.82k
  std::string name;
376
221k
  for (int c=0; c<sSz; ++c) name+=char(input->readULong(1));
377
1.82k
  f << name;
378
1.82k
  ascii().addPos(pos);
379
1.82k
  ascii().addNote(f.str().c_str());
380
1.82k
  input->seek(pos+256, librevenge::RVNG_SEEK_SET);
381
1.82k
  return true;
382
1.82k
}
383
384
bool MultiplanParser::readColumnsPos()
385
1.82k
{
386
1.82k
  MWAWInputStreamPtr input = getInput();
387
1.82k
  long pos = input->tell();
388
1.82k
  if (!input->checkPosition(pos+256)) {
389
0
    MWAW_DEBUG_MSG(("MultiplanParser::readColumnsPos: the zone seems too short\n"));
390
0
    return false;
391
0
  }
392
1.82k
  libmwaw::DebugStream f;
393
1.82k
  f << "Entries(ColPos):pos=[";
394
118k
  for (int i=0; i<64; ++i) {
395
116k
    m_state->m_columnPositions.push_back(int(input->readULong(2)));
396
116k
    f << m_state->m_columnPositions.back() << ",";
397
116k
  }
398
1.82k
  f << "],";
399
1.82k
  ascii().addPos(pos);
400
1.82k
  ascii().addNote(f.str().c_str());
401
1.82k
  return true;
402
1.82k
}
403
404
bool MultiplanParser::readZonesList()
405
1.82k
{
406
1.82k
  MWAWInputStreamPtr input = getInput();
407
1.82k
  long pos = input->tell();
408
1.82k
  if (!input->checkPosition(pos+20)) {
409
0
    MWAW_DEBUG_MSG(("MultiplanParser::readZonesList: the zone seems too short\n"));
410
0
    return false;
411
0
  }
412
1.82k
  libmwaw::DebugStream f;
413
1.82k
  f << "Entries(ZonesList):";
414
1.82k
  int lastPos=0;
415
1.82k
  f << "zones=[";
416
20.0k
  for (int i=0, w=0; i<10; ++i) {
417
18.2k
    int newPos=int(input->readULong(2));
418
18.2k
    if (i==6) newPos+=lastPos; // length
419
18.2k
    if (i==7) {
420
1.82k
      lastPos=newPos;
421
1.82k
      continue;
422
1.82k
    }
423
16.3k
    if (newPos>lastPos) {
424
8.22k
      if (!input->checkPosition(pos+20+newPos)) {
425
516
        MWAW_DEBUG_MSG(("MultiplanParser::readZonesList: find a bad position"));
426
516
        f << "###";
427
516
      }
428
7.70k
      else {
429
7.70k
        m_state->m_entries[w].setBegin(pos+20+lastPos);
430
7.70k
        m_state->m_entries[w].setEnd(pos+20+newPos);
431
7.70k
      }
432
8.22k
      f << std::hex << lastPos << "<->" << newPos << std::dec << ",";
433
8.22k
      lastPos=newPos;
434
8.22k
    }
435
8.15k
    else
436
8.15k
      f << "_,";
437
16.3k
    ++w;
438
16.3k
  }
439
1.82k
  f << "],";
440
1.82k
  ascii().addPos(pos);
441
1.82k
  ascii().addNote(f.str().c_str());
442
18.2k
  for (int i=0; i<9; ++i) {
443
16.3k
    if (!m_state->m_entries[i].valid()) continue;
444
7.70k
    bool ok=false;
445
7.70k
    std::string name;
446
7.70k
    switch (i) {
447
624
    case 1:
448
624
      ok=readZone1(m_state->m_entries[i]);
449
624
      break;
450
1.65k
    case 3:
451
1.65k
      ok=readCellDataPosition(m_state->m_entries[i]);
452
1.65k
      break;
453
1.03k
    case 4:
454
1.03k
      name="Link";
455
1.03k
      break;
456
740
    case 5:
457
740
      name="Link";
458
740
      break;
459
1.62k
    case 6:
460
1.62k
      name="DataCell";
461
1.62k
      break;
462
1.03k
    case 7: // the data are normally read in zone 6
463
1.03k
      name="SharedData";
464
1.03k
      break;
465
539
    case 8:
466
539
      name="Names";
467
539
      break;
468
459
    default:
469
459
      ok=false;
470
459
      break;
471
7.70k
    }
472
7.70k
    if (ok)
473
2.08k
      continue;
474
5.62k
    f.str("");
475
5.62k
    if (!name.empty())
476
4.96k
      f << "Entries(" << name << "):";
477
652
    else
478
652
      f << "Entries(Zone" << i << "):";
479
5.62k
    ascii().addPos(m_state->m_entries[i].begin());
480
5.62k
    ascii().addNote(f.str().c_str());
481
5.62k
    ascii().addPos(m_state->m_entries[i].end());
482
5.62k
    ascii().addNote("_");
483
5.62k
    input->seek(m_state->m_entries[i].end(), librevenge::RVNG_SEEK_SET);
484
5.62k
  }
485
1.82k
  return true;
486
1.82k
}
487
488
bool MultiplanParser::readZone1(MWAWEntry const &entry)
489
624
{
490
624
  if (entry.length()%30) {
491
174
    MWAW_DEBUG_MSG(("MultiplanParser::readZone1: the zone size seems bad\n"));
492
174
    return false;
493
174
  }
494
450
  MWAWInputStreamPtr input = getInput();
495
450
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
496
450
  libmwaw::DebugStream f;
497
450
  f << "Entries(Zone1):";
498
450
  ascii().addPos(entry.begin());
499
450
  ascii().addNote(f.str().c_str());
500
450
  int N=int(entry.length()/30);
501
4.92k
  for (int i=0; i<N; ++i) {
502
    /* find something like that
503
       0000000000fb01d80012001c00fb01d800000000000d0007ffe4ffeec000
504
       00000000005500550012001c005500550000000000040001ffe4ffeec000
505
    */
506
4.47k
    long pos=input->tell();
507
4.47k
    f.str("");
508
4.47k
    f << "Zone1-" << i << ":";
509
4.47k
    ascii().addPos(pos);
510
4.47k
    ascii().addNote(f.str().c_str());
511
4.47k
    input->seek(pos+30, librevenge::RVNG_SEEK_SET);
512
4.47k
  }
513
450
  return true;
514
624
}
515
516
bool MultiplanParser::readCellDataPosition(MWAWEntry const &entry)
517
1.65k
{
518
1.65k
  if (m_state->m_maximumCell[0]<=0 || m_state->m_maximumCell[1]<=0 || entry.length()/m_state->m_maximumCell[0]/2<m_state->m_maximumCell[1]) {
519
19
    MWAW_DEBUG_MSG(("MultiplanParser::readCellDataPosition: the zone seems bad\n"));
520
19
    return false;
521
19
  }
522
1.63k
  MWAWInputStreamPtr input = getInput();
523
1.63k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
524
1.63k
  libmwaw::DebugStream f;
525
1.63k
  f << "Entries(DataPos):";
526
1.63k
  m_state->m_cellPositions.resize(size_t(m_state->m_maximumCell[0]));
527
1.63k
  auto &posSet=m_state->m_cellPositionsSet;
528
327k
  for (int i=0; i<m_state->m_maximumCell[0]; ++i) {
529
326k
    f << "[" << std::hex;
530
326k
    auto &cellPos=m_state->m_cellPositions[size_t(i)];
531
7.67M
    for (int j=0; j<m_state->m_maximumCell[1]; ++j) {
532
7.34M
      cellPos.push_back(int(input->readLong(2)));
533
7.34M
      posSet.insert(cellPos.back());
534
7.34M
      if (cellPos.back())
535
5.91M
        f << cellPos.back() << ",";
536
1.43M
      else
537
1.43M
        f << "_,";
538
7.34M
    }
539
326k
    f << std::dec << "],";
540
326k
  }
541
1.63k
  if (input->tell()!=entry.end()) {
542
1.20k
    MWAW_DEBUG_MSG(("MultiplanParser::readCellDataPosition: find extra data\n"));
543
1.20k
    f << "###extra";
544
1.20k
    ascii().addDelimiter(input->tell(),'|');
545
1.20k
  }
546
1.63k
  ascii().addPos(entry.begin());
547
1.63k
  ascii().addNote(f.str().c_str());
548
1.63k
  return true;
549
1.65k
}
550
551
bool MultiplanParser::readLink(int pos, MWAWCellContent::FormulaInstruction &instr)
552
99.4k
{
553
99.4k
  auto it=m_state->m_posToLinkMap.find(pos);
554
99.4k
  if (it!=m_state->m_posToLinkMap.end()) {
555
16.4k
    instr=it->second;
556
16.4k
    return true;
557
16.4k
  }
558
83.0k
  auto const &entry=m_state->m_entries[4];
559
83.0k
  if (!entry.valid() || pos<0 || pos+12>entry.length()) {
560
74.6k
    MWAW_DEBUG_MSG(("MultiplanParser::readLink: the pos %d seems bad\n", pos));
561
74.6k
    return false;
562
74.6k
  }
563
8.34k
  auto input = getInput();
564
8.34k
  long actPos=input->tell();
565
8.34k
  long begPos=entry.begin()+pos;
566
8.34k
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
567
8.34k
  libmwaw::DebugStream f;
568
8.34k
  f << "Link-" << std::hex << pos << std::dec << "[pos]:";
569
8.34k
  int dSz=int(input->readULong(1));
570
8.34k
  if (dSz<0 || pos+12+dSz > entry.end()) {
571
0
    MWAW_DEBUG_MSG(("MultiplanParser::readLink: the pos %d seems bad\n", pos));
572
0
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
573
0
    return false;
574
0
  }
575
8.34k
  int type=int(input->readULong(1));
576
8.34k
  f << "type=" << type << ",";
577
8.34k
  int lPos=int(input->readULong(2));
578
8.34k
  if (!readLinkFilename(lPos, instr))
579
4.05k
    f << "###";
580
8.34k
  f << "pos=" << std::hex << lPos << std::dec << ",";
581
8.34k
  int val;
582
25.0k
  for (int j=0; j<2; ++j) {
583
16.6k
    val=int(input->readULong(1));
584
16.6k
    int const expected[]= {0x1a,0x1a};
585
16.6k
    if (val!=expected[j]) f << "f" << j+2 << "=" << val << ",";
586
16.6k
  }
587
33.3k
  for (int j=0; j<3; ++j) {  // f4=1|821
588
25.0k
    val=int(input->readULong(2));
589
25.0k
    if (val)
590
16.0k
      f << "f" << j+4 << "=" << std::hex << val << std::dec << ",";
591
25.0k
  }
592
8.34k
  bool ok=false;
593
8.34k
  switch (type) {
594
1.19k
  case 0: {
595
1.19k
    ok=true;
596
1.19k
    auto fontConverter=getFontConverter();
597
1.19k
    auto const fId=m_state->m_font.id();
598
1.19k
    librevenge::RVNGString name=instr.m_fileName;
599
1.19k
    name.append(':');
600
29.0k
    for (int i=0; i<dSz; ++i) {
601
27.8k
      auto ch=static_cast<unsigned char>(input->readULong(1));
602
27.8k
      int unicode = fontConverter->unicode(fId, static_cast<unsigned char>(ch));
603
27.8k
      if (unicode!=-1)
604
7.02k
        libmwaw::appendUnicode(uint32_t(unicode), name);
605
20.7k
      else if (ch==0x9 || ch > 0x1f)
606
2.60k
        libmwaw::appendUnicode(static_cast<uint32_t>(ch), name);
607
18.1k
      else {
608
18.1k
        f << "##";
609
18.1k
        MWAW_DEBUG_MSG(("MultiplanParser::readLink: name seems bad\n"));
610
18.1k
      }
611
27.8k
    }
612
1.19k
    instr.m_type=instr.F_Text;
613
1.19k
    instr.m_content=name.cstr();
614
1.19k
    break;
615
0
  }
616
1.77k
  case 1: {
617
1.77k
    if (dSz<4)
618
1.41k
      break;
619
363
    ok=true;
620
363
    int rows[2], cols[2];
621
726
    for (auto &r : rows) r=int(input->readULong(1));
622
726
    for (auto &c : cols) c=int(input->readULong(1));
623
1.08k
    for (int j=0; j<2; ++j) {
624
726
      instr.m_position[j]=MWAWVec2i(cols[j],rows[j]);
625
726
      instr.m_positionRelative[j]=MWAWVec2b(false,false);
626
726
    }
627
363
    instr.m_type=instr.m_position[0]==instr.m_position[1] ? instr.F_Cell : instr.F_CellList;
628
363
    f << instr << ",";
629
363
    break;
630
1.77k
  }
631
5.36k
  default:
632
5.36k
    MWAW_DEBUG_MSG(("MultiplanParser::readLink: find unknown type %d\n", type));
633
5.36k
    break;
634
8.34k
  }
635
8.34k
  if (!ok) {
636
6.78k
    MWAW_DEBUG_MSG(("MultiplanParser::readLink: can not read link at pos %d\n", pos));
637
6.78k
    f << "###";
638
6.78k
  }
639
1.56k
  else
640
1.56k
    m_state->m_posToLinkMap[pos]=instr;
641
8.34k
  ascii().addPos(begPos);
642
8.34k
  ascii().addNote(f.str().c_str());
643
8.34k
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
644
8.34k
  return ok;
645
8.34k
}
646
647
bool MultiplanParser::readLinkFilename(int pos, MWAWCellContent::FormulaInstruction &instr)
648
8.34k
{
649
8.34k
  MWAWInputStreamPtr input = getInput();
650
8.34k
  auto const &entry=m_state->m_entries[5];
651
8.34k
  if (!entry.valid() || pos<0 || pos+10>entry.length() || !input->checkPosition(entry.end())) {
652
3.91k
    MWAW_DEBUG_MSG(("MultiplanParser::readLinkFilename: the pos %d seems bad\n", pos));
653
3.91k
    return false;
654
3.91k
  }
655
4.43k
  long actPos=input->tell();
656
4.43k
  long begPos=entry.begin()+pos;
657
4.43k
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
658
4.43k
  libmwaw::DebugStream f;
659
4.43k
  f << "Link-" << std::hex << pos << std::dec << ":";
660
13.2k
  for (int i=0; i<2; ++i) {
661
8.86k
    int val=int(input->readLong(2));
662
8.86k
    if (val!=1-i) f << "f" << i << "=" << val << ",";
663
8.86k
  }
664
4.43k
  f << "unkn=" << std::hex << input->readULong(4) << std::dec << ","; // d66aa996 | d66aab1e maybe dirId, fileId
665
4.43k
  int dSz=int(input->readULong(1));
666
4.43k
  if (begPos+9+dSz>entry.end()) {
667
146
    MWAW_DEBUG_MSG(("MultiplanParser::readLinkFilename: the pos %d seems bad\n", pos));
668
146
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
669
146
    return false;
670
146
  }
671
4.28k
  librevenge::RVNGString filename;
672
4.28k
  auto fontConverter=getFontConverter();
673
4.28k
  auto const fId=m_state->m_font.id();
674
153k
  for (int i=0; i<dSz; ++i) {
675
149k
    auto ch=static_cast<unsigned char>(input->readULong(1));
676
149k
    int unicode = fontConverter->unicode(fId, static_cast<unsigned char>(ch));
677
149k
    if (unicode!=-1)
678
62.0k
      libmwaw::appendUnicode(uint32_t(unicode), filename);
679
87.4k
    else if (ch==0x9 || ch > 0x1f)
680
7.36k
      libmwaw::appendUnicode(static_cast<uint32_t>(ch), filename);
681
80.0k
    else {
682
80.0k
      f << "##";
683
80.0k
      MWAW_DEBUG_MSG(("MultiplanParser::readLinkFilename: dir seems bad\n"));
684
80.0k
    }
685
149k
  }
686
4.28k
  instr.m_fileName=filename;
687
4.28k
  f << instr.m_fileName.cstr() << ",";
688
4.28k
  instr.m_sheet[0]="Sheet0";
689
4.28k
  ascii().addPos(begPos);
690
4.28k
  ascii().addNote(f.str().c_str());
691
4.28k
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
692
4.28k
  return true;
693
4.43k
}
694
695
bool MultiplanParser::readSharedData(int pos, int cellType, MWAWVec2i const &cellPos, MWAWCellContent &content)
696
73.5k
{
697
73.5k
  auto const &entry=m_state->m_entries[7];
698
73.5k
  MWAWInputStreamPtr input = getInput();
699
73.5k
  if (!entry.valid() || pos<0 || pos+3>entry.length() || !input->checkPosition(entry.end())) {
700
9.55k
    MWAW_DEBUG_MSG(("MultiplanParser::readSharedData: the pos %d seems bad\n", pos));
701
9.55k
    return false;
702
9.55k
  }
703
63.9k
  long actPos=input->tell();
704
63.9k
  long begPos=entry.begin()+pos;
705
63.9k
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
706
63.9k
  libmwaw::DebugStream f;
707
63.9k
  f << "SharedData-" << std::hex << pos << std::dec << ":";
708
63.9k
  int type=int(input->readULong(2));
709
63.9k
  f << "type=" << (type&3) << ",";
710
63.9k
  int N=(type/4);
711
63.9k
  if (N!=2) f << "used=" << N << ",";
712
63.9k
  int dSz=int(input->readULong(1));
713
63.9k
  long endPos=begPos+3+dSz;
714
63.9k
  if (endPos>entry.end()) {
715
1.63k
    MWAW_DEBUG_MSG(("MultiplanParser::readSharedData: the pos %d seems bad\n", pos));
716
1.63k
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
717
1.63k
    return false;
718
1.63k
  }
719
62.3k
  bool ok=true;
720
62.3k
  switch (type&3) {
721
43.8k
  case 0:
722
43.8k
    switch (cellType&3) {
723
16.4k
    case 0: {
724
16.4k
      double value;
725
16.4k
      if (dSz!=8 || !readDouble(value))
726
13.6k
        ok=false;
727
2.84k
      else {
728
2.84k
        content.m_contentType=content.C_NUMBER;
729
2.84k
        content.setValue(value);
730
2.84k
        f << value << ",";
731
2.84k
      }
732
16.4k
      break;
733
0
    }
734
17.7k
    case 1: {
735
17.7k
      content.m_contentType=content.C_TEXT;
736
17.7k
      content.m_textEntry.setBegin(input->tell());
737
17.7k
      content.m_textEntry.setLength(dSz);
738
17.7k
      std::string name;
739
209k
      for (int c=0; c<dSz; ++c) name+=char(input->readULong(1));
740
17.7k
      f << name << ",";
741
17.7k
      break;
742
0
    }
743
4.12k
    case 2:
744
4.12k
      if (dSz!=8)
745
3.71k
        ok=false;
746
412
      else {
747
412
        f << "Nan" << input->readULong(1) << ",";
748
412
        input->seek(7, librevenge::RVNG_SEEK_CUR);
749
412
        content.m_contentType=content.C_NUMBER;
750
412
        content.setValue(std::nan(""));
751
412
      }
752
4.12k
      break;
753
5.53k
    case 3:
754
5.53k
    default:
755
5.53k
      if (dSz!=8)
756
5.19k
        ok=false;
757
337
      else {
758
337
        int val=int(input->readULong(1));
759
337
        content.m_contentType=content.C_NUMBER;
760
337
        content.setValue(val);
761
337
        if (val==0)
762
0
          f << "false,";
763
337
        else if (val==1)
764
0
          f << "true,";
765
337
        else
766
337
          f << "##bool=" << val << ",";
767
337
        input->seek(7, librevenge::RVNG_SEEK_CUR);
768
337
      }
769
5.53k
      break;
770
43.8k
    }
771
43.8k
    break;
772
43.8k
  case 1: {
773
13.4k
    std::string err;
774
13.4k
    if (!readFormula(cellPos, content.m_formula, endPos, err))
775
0
      f << "###";
776
13.4k
    else
777
13.4k
      content.m_contentType=content.C_FORMULA;
778
26.4k
    for (auto const &fo : content.m_formula) f << fo;
779
13.4k
    f << ",";
780
13.4k
    f << err;
781
13.4k
    break;
782
43.8k
  }
783
5.07k
  default:
784
5.07k
    ok=false;
785
5.07k
    break;
786
62.3k
  }
787
62.3k
  if (!ok) {
788
27.5k
    MWAW_DEBUG_MSG(("MultiplanParser::readSharedData: can not read data for the pos %d\n", pos));
789
27.5k
    f << "###";
790
27.5k
  }
791
62.3k
  if (m_state->m_posToSharedDataSeen.find(pos)==m_state->m_posToSharedDataSeen.end()) {
792
19.9k
    m_state->m_posToSharedDataSeen.insert(pos);
793
19.9k
    if (input->tell()!=endPos)
794
4.90k
      ascii().addDelimiter(input->tell(),'|');
795
19.9k
    ascii().addPos(begPos);
796
19.9k
    ascii().addNote(f.str().c_str());
797
19.9k
  }
798
62.3k
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
799
62.3k
  return true;
800
62.3k
}
801
802
bool MultiplanParser::readName(int pos, MWAWCellContent::FormulaInstruction &instruction)
803
959
{
804
959
  auto it=m_state->m_posToNameMap.find(pos);
805
959
  if (it!=m_state->m_posToNameMap.end()) {
806
136
    instruction=it->second;
807
136
    return true;
808
136
  }
809
823
  auto const &entry=m_state->m_entries[8]; // the named entry
810
823
  if (!entry.valid() || pos<0 || pos+10>=entry.length()) {
811
533
    MWAW_DEBUG_MSG(("MultiplanParser::readName: the pos %d seeems bad\n", pos));
812
533
    return false;
813
533
  }
814
290
  MWAWInputStreamPtr input = getInput();
815
290
  long actPos=input->tell();
816
290
  long begPos=entry.begin()+pos;
817
290
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
818
290
  libmwaw::DebugStream f;
819
290
  f << "Names-" << std::hex << pos << std::dec << ":";
820
290
  int val=int(input->readULong(1));
821
290
  int dSz=(val>>3);
822
290
  if (dSz<=0 || begPos+10+dSz>entry.end()) {
823
146
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
824
146
    MWAW_DEBUG_MSG(("MultiplanParser::readName: the pos %d seeems bad\n", pos));
825
146
    return false;
826
146
  }
827
144
  if (val&3) f << "f0=" << val << ",";
828
144
  val=int(input->readULong(1)); // 40|60
829
144
  if (val) f << "f1=" << std::hex << val << std::dec << ",";
830
144
  int rows[2];
831
288
  for (auto &r : rows) r=int(input->readULong(1));
832
144
  val=int(input->readULong(2));
833
144
  int cols[2]= {(val>>10),(val>>4)&0x3f};
834
432
  for (int i=0; i<2; ++i) {
835
288
    instruction.m_position[i]=MWAWVec2i(cols[i], rows[i]);
836
288
    instruction.m_positionRelative[i]=MWAWVec2b(false,false);
837
288
  }
838
144
  instruction.m_type=instruction.m_position[0]==instruction.m_position[1] ? instruction.F_Cell : instruction.F_CellList;
839
144
  f << instruction << ",";
840
144
  m_state->m_posToNameMap[pos]=instruction;
841
144
  if (val&0xf) f << "f2=" << (val&0xf) << ","; // 0|2
842
432
  for (int i=0; i<2; ++i) { // 0
843
288
    val=int(input->readLong(2));
844
288
    if (val) f << "f" << i+2 << "=" << val << ",";
845
288
  }
846
144
  std::string name;
847
1.65k
  for (int c=0; c<dSz; ++c) name+=char(input->readULong(1));
848
144
  f << name << ",";
849
144
  ascii().addPos(begPos);
850
144
  ascii().addNote(f.str().c_str());
851
852
144
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
853
144
  return true;
854
290
}
855
856
bool MultiplanParser::readZoneB()
857
1.82k
{
858
1.82k
  MWAWInputStreamPtr input = getInput();
859
1.82k
  long pos = input->tell();
860
1.82k
  if (!input->checkPosition(pos+82)) {
861
0
    MWAW_DEBUG_MSG(("MultiplanParser::readZoneB: the zone seems too short\n"));
862
0
    return false;
863
0
  }
864
1.82k
  libmwaw::DebugStream f;
865
1.82k
  f << "Entries(ZoneB):";
866
1.82k
  int dim[2];
867
3.64k
  for (auto &d : dim) d=int(input->readULong(2));
868
1.82k
  m_state->m_maximumCell=MWAWVec2i(dim[0],dim[1]);
869
1.82k
  f << "cell[max]=" << m_state->m_maximumCell << ",";
870
1.82k
  int val;
871
14.5k
  for (int i=0; i<7; ++i) {
872
12.7k
    val=int(input->readLong(2));
873
12.7k
    int const expected[]= {0,0,0x7fff,0x47,0xc,0x1e7,0x10a};
874
12.7k
    if (val!=expected[i])
875
9.49k
      f << "f" << i << "=" << val << ",";
876
12.7k
  }
877
29.1k
  for (int i=0; i<15; ++i) {
878
27.3k
    val=int(input->readLong(2));
879
27.3k
    if (!val) continue;
880
12.8k
    f << "g" << i << "=" << val << ",";
881
12.8k
  }
882
1.82k
  ascii().addPos(pos);
883
1.82k
  ascii().addNote(f.str().c_str());
884
885
1.82k
  pos=input->tell();
886
1.82k
  f.str("");
887
1.82k
  f << "ZoneB[II]:";
888
5.46k
  for (int i=0; i<2; ++i) {
889
3.64k
    val=int(input->readLong(1));
890
3.64k
    if (val!=1-i) f << "f" << i << "=" << val << ",";
891
3.64k
  }
892
1.82k
  int dim4[4];
893
7.28k
  for (auto &d : dim4) d=int(input->readULong(1));
894
1.82k
  f << "selection=" << MWAWBox2i(MWAWVec2i(dim4[0],dim4[1]),MWAWVec2i(dim4[2],dim4[3])) << ",";
895
36.4k
  for (int i=0; i<19; ++i) { // 0
896
34.5k
    val=int(input->readLong(1));
897
34.5k
    if (val) f << "g" << i << "=" << val << ",";
898
34.5k
  }
899
1.82k
  ascii().addPos(pos);
900
1.82k
  ascii().addNote(f.str().c_str());
901
902
1.82k
  input->seek(pos+82, librevenge::RVNG_SEEK_SET);
903
1.82k
  return true;
904
1.82k
}
905
906
bool MultiplanParser::readZoneC()
907
1.82k
{
908
1.82k
  MWAWInputStreamPtr input = getInput();
909
1.82k
  long pos = input->tell();
910
1.82k
  if (!input->checkPosition(pos+22)) {
911
0
    MWAW_DEBUG_MSG(("MultiplanParser::readZoneC: the zone seems too short\n"));
912
0
    return false;
913
0
  }
914
1.82k
  libmwaw::DebugStream f;
915
1.82k
  f << "Entries(ZoneC):";
916
1.82k
  int val;
917
1.82k
  f << "unkn=[";
918
9.10k
  for (int i=0; i<4; ++i) { // small number
919
7.28k
    val=int(input->readLong(2));
920
7.28k
    if (val)
921
5.37k
      f << val << ",";
922
1.90k
    else
923
1.90k
      f << "_,";
924
7.28k
  }
925
1.82k
  f << "],";
926
1.82k
  val=int(input->readLong(2));
927
1.82k
  if (val==1)
928
3
    f << "protected,";
929
1.81k
  else if (val)
930
856
    f << "protected=#" << val << ",";
931
1.82k
  val=int(input->readULong(2));
932
1.82k
  if (val) f << "passwd[crypted]=" << std::hex << val << std::dec << ",";
933
10.9k
  for (int i=0; i<5; ++i) {
934
9.10k
    val=int(input->readLong(2));
935
9.10k
    int const expected[]= {0,0,0,2,1};
936
9.10k
    if (val!=expected[i]) f << "g" << i << "=" << val << ",";
937
9.10k
  }
938
1.82k
  ascii().addPos(pos);
939
1.82k
  ascii().addNote(f.str().c_str());
940
1.82k
  return true;
941
1.82k
}
942
943
////////////////////////////////////////////////////////////
944
// double
945
////////////////////////////////////////////////////////////
946
bool MultiplanParser::readDouble(double &value)
947
31.1k
{
948
31.1k
  MWAWInputStreamPtr input = getInput();
949
31.1k
  long pos=input->tell();
950
31.1k
  value=0;
951
31.1k
  if (!input->checkPosition(input->tell()+8)) {
952
0
    MWAW_DEBUG_MSG(("MultiplanParser::readDouble: the zone is too short\n"));
953
0
    return false;
954
0
  }
955
31.1k
  int exponant=int(input->readULong(1));
956
31.1k
  double sign=1;
957
31.1k
  if (exponant&0x80) {
958
5.71k
    exponant&=0x7f;
959
5.71k
    sign=-1;
960
5.71k
  }
961
31.1k
  bool ok=true;
962
31.1k
  double factor=1;
963
218k
  for (int i=0; i<7; ++i) {
964
192k
    int val=int(input->readULong(1));
965
569k
    for (int d=0; d<2; ++d) {
966
381k
      int v= d==0 ? (val>>4) : (val&0xf);
967
381k
      if (v>=10) {
968
5.61k
        MWAW_DEBUG_MSG(("MultiplanParser::readDouble: oops find a bad digits\n"));
969
5.61k
        ok=false;
970
5.61k
        break;
971
5.61k
      }
972
376k
      factor/=10.;
973
376k
      value+=factor*v;
974
376k
    }
975
192k
    if (!ok) break;
976
192k
  }
977
31.1k
  value *= sign*std::pow(10.,exponant-0x40);
978
31.1k
  input->seek(pos+8, librevenge::RVNG_SEEK_SET);
979
31.1k
  return ok;
980
31.1k
}
981
982
////////////////////////////////////////////////////////////
983
// formula
984
////////////////////////////////////////////////////////////
985
namespace MultiplanParserInternal
986
{
987
struct Functions {
988
  char const *m_name;
989
  int m_arity;
990
};
991
992
static Functions const s_listOperators[] = {
993
  // 0
994
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
995
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
996
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
997
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
998
  // 1
999
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1000
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1001
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1002
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1003
  // 2
1004
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1005
  { "", -2}, { ":", 2}, { "", -2}, { "", -2},
1006
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1007
  { "", -2}, { ":", 2}, { "", -2}, { "", -2},
1008
  // 3
1009
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1010
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1011
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1012
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1013
  // 4
1014
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1015
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1016
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1017
  { "", -2}, { ":", 2}, { "", -2}, { "", -2},
1018
  // 5
1019
  { "&", 2}, { "", -2}, { "", -2}, { "", -2},
1020
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1021
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1022
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1023
  // 6
1024
  { "<", 2}, { "", -2}, { "<=", 2}, { "", -2},
1025
  { "=", 2}, { "", -2}, { ">=", 2}, { "", -2},
1026
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1027
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1028
  // 7
1029
  { ">", 2}, { "", -2}, { "<>", 2}, { "", -2},
1030
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1031
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1032
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1033
  // 8
1034
  { "", -2}, { "", -2}, { "+", 2}, { "", -2},
1035
  { "-", 2}, { "", -2}, { "*", 2}, { "", -2},
1036
  { "/", 2}, { "", -2}, { "^", 2}, { "", -2},
1037
  { "", -2}, { "", -2}, { "-", 1}, { "", -2},
1038
  // 9
1039
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1040
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1041
  { "%", 1}, { "", -2}, { "", -2}, { "", -2},
1042
  { "", -2}, { "", -2}, { "", -2}, { "", -2},
1043
};
1044
1045
static char const* const s_listFunctions[]= {
1046
  // 0
1047
  "Count", "If", "IsNA", "IsError",
1048
  "Sum", "Average", "Min", "Max",
1049
  "Row", "Column", "NA", "NPV",
1050
  "Stdev", "Dollar", "Fixed", "Sin",
1051
  // 1
1052
  "Cos", "Tan", "Atan", "Pi",
1053
  "Sqrt", "Exp", "Ln", "Log",
1054
  "Abs", "Int", "Sign", "Round",
1055
  "Lookup", "Index", "Rept", "Mid",
1056
  // 2
1057
  "Length", "Value", "True", "False",
1058
  "And", "Or", "Not", "Mod",
1059
  "IterCnt", "Delta", "PV", "FV",
1060
  "NPer", "PMT", "Rate", "MIRR",
1061
  // 3
1062
  "Irr", nullptr, nullptr, nullptr,
1063
  nullptr, nullptr, nullptr, nullptr,
1064
  nullptr, nullptr, nullptr, nullptr,
1065
  nullptr, nullptr, nullptr, nullptr,
1066
};
1067
}
1068
1069
bool MultiplanParser::readFormula(MWAWVec2i const &cellPos, std::vector<MWAWCellContent::FormulaInstruction> &formula, long endPos, std::string &error)
1070
562k
{
1071
562k
  formula.clear();
1072
562k
  MWAWInputStreamPtr input = getInput();
1073
562k
  if (!input || !input->checkPosition(endPos)) {
1074
0
    MWAW_DEBUG_MSG(("MultiplanParser::readFormula: bad position\n"));
1075
0
    error="badPos###";
1076
0
    return false;
1077
0
  }
1078
562k
  std::vector<std::vector<MWAWCellContent::FormulaInstruction> > stack;
1079
562k
  auto const &listOperators=MultiplanParserInternal::s_listOperators;
1080
562k
  int const numOperators=int(MWAW_N_ELEMENTS(MultiplanParserInternal::s_listOperators));
1081
562k
  auto const &listFunctions=MultiplanParserInternal::s_listFunctions;
1082
562k
  int const numFunctions=int(MWAW_N_ELEMENTS(MultiplanParserInternal::s_listFunctions));
1083
562k
  bool ok=true;
1084
562k
  int closeDelayed=0;
1085
562k
  bool checkForClose=false;
1086
712k
  while (input->tell()<=endPos) {
1087
712k
    long pos=input->tell();
1088
712k
    int wh=pos==endPos ? -1 : int(input->readULong(1));
1089
712k
    bool needCloseParenthesis=closeDelayed && (checkForClose || pos==endPos);
1090
712k
    ok=true;
1091
712k
    if (closeDelayed && !needCloseParenthesis && wh!=0x3c)
1092
19.9k
      needCloseParenthesis=wh>=numOperators || listOperators[wh].m_arity!=2;
1093
712k
    while (needCloseParenthesis && closeDelayed>0) {
1094
20.2k
      auto len=stack.size();
1095
20.2k
      if (len<2) {
1096
19.7k
        error="##closedParenthesis,";
1097
19.7k
        ok=false;
1098
19.7k
        break;
1099
19.7k
      }
1100
475
      auto &dParenthesisFunc=stack[len-2];
1101
475
      if (dParenthesisFunc.size()!=1 || dParenthesisFunc[0].m_type!=dParenthesisFunc[0].F_Operator ||
1102
379
          dParenthesisFunc[0].m_content!="(") {
1103
99
        error="##closedParenthesis,";
1104
99
        ok=false;
1105
99
        break;
1106
99
      }
1107
376
      dParenthesisFunc.insert(dParenthesisFunc.end(),stack.back().begin(), stack.back().end());
1108
376
      MWAWCellContent::FormulaInstruction instr;
1109
376
      instr.m_type=instr.F_Operator;
1110
376
      instr.m_content=")";
1111
376
      dParenthesisFunc.push_back(instr);
1112
376
      stack.resize(len-1);
1113
376
      --closeDelayed;
1114
376
    }
1115
712k
    if (!ok || pos==endPos)
1116
31.1k
      break;
1117
681k
    int arity=0;
1118
681k
    MWAWCellContent::FormulaInstruction instr;
1119
681k
    ok=false;
1120
681k
    bool noneInstr=false, closeFunction=false;
1121
681k
    switch (wh) {
1122
139k
    case 0:
1123
139k
      if (pos+3>endPos || !readLink(int(input->readULong(2)), instr))
1124
121k
        break;
1125
17.9k
      ok=true;
1126
17.9k
      break;
1127
12.1k
    case 0x12: {
1128
12.1k
      if (pos+2>endPos)
1129
717
        break;
1130
11.4k
      ok=true;
1131
11.4k
      instr.m_type=instr.F_Function;
1132
11.4k
      int id=int(input->readULong(1));
1133
11.4k
      if (id<numFunctions && listFunctions[id])
1134
10.8k
        instr.m_content=listFunctions[id];
1135
654
      else {
1136
654
        std::stringstream s;
1137
654
        s << "Funct" << std::hex << id << std::dec;
1138
654
        instr.m_content=s.str();
1139
654
      }
1140
11.4k
      std::vector<MWAWCellContent::FormulaInstruction> child;
1141
11.4k
      child.push_back(instr);
1142
11.4k
      stack.push_back(child);
1143
11.4k
      instr.m_type=instr.F_Operator;
1144
11.4k
      instr.m_content="(";
1145
11.4k
      break;
1146
12.1k
    }
1147
992
    case 0x51:
1148
5.42k
    case 0x71:
1149
7.43k
    case 0x91:
1150
7.82k
    case 0xd1:
1151
8.47k
    case 0xf1:
1152
8.47k
      closeFunction=ok=true;
1153
8.47k
      break;
1154
4.96k
    case 0x1c: // use before %
1155
8.61k
    case 0x1e: // use for <> A 1e B "code <>"
1156
10.1k
    case 0x34: // use for <=,>= ... A 34 B "code <=,..."
1157
11.8k
    case 0x36: // use before -unary
1158
11.8k
      noneInstr=ok=true;
1159
11.8k
      break;
1160
6.43k
    case 0x3a:
1161
6.43k
      ok=true;
1162
6.43k
      instr.m_type=instr.F_Operator;
1163
6.43k
      instr.m_content=";";
1164
6.43k
      break;
1165
27.5k
    case 0x3c:
1166
27.5k
      noneInstr=ok=true;
1167
27.5k
      ++closeDelayed;
1168
27.5k
      break;
1169
20.7k
    case 0x3e:
1170
20.7k
      ok=true;
1171
20.7k
      instr.m_type=instr.F_Operator;
1172
20.7k
      instr.m_content="(";
1173
20.7k
      break;
1174
12.9k
    case 0x56: {
1175
12.9k
      int dSz=int(input->readULong(1));
1176
12.9k
      if (pos+2+dSz>endPos)
1177
12.0k
        break;
1178
911
      instr.m_type=instr.F_Text;
1179
911
      auto fontConverter=getFontConverter();
1180
911
      auto const fId=m_state->m_font.id();
1181
911
      librevenge::RVNGString content;
1182
14.1k
      for (int i=0; i<dSz; ++i) {
1183
13.2k
        auto ch=static_cast<unsigned char>(input->readULong(1));
1184
13.2k
        int unicode = fontConverter->unicode(fId, static_cast<unsigned char>(ch));
1185
13.2k
        if (unicode!=-1)
1186
8.94k
          libmwaw::appendUnicode(uint32_t(unicode), content);
1187
4.28k
        else if (ch==0x9 || ch > 0x1f)
1188
993
          libmwaw::appendUnicode(static_cast<uint32_t>(ch), content);
1189
3.29k
        else {
1190
3.29k
          MWAW_DEBUG_MSG(("MultiplanParser::readFormula: content seen bad seems bad\n"));
1191
3.29k
          error="##content";
1192
3.29k
        }
1193
13.2k
      }
1194
911
      instr.m_content=content.cstr();
1195
911
      ok=true;
1196
911
      break;
1197
12.9k
    }
1198
32.1k
    case 0x21: // double
1199
32.9k
    case 0xe1:
1200
33.1k
    case 0x8f: // simple
1201
33.3k
    case 0xef:
1202
33.3k
      if (pos+3>endPos)
1203
580
        break;
1204
32.7k
      instr.m_type=instr.F_Cell;
1205
32.7k
      instr.m_positionRelative[0]=MWAWVec2b(false,false);
1206
32.7k
      instr.m_position[0][1]=int(input->readULong(1));
1207
32.7k
      instr.m_position[0][0]=int(input->readULong(1));
1208
32.7k
      ok=(instr.m_position[0][0]<63) && (instr.m_position[0][1]<255);
1209
32.7k
      if (!ok) {
1210
8.44k
        error="###RorC";
1211
8.44k
        MWAW_DEBUG_MSG(("MultiplanParser::readFormula: find only row/column reference\n"));
1212
8.44k
      }
1213
32.7k
      break;
1214
5.09k
    case 0x29: // example C2 R1
1215
5.09k
      MWAW_DEBUG_MSG(("MultiplanParser::readFormula: find union operator\n"));
1216
5.09k
      error="###union";
1217
5.09k
      ok=false;
1218
5.09k
      break;
1219
3.02k
    case 0x37: // use for list cell
1220
4.52k
    case 0x53:
1221
4.83k
    case 0x73:
1222
6.23k
    case 0x93: // basic cell
1223
7.61k
    case 0xf3: { // difference ?
1224
7.61k
      if (pos+3>endPos)
1225
786
        break;
1226
6.83k
      instr.m_type=instr.F_Cell;
1227
6.83k
      instr.m_positionRelative[0]=MWAWVec2b(true,true);
1228
6.83k
      int val=int(input->readULong(2));
1229
6.83k
      auto &newPos=instr.m_position[0];
1230
6.83k
      if (val&0x80)
1231
2.75k
        newPos[1]=cellPos[1]-(val>>8);
1232
4.07k
      else
1233
4.07k
        newPos[1]=cellPos[1]+(val>>8);
1234
6.83k
      if (val&0x40)
1235
2.02k
        newPos[0]=cellPos[0]-(val&0x3f);
1236
4.81k
      else
1237
4.81k
        newPos[0]=cellPos[0]+(val&0x3f);
1238
6.83k
      ok=newPos[0]>=0 && newPos[1]>=0;
1239
6.83k
      break;
1240
7.61k
    }
1241
9.58k
    case 0x94:
1242
9.58k
      if (pos+9>endPos || !readDouble(instr.m_doubleValue))
1243
646
        break;
1244
8.93k
      instr.m_type=instr.F_Double;
1245
8.93k
      ok=true;
1246
8.93k
      break;
1247
1.04k
    case 0xf5:
1248
1.04k
      if (pos+3>endPos || !readName(int(input->readULong(2)), instr))
1249
764
        break;
1250
280
      ok=true;
1251
280
      break;
1252
384k
    default:
1253
384k
      if (wh<numOperators && listOperators[wh].m_arity!=-2) {
1254
18.0k
        instr.m_content=listOperators[wh].m_name;
1255
18.0k
        instr.m_type=instr.F_Function;
1256
18.0k
        arity=listOperators[wh].m_arity;
1257
18.0k
      }
1258
384k
      if (instr.m_content.empty()) {
1259
366k
        MWAW_DEBUG_MSG(("MultiplanParser::readFormula: find unknown type %x\n", wh));
1260
366k
        std::stringstream s;
1261
366k
        s << "##unkn[func]=" << std::hex << wh << std::dec << ",";
1262
366k
        error=s.str();
1263
366k
        break;
1264
366k
      }
1265
18.0k
      ok=true;
1266
18.0k
      break;
1267
681k
    }
1268
681k
    if (!ok) {
1269
518k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1270
518k
      break;
1271
518k
    }
1272
162k
    checkForClose=!noneInstr && closeDelayed>0;
1273
162k
    if (noneInstr) continue;
1274
123k
    if (closeFunction) {
1275
8.47k
      ok=false;
1276
8.47k
      if (stack.empty()) {
1277
4.76k
        error="##closed,";
1278
4.76k
        break;
1279
4.76k
      }
1280
3.70k
      auto it=stack.end();
1281
3.70k
      --it;
1282
9.07k
      for (; it!=stack.begin(); --it) {
1283
8.38k
        if (it->size()!=1) continue;
1284
7.53k
        auto const &dInstr=(*it)[0];
1285
7.53k
        if (dInstr.m_type!=dInstr.F_Operator || dInstr.m_content!="(") continue;
1286
3.02k
        auto fIt=it;
1287
3.02k
        --fIt;
1288
3.02k
        auto &functionStack=*fIt;
1289
3.02k
        if (functionStack.size()!=1 || functionStack[0].m_type!=functionStack[0].F_Function) continue;
1290
3.01k
        ok=true;
1291
10.3k
        for (; it!=stack.end(); ++it)
1292
7.37k
          functionStack.insert(functionStack.end(), it->begin(), it->end());
1293
3.01k
        ++fIt;
1294
3.01k
        stack.erase(fIt, stack.end());
1295
3.01k
        break;
1296
3.02k
      }
1297
3.70k
      if (!ok) {
1298
693
        error="##closed";
1299
693
        break;
1300
693
      }
1301
3.01k
      instr.m_type=instr.F_Operator;
1302
3.01k
      instr.m_content=")";
1303
3.01k
      stack.back().push_back(instr);
1304
3.01k
      continue;
1305
3.70k
    }
1306
115k
    if (instr.m_type!=MWAWCellContent::FormulaInstruction::F_Function) {
1307
97.1k
      std::vector<MWAWCellContent::FormulaInstruction> child;
1308
97.1k
      child.push_back(instr);
1309
97.1k
      stack.push_back(child);
1310
97.1k
      continue;
1311
97.1k
    }
1312
18.0k
    size_t numElt = stack.size();
1313
18.0k
    if (static_cast<int>(numElt) < arity) {
1314
7.45k
      std::stringstream s;
1315
7.45k
      s << instr.m_content << "[##" << arity << "]";
1316
7.45k
      error=s.str();
1317
7.45k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1318
7.45k
      ok=false;
1319
7.45k
      break;
1320
7.45k
    }
1321
10.5k
    if (arity==1) {
1322
454
      instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
1323
454
      if (instr.m_content=="%")
1324
99
        stack[numElt-1].push_back(instr);
1325
355
      else
1326
355
        stack[numElt-1].insert(stack[numElt-1].begin(), instr);
1327
454
      continue;
1328
454
    }
1329
10.0k
    if (arity==2) {
1330
10.0k
      instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
1331
10.0k
      stack[numElt-2].push_back(instr);
1332
10.0k
      stack[numElt-2].insert(stack[numElt-2].end(), stack[numElt-1].begin(), stack[numElt-1].end());
1333
10.0k
      stack.resize(numElt-1);
1334
10.0k
      continue;
1335
10.0k
    }
1336
0
    ok=false;
1337
0
    error = "### unexpected arity";
1338
0
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1339
0
    break;
1340
10.0k
  }
1341
562k
  long pos=input->tell();
1342
562k
  if (pos!=endPos || !ok || closeDelayed || stack.size()!=1 || stack[0].empty()) {
1343
555k
    MWAW_DEBUG_MSG(("MultiplanParser::readFormula: can not read a formula\n"));
1344
555k
    ascii().addDelimiter(pos, '|');
1345
555k
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
1346
1347
555k
    std::stringstream s;
1348
555k
    if (!error.empty())
1349
412k
      s << error;
1350
142k
    else
1351
142k
      s << "##unknownError";
1352
555k
    s << "[";
1353
555k
    for (auto const &i : stack) {
1354
83.8k
      for (auto const &j : i)
1355
93.9k
        s << j << ",";
1356
83.8k
    }
1357
555k
    s << "],";
1358
555k
    error=s.str();
1359
555k
    return true;
1360
555k
  }
1361
6.86k
  formula=stack[0];
1362
6.86k
  return true;
1363
562k
}
1364
1365
////////////////////////////////////////////////////////////
1366
// read the header
1367
////////////////////////////////////////////////////////////
1368
bool MultiplanParser::checkHeader(MWAWHeader *header, bool strict)
1369
7.31k
{
1370
7.31k
  *m_state = MultiplanParserInternal::State();
1371
1372
7.31k
  MWAWInputStreamPtr input = getInput();
1373
7.31k
  if (!input || !input->hasDataFork())
1374
2
    return false;
1375
1376
7.31k
  if (!input->checkPosition(0x778)) {
1377
137
    MWAW_DEBUG_MSG(("MultiplanParser::checkHeader: file is too short\n"));
1378
137
    return false;
1379
137
  }
1380
7.17k
  input->seek(0,librevenge::RVNG_SEEK_SET);
1381
7.17k
  if (input->readULong(2)!=0x11ab || input->readULong(2)!=0 ||
1382
7.04k
      input->readULong(2)!=0x13e8 || input->readULong(2)!=0)
1383
248
    return false;
1384
6.92k
  libmwaw::DebugStream f;
1385
6.92k
  f << "FileHeader:";
1386
20.7k
  for (int i=0; i<2; ++i) {
1387
13.8k
    int val=int(input->readLong(2));
1388
13.8k
    if (val)
1389
6.38k
      f << "f" << i << "=" << val << ",";
1390
13.8k
  }
1391
6.92k
  ascii().addPos(0);
1392
6.92k
  ascii().addNote(f.str().c_str());
1393
6.92k
  if (strict) {
1394
    // read the last zone list position and check that it corresponds to a valid position
1395
2.74k
    input->seek(0x758, librevenge::RVNG_SEEK_SET);
1396
2.74k
    int val=int(input->readULong(2));
1397
2.74k
    if (val<0x3c || !input->checkPosition(0x75a+val)) {
1398
548
      MWAW_DEBUG_MSG(("MultiplanParser::checkHeader: can not find last spreadsheet position\n"));
1399
548
      return false;
1400
548
    }
1401
2.74k
  }
1402
  /* checkme: MsMultiplan DOS begins by a list of 8 potential filename
1403
     linked to this files (with length 0x1f) maybe something like
1404
     that... */
1405
6.37k
  input->seek(0x30,librevenge::RVNG_SEEK_SET);
1406
6.37k
  ascii().addPos(0x30);
1407
6.37k
  ascii().addNote("Entries(ZoneA):");
1408
31.8k
  for (int i=0; i<4; ++i) {
1409
25.5k
    long pos=input->tell();
1410
25.5k
    f.str("");
1411
25.5k
    f << "ZoneA" << i << ":";
1412
25.5k
    ascii().addPos(pos);
1413
25.5k
    ascii().addNote(f.str().c_str());
1414
25.5k
    input->seek(pos+0x80,librevenge::RVNG_SEEK_SET);
1415
25.5k
  }
1416
6.37k
  long pos=input->tell();
1417
6.37k
  ascii().addPos(pos);
1418
6.37k
  ascii().addNote("ZoneA4");
1419
6.37k
  input->seek(0x272,librevenge::RVNG_SEEK_SET);
1420
6.37k
  if (header)
1421
2.73k
    header->reset(MWAWDocument::MWAW_T_MICROSOFTMULTIPLAN, 1, MWAWDocument::MWAW_K_SPREADSHEET);
1422
6.37k
  return true;
1423
6.92k
}
1424
1425
////////////////////////////////////////////////////////////
1426
// send spreadsheet
1427
////////////////////////////////////////////////////////////
1428
bool MultiplanParser::sendText(MWAWEntry const &entry)
1429
3.12k
{
1430
3.12k
  auto listener=getMainListener();
1431
3.12k
  if (!listener) {
1432
0
    MWAW_DEBUG_MSG(("MultiplanParser::sendText: can not find the listener\n"));
1433
0
    return false;
1434
0
  }
1435
3.12k
  listener->setFont(m_state->m_font);
1436
3.12k
  auto input=getInput();
1437
3.12k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1438
445k
  for (long l=0; l<entry.length(); ++l) {
1439
442k
    if (input->isEnd()) {
1440
0
      MWAW_DEBUG_MSG(("MultiplanParser::sendText: oops, can not read a character\n"));
1441
0
      break;
1442
0
    }
1443
442k
    auto c=static_cast<unsigned char>(input->readULong(1));
1444
442k
    if (c==0x9)
1445
2.64k
      listener->insertTab();
1446
439k
    else if (c==0xa || c==0xd)
1447
10.8k
      listener->insertEOL();
1448
428k
    else
1449
428k
      listener->insertCharacter(c);
1450
442k
  }
1451
3.12k
  return true;
1452
3.12k
}
1453
1454
bool MultiplanParser::sendCell(MWAWVec2i const &cellPos, int p)
1455
4.30M
{
1456
4.30M
  MWAWSpreadsheetListenerPtr listener=getSpreadsheetListener();
1457
4.30M
  if (!listener) {
1458
0
    MWAW_DEBUG_MSG(("MultiplanParser::sendCell: I can not find the listener\n"));
1459
0
    return false;
1460
0
  }
1461
4.30M
  auto const &entry=m_state->m_entries[6];
1462
4.30M
  if (p<=0 || p>entry.length()) {
1463
0
    MWAW_DEBUG_MSG(("MultiplanParser::sendCell: unexpected position %d\n", p));
1464
0
    return false;
1465
0
  }
1466
4.30M
  MWAWCell cell;
1467
4.30M
  MWAWCellContent content;
1468
4.30M
  MWAWCell::Format format;
1469
4.30M
  cell.setPosition(cellPos);
1470
4.30M
  cell.setFont(m_state->m_font);
1471
4.30M
  libmwaw::DebugStream f;
1472
4.30M
  f << "DataCell[C" << cellPos[0]+1 << "R" << cellPos[1]+1 << "]:";
1473
4.30M
  long pos=entry.begin()+p;
1474
4.30M
  auto it=m_state->m_cellPositionsSet.find(p);
1475
4.30M
  ++it;
1476
4.30M
  long endPos=it!=m_state->m_cellPositionsSet.end() ? entry.begin()+*it : entry.end();
1477
4.30M
  auto input=getInput();
1478
4.30M
  if (endPos-pos<4 || !input->checkPosition(endPos)) {
1479
1.85M
    MWAW_DEBUG_MSG(("MultiplanParser::sendCell: a cell %d seems to short\n", p));
1480
1.85M
    f << "###";
1481
1.85M
    ascii().addPos(pos);
1482
1.85M
    ascii().addNote(f.str().c_str());
1483
1.85M
    return false;
1484
1.85M
  }
1485
2.45M
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1486
2.45M
  int formSize=int(input->readULong(1));
1487
2.45M
  if (formSize) f << "form[size]=" << std::hex << formSize << std::dec << ",";
1488
2.45M
  int val=int(input->readULong(1));
1489
2.45M
  int digits=(val&0xf);
1490
2.45M
  if (digits) f << "decimal=" << digits << ",";
1491
2.45M
  int form=(val>>4)&7;
1492
2.45M
  format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC;
1493
2.45M
  switch (form) {
1494
442k
  case 2:
1495
442k
    format.m_numberFormat=MWAWCell::F_NUMBER_SCIENTIFIC;
1496
442k
    format.m_digits=digits;
1497
442k
    f << "scientific,";
1498
442k
    break;
1499
105k
  case 3:
1500
105k
    format.m_numberFormat=MWAWCell::F_NUMBER_DECIMAL;
1501
105k
    format.m_digits=digits;
1502
105k
    f << "decimal,";
1503
105k
    break;
1504
148k
  case 4: // default
1505
148k
    break;
1506
128k
  case 5:
1507
128k
    format.m_numberFormat=MWAWCell::F_NUMBER_CURRENCY;
1508
128k
    format.m_digits=digits;
1509
128k
    f << "currency,";
1510
128k
    break;
1511
121k
  case 6: // a bar
1512
121k
    f << "bar,";
1513
121k
    break;
1514
103k
  case 7:
1515
103k
    format.m_numberFormat=MWAWCell::F_NUMBER_PERCENT;
1516
103k
    format.m_digits=digits;
1517
103k
    f << "percent,";
1518
103k
    break;
1519
1.40M
  default:
1520
1.40M
    f << "format=" << form << ",";
1521
1.40M
    break;
1522
2.45M
  }
1523
2.45M
  cell.setProtected((val&0x80)!=0);
1524
2.45M
  if ((val&0x80)==0) f << "no[protection],";
1525
2.45M
  val=int(input->readULong(1));
1526
2.45M
  int align=(val>>2)&7;
1527
2.45M
  switch (align) {
1528
170k
  case 1:
1529
170k
    cell.setHAlignment(cell.HALIGN_CENTER);
1530
170k
    f << "center,";
1531
170k
    break;
1532
1.27M
  case 0: // default
1533
1.46M
  case 2: // generic
1534
1.46M
    break;
1535
395k
  case 3:
1536
395k
    cell.setHAlignment(cell.HALIGN_LEFT);
1537
395k
    f << "left,";
1538
395k
    break;
1539
132k
  case 4:
1540
132k
    cell.setHAlignment(cell.HALIGN_RIGHT);
1541
132k
    f << "right,";
1542
132k
    break;
1543
291k
  default:
1544
291k
    f << "#align=" << (align) << ",";
1545
291k
    break;
1546
2.45M
  }
1547
2.45M
  switch (val&3) {
1548
1.27M
  case 0:
1549
1.27M
    f << "double,";
1550
1.27M
    format.m_format=MWAWCell::F_NUMBER;
1551
1.27M
    content.m_contentType=content.C_NUMBER;
1552
1.27M
    break;
1553
428k
  case 1:
1554
428k
    format.m_format=MWAWCell::F_TEXT;
1555
428k
    content.m_contentType=content.C_TEXT;
1556
428k
    f << "text,";
1557
428k
    break;
1558
236k
  case 2:
1559
236k
    format.m_format=MWAWCell::F_NUMBER;
1560
236k
    content.m_contentType=content.C_NUMBER;
1561
236k
    f << "nan,";
1562
236k
    break;
1563
514k
  case 3: // or nothing
1564
514k
    format.m_format=MWAWCell::F_BOOLEAN;
1565
514k
    content.m_contentType=content.C_NUMBER;
1566
514k
    f << "bool,";
1567
514k
    break;
1568
0
  default: // impossible
1569
0
    break;
1570
2.45M
  }
1571
2.45M
  cell.setFormat(format);
1572
2.45M
  if ((val&0x20)==0)
1573
1.63M
    f << "no20[f1],";
1574
2.45M
  if (val&0x40)
1575
520k
    f << "shared,";
1576
2.45M
  int type=(val&0xe3);
1577
2.45M
  if (val&0x80) f << "80[f1],";
1578
2.45M
  int dSz=int(input->readULong(1));
1579
2.45M
  if (endPos<pos+4+dSz) {
1580
862k
    MWAW_DEBUG_MSG(("MultiplanParser::sendCell: a cell seems to short\n"));
1581
862k
    f << "###";
1582
862k
    ascii().addPos(pos);
1583
862k
    ascii().addNote(f.str().c_str());
1584
862k
    return false;
1585
862k
  }
1586
1.59M
  if ((type&0x3)==0 && dSz==8) {
1587
18.6k
    double value;
1588
18.6k
    if (!readDouble(value))
1589
4.89k
      f << "###";
1590
13.7k
    else
1591
13.7k
      content.setValue(value);
1592
18.6k
    f << value << ",";
1593
18.6k
  }
1594
1.57M
  else if ((type&0x3)==1 && dSz && pos+4+dSz+((type&0x40) ? 2 : 0) <= endPos) {
1595
133k
    content.m_textEntry.setBegin(input->tell());
1596
133k
    content.m_textEntry.setLength(dSz);
1597
133k
    std::string name;
1598
4.61M
    for (int c=0; c<dSz; ++c) name+=char(input->readULong(1));
1599
133k
    f << name << ",";
1600
133k
  }
1601
1.44M
  else if ((type&0x3)==2 && dSz==8) {
1602
4.34k
    content.setValue(std::nan(""));
1603
4.34k
    f << "Nan" << input->readULong(1) << ",";
1604
4.34k
    input->seek(7, librevenge::RVNG_SEEK_CUR);
1605
4.34k
  }
1606
1.44M
  else if ((type&0x3)==3 && dSz==8) {
1607
4.39k
    val=int(input->readULong(1));
1608
4.39k
    content.setValue(val);
1609
4.39k
    if (val==0)
1610
1.89k
      f << "false,";
1611
2.50k
    else if (val==1)
1612
1.49k
      f << "true,";
1613
1.01k
    else
1614
1.01k
      f << "##bool=" << val << ",";
1615
4.39k
    input->seek(7, librevenge::RVNG_SEEK_CUR);
1616
4.39k
  }
1617
1.59M
  if ((type&0x40) && input->tell()+2<=endPos && (formSize==0 || formSize==2)) {
1618
73.5k
    if ((input->tell()-pos)%2)
1619
3.46k
      input->seek(1, librevenge::RVNG_SEEK_CUR);
1620
73.5k
    int nPos=int(input->readULong(2));
1621
73.5k
    if (!readSharedData(nPos, type, cellPos, content))
1622
11.1k
      f << "###";
1623
73.5k
    f << "sharedData-" << std::hex << nPos << std::dec << ",";
1624
73.5k
  }
1625
1.52M
  else if (!(type&0x40) && formSize && input->tell()+formSize<=endPos) {
1626
548k
    auto endFPos=input->tell()+formSize;
1627
548k
    std::string err;
1628
548k
    if (!readFormula(cellPos, content.m_formula, endFPos, err)) {
1629
0
      ascii().addDelimiter(input->tell(),'|');
1630
0
      f << "###";
1631
0
    }
1632
548k
    else
1633
548k
      content.m_contentType=content.C_FORMULA;
1634
1635
548k
    for (auto const &fo : content.m_formula) f << fo;
1636
548k
    f << ",";
1637
548k
    f << err;
1638
548k
    input->seek(endFPos, librevenge::RVNG_SEEK_SET);
1639
548k
  }
1640
973k
  else if (formSize) {
1641
400k
    MWAW_DEBUG_MSG(("MultiplanParser::sendCell: can not read a formula\n"));
1642
400k
    f << "###form";
1643
400k
  }
1644
1.59M
  listener->openSheetCell(cell, content);
1645
1.59M
  if (content.m_textEntry.valid()) {
1646
122k
    listener->setFont(cell.getFont());
1647
122k
    input->seek(content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
1648
3.70M
    while (!input->isEnd() && input->tell()<content.m_textEntry.end()) {
1649
3.58M
      auto c=static_cast<unsigned char>(input->readULong(1));
1650
3.58M
      if (c==0x9)
1651
33.6k
        listener->insertTab();
1652
3.54M
      else if (c==0xa || c==0xd)
1653
59.6k
        listener->insertEOL();
1654
3.48M
      else
1655
3.48M
        listener->insertCharacter(c);
1656
3.58M
    }
1657
122k
  }
1658
1.59M
  listener->closeSheetCell();
1659
1.59M
  if (input->tell()!=endPos)
1660
1.50M
    ascii().addDelimiter(input->tell(),'|');
1661
1.59M
  ascii().addPos(pos);
1662
1.59M
  ascii().addNote(f.str().c_str());
1663
1.59M
  return true;
1664
2.45M
}
1665
1666
bool MultiplanParser::sendSpreadsheet()
1667
1.82k
{
1668
1.82k
  MWAWSpreadsheetListenerPtr listener=getSpreadsheetListener();
1669
1.82k
  if (!listener) {
1670
0
    MWAW_DEBUG_MSG(("MultiplanParser::sendSpreadsheet: I can not find the listener\n"));
1671
0
    return false;
1672
0
  }
1673
1.82k
  listener->openSheet(m_state->getColumnsWidth(), librevenge::RVNG_POINT, std::vector<int>(), "Sheet0");
1674
1.82k
  auto const &dataEntry=m_state->m_entries[6];
1675
1.82k
  m_state->m_cellPositionsSet.insert(int(dataEntry.length()));
1676
327k
  for (size_t r=0; r<m_state->m_cellPositions.size(); ++r) {
1677
326k
    auto const &row = m_state->m_cellPositions[r];
1678
326k
    listener->openSheetRow(-16.f, librevenge::RVNG_POINT);
1679
7.67M
    for (size_t col=0; col<row.size(); ++col) {
1680
7.34M
      auto p=row[col];
1681
7.34M
      if (p<0 || p>dataEntry.length()) {
1682
1.62M
        MWAW_DEBUG_MSG(("MultiplanParser::sendSpreadsheet: find some bad data\n"));
1683
1.62M
        continue;
1684
1.62M
      }
1685
5.72M
      if (!p) continue;
1686
4.30M
      MWAWVec2i cellPos(static_cast<int>(col), static_cast<int>(r));
1687
4.30M
      sendCell(cellPos, p);
1688
4.30M
    }
1689
326k
    listener->closeSheetRow();
1690
326k
  }
1691
1.82k
  listener->closeSheet();
1692
1.82k
  return true;
1693
1.82k
}
1694
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: