Coverage Report

Created: 2026-07-20 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/MsWksDocument.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 <string.h>
35
36
#include <iomanip>
37
#include <iostream>
38
#include <limits>
39
#include <set>
40
#include <sstream>
41
42
#include <librevenge/librevenge.h>
43
44
#include "MWAWTextListener.hxx"
45
#include "MWAWHeader.hxx"
46
#include "MWAWOLEParser.hxx"
47
#include "MWAWParagraph.hxx"
48
#include "MWAWParser.hxx"
49
#include "MWAWPosition.hxx"
50
#include "MWAWPrinter.hxx"
51
#include "MWAWSection.hxx"
52
#include "MWAWSubDocument.hxx"
53
#include "MWAWRSRCParser.hxx"
54
55
#include "MsWksGraph.hxx"
56
#include "MsWks3Text.hxx"
57
#include "MsWks4Text.hxx"
58
#include "MsWks4Zone.hxx"
59
60
#include "MsWksDocument.hxx"
61
62
/** Internal: the structures of a MsWksDocument */
63
namespace MsWksDocumentInternal
64
{
65
66
////////////////////////////////////////
67
//! Internal: the state of a MsWksDocument
68
struct State {
69
  //! constructor
70
  State()
71
636k
    : m_kind(MWAWDocument::MWAW_K_TEXT)
72
636k
    , m_fileHeaderSize(0)
73
636k
    , m_freeZoneId(0)
74
636k
    , m_typeZoneMap()
75
636k
    , m_entryMap()
76
636k
    , m_hasHeader(false)
77
636k
    , m_hasFooter(false)
78
636k
    , m_oleParser()
79
636k
    , m_headerParser()
80
636k
    , m_footerParser()
81
636k
    , m_footnoteParser()
82
636k
    , m_frameParserMap()
83
636k
    , m_unparsedOlesName()
84
636k
    , m_actPage(0)
85
636k
    , m_numPages(0)
86
636k
    , m_headerHeight(0)
87
636k
    , m_footerHeight(0)
88
636k
  {
89
636k
  }
90
  //! returns a color palette
91
  std::vector<MWAWColor> const &getPalette(int vers);
92
  //! the type of document
93
  MWAWDocument::Kind m_kind;
94
  //! the file header size
95
  long m_fileHeaderSize;
96
  //! an id to count the number of free map
97
  mutable int m_freeZoneId;
98
  //! the list of zone (for v1-v3) document
99
  std::multimap<int, MsWksDocument::Zone> m_typeZoneMap;
100
  //! the list of entries, name->entry ( for v4 document)
101
  std::multimap<std::string, MWAWEntry> m_entryMap;
102
  bool m_hasHeader /** true if there is a header v3*/, m_hasFooter /** true if there is a footer v3*/;
103
104
  // ole data
105
  /** the ole parser */
106
  std::shared_ptr<MWAWOLEParser> m_oleParser;
107
  std::shared_ptr<MsWks4Zone> m_headerParser /**parser of the header ole*/, m_footerParser /**parser of the footer ole*/,
108
      m_footnoteParser /**parser of the footnote ole*/;
109
  /**the frame parsers: name-> parser*/
110
  std::map<std::string, std::shared_ptr<MsWks4Zone> > m_frameParserMap;
111
  //! the list of unparsed OLEs
112
  std::vector<std::string> m_unparsedOlesName;
113
  //! the color palette
114
  std::vector<MWAWColor> m_colorPalette[4];
115
  int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
116
117
  int m_headerHeight /** the header height if known */,
118
      m_footerHeight /** the footer height if known */;
119
};
120
121
std::vector<MWAWColor> const &State::getPalette(int vers)
122
86.8M
{
123
86.8M
  switch (vers) {
124
1.25M
  case 2: {
125
1.25M
    if (!m_colorPalette[0].empty())
126
1.24M
      return m_colorPalette[0];
127
14.3k
    m_colorPalette[0].resize(9);
128
14.3k
    m_colorPalette[0][0]=MWAWColor(0,0,0); // undef
129
14.3k
    m_colorPalette[0][1]=MWAWColor(0,0,0);
130
14.3k
    m_colorPalette[0][2]=MWAWColor(255,255,255);
131
14.3k
    m_colorPalette[0][3]=MWAWColor(255,0,0);
132
14.3k
    m_colorPalette[0][4]=MWAWColor(0,255,0);
133
14.3k
    m_colorPalette[0][5]=MWAWColor(0,0,255);
134
14.3k
    m_colorPalette[0][6]=MWAWColor(0, 255,255);
135
14.3k
    m_colorPalette[0][7]=MWAWColor(255,0,255);
136
14.3k
    m_colorPalette[0][8]=MWAWColor(255,255,0);
137
14.3k
    return m_colorPalette[0];
138
1.25M
  }
139
84.8M
  case 3: {
140
84.8M
    if (!m_colorPalette[1].empty())
141
84.7M
      return m_colorPalette[1];
142
38.8k
    m_colorPalette[1].resize(256);
143
38.8k
    size_t ind=0;
144
271k
    for (int k = 0; k < 6; k++) {
145
1.63M
      for (int j = 0; j < 6; j++) {
146
8.84M
        for (int i = 0; i < 6; i++, ind++) {
147
7.68M
          if (j==5 && i==2) break;
148
7.45M
          m_colorPalette[1][ind]=MWAWColor(static_cast<unsigned char>(255-51*i), static_cast<unsigned char>(255-51*k), static_cast<unsigned char>(255-51*j));
149
7.45M
        }
150
1.39M
      }
151
232k
    }
152
153
    // the last 2 lines
154
116k
    for (int r = 0; r < 2; r++) {
155
      // the black, red, green, blue zone of 5*2
156
388k
      for (int c = 0; c < 4; c++) {
157
1.86M
        for (int i = 0; i < 5; i++, ind++) {
158
1.55M
          int val = 17*r+51*i;
159
1.55M
          if (c == 0) {
160
388k
            m_colorPalette[1][ind]=MWAWColor(static_cast<unsigned char>(val), static_cast<unsigned char>(val), static_cast<unsigned char>(val));
161
388k
            continue;
162
388k
          }
163
1.16M
          int color[3]= {0,0,0};
164
1.16M
          color[c-1]=val;
165
1.16M
          m_colorPalette[1][ind]=MWAWColor(static_cast<unsigned char>(color[0]),static_cast<unsigned char>(color[1]),static_cast<unsigned char>(color[2]));
166
1.16M
        }
167
310k
      }
168
      // last part of j==5, i=2..5
169
310k
      for (int k = r; k < 6; k+=2) {
170
1.16M
        for (int i = 2; i < 6; i++, ind++)
171
931k
          m_colorPalette[1][ind]=MWAWColor(static_cast<unsigned char>(255-51*i), static_cast<unsigned char>(255-51*k), static_cast<unsigned char>(255-51*5));
172
232k
      }
173
77.6k
    }
174
38.8k
    return m_colorPalette[1];
175
84.8M
  }
176
61.3k
  case 4: {
177
61.3k
    if (!m_colorPalette[2].empty())
178
61.2k
      return m_colorPalette[2];
179
145
    m_colorPalette[2].resize(256);
180
145
    size_t ind=0;
181
1.01k
    for (int k = 0; k < 6; k++) {
182
6.09k
      for (int j = 0; j < 6; j++) {
183
36.5k
        for (int i = 0; i < 6; i++, ind++) {
184
31.3k
          m_colorPalette[2][ind]=
185
31.3k
            MWAWColor(static_cast<unsigned char>(255-51*k), static_cast<unsigned char>(255-51*j),
186
31.3k
                      static_cast<unsigned char>(255-51*i));
187
31.3k
        }
188
5.22k
      }
189
870
    }
190
145
    ind--; // remove the black color
191
725
    for (int c = 0; c < 4; c++) {
192
580
      unsigned char color[3] = {0,0,0};
193
580
      auto val=static_cast<unsigned char>(251);
194
6.38k
      for (int i = 0; i < 10; i++) {
195
5.80k
        val = static_cast<unsigned char>(val-17);
196
5.80k
        if (c == 3) m_colorPalette[2][ind++]=MWAWColor(val, val, val);
197
4.35k
        else {
198
4.35k
          color[c] = val;
199
4.35k
          m_colorPalette[2][ind++]=MWAWColor(color[0],color[1],color[2]);
200
4.35k
        }
201
5.80k
        if ((i%2)==1) val = static_cast<unsigned char>(val-17);
202
5.80k
      }
203
580
    }
204
205
    // last is black
206
145
    m_colorPalette[2][ind++]=MWAWColor(0,0,0);
207
145
    return m_colorPalette[2];
208
61.3k
  }
209
715k
  default:
210
715k
    break;
211
86.8M
  }
212
715k
  MWAW_DEBUG_MSG(("MsWksDocumentInternal::State::getPalette: can not find palette for version %d\n", vers));
213
715k
  return m_colorPalette[3];
214
86.8M
}
215
216
////////////////////////////////////////
217
//! Internal: the subdocument of a MsWksDocument
218
class SubDocument final : public MWAWSubDocument
219
{
220
public:
221
  enum Type { Note, OLE, Zone, Text };
222
  SubDocument(MsWksDocument &document, MWAWInputStreamPtr const &input, Type type, int zoneId)
223
31.2k
    : MWAWSubDocument(document.m_parser, input, MWAWEntry())
224
31.2k
    , m_document(document)
225
31.2k
    , m_type(type)
226
31.2k
    , m_id(zoneId)
227
31.2k
  {
228
31.2k
  }
229
230
  //! destructor
231
0
  ~SubDocument() final {}
232
233
  //! operator!=
234
  bool operator!=(MWAWSubDocument const &doc) const final;
235
236
  //! the parser function
237
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
238
239
protected:
240
  /** the main document */
241
  MsWksDocument &m_document;
242
  /** the type */
243
  Type m_type;
244
  /** the subdocument id*/
245
  int m_id;
246
};
247
248
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
249
1.49M
{
250
1.49M
  if (!listener.get()) {
251
0
    MWAW_DEBUG_MSG(("MsWksDocument::SubDocument::parse: no listener\n"));
252
0
    return;
253
0
  }
254
255
1.49M
  long pos = m_input->tell();
256
1.49M
  switch (m_type) {
257
843
  case Note:
258
843
    m_document.sendFootnoteContent(m_id);
259
843
    break;
260
4.51k
  case OLE:
261
4.51k
    if (m_id==MsWksDocument::Z_HEADER) m_document.sendTextbox(MWAWEntry(),"QHdr");
262
1.91k
    else if (m_id==MsWksDocument::Z_FOOTER) m_document.sendTextbox(MWAWEntry(),"QFtr");
263
0
    else {
264
0
      MWAW_DEBUG_MSG(("MsWksDocument::SubDocument::parse: unexpected ole zone %d\n", m_id));
265
0
    }
266
4.51k
    break;
267
4.30k
  case Text:
268
4.30k
    m_document.sendText(m_id);
269
4.30k
    break;
270
1.48M
  case Zone:
271
1.48M
    m_document.sendZone(m_id);
272
1.48M
    break;
273
#if !defined(__clang__)
274
  default:
275
    MWAW_DEBUG_MSG(("MsWksDocument::SubDocument::parse: unexpected zone type\n"));
276
    break;
277
#endif
278
1.49M
  }
279
1.49M
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
280
1.49M
}
281
282
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
283
0
{
284
0
  if (MWAWSubDocument::operator!=(doc)) return true;
285
0
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
286
0
  if (!sDoc) return true;
287
0
  if (&m_document != &sDoc->m_document) return true;
288
0
  if (m_id != sDoc->m_id) return true;
289
0
  if (m_type != sDoc->m_type) return true;
290
0
  return false;
291
0
}
292
}
293
294
////////////////////////////////////////////////////////////
295
// constructor/destructor, accessor functions...
296
////////////////////////////////////////////////////////////
297
MsWksDocument::MsWksDocument(MWAWInputStreamPtr const &input, MWAWParser &parser)
298
284k
  : m_state()
299
284k
  , m_parserState(parser.getParserState())
300
284k
  , m_parser(&parser)
301
284k
  , m_parentDocument(nullptr)
302
284k
  , m_input(input)
303
284k
  , m_asciiFile(MWAWInputStreamPtr())
304
284k
  , m_graphParser()
305
284k
  , m_textParser3()
306
284k
  , m_textParser4()
307
284k
  , m_newPage(nullptr)
308
284k
{
309
284k
  m_state.reset(new MsWksDocumentInternal::State());
310
284k
  m_graphParser.reset(new MsWksGraph(*this));
311
284k
}
312
313
MsWksDocument::~MsWksDocument()
314
284k
{
315
284k
}
316
317
void MsWksDocument::initAsciiFile(std::string const &name)
318
116k
{
319
116k
  m_asciiFile.setStream(m_input);
320
116k
  m_asciiFile.open(name);
321
116k
}
322
323
std::shared_ptr<MsWks3Text> MsWksDocument::getTextParser3()
324
65.3M
{
325
65.3M
  if (!m_textParser3) m_textParser3.reset(new MsWks3Text(*this));
326
65.3M
  return m_textParser3;
327
65.3M
}
328
329
std::shared_ptr<MsWks4Text> MsWksDocument::getTextParser4()
330
71.0k
{
331
71.0k
  if (!m_textParser4) m_textParser4.reset(new MsWks4Text(*this));
332
71.0k
  return m_textParser4;
333
71.0k
}
334
335
void MsWksDocument::setVersion(int vers)
336
351k
{
337
351k
  m_parserState->m_version=vers;
338
351k
}
339
340
int MsWksDocument::version() const
341
23.3M
{
342
23.3M
  return m_parserState->m_version;
343
23.3M
}
344
345
MWAWDocument::Kind MsWksDocument::getKind() const
346
290k
{
347
290k
  return m_state->m_kind;
348
290k
}
349
350
void MsWksDocument::setKind(MWAWDocument::Kind kind)
351
0
{
352
0
  m_state->m_kind=kind;
353
0
}
354
355
int MsWksDocument::getNewZoneId() const
356
11.8k
{
357
11.8k
  return MsWksDocument::Z_NONE + ++m_state->m_freeZoneId;
358
11.8k
}
359
360
MsWksDocument::Zone MsWksDocument::getZone(MsWksDocument::ZoneType type) const
361
1.75M
{
362
1.75M
  Zone res;
363
1.75M
  if (m_state->m_typeZoneMap.find(int(type)) != m_state->m_typeZoneMap.end())
364
1.62M
    res = m_state->m_typeZoneMap.find(int(type))->second;
365
1.75M
  return res;
366
1.75M
}
367
368
std::multimap<int, MsWksDocument::Zone> &MsWksDocument::getTypeZoneMap()
369
112k
{
370
112k
  return m_state->m_typeZoneMap;
371
112k
}
372
373
std::multimap<std::string, MWAWEntry> &MsWksDocument::getEntryMap()
374
358k
{
375
358k
  return m_state->m_entryMap;
376
358k
}
377
378
long MsWksDocument::getLengthOfFileHeader3() const
379
1.24k
{
380
1.24k
  return m_state->m_fileHeaderSize;
381
1.24k
}
382
383
void MsWksDocument::sendText(int id)
384
1.30M
{
385
1.30M
  if (m_textParser3) m_textParser3->sendZone(id);
386
1.30M
}
387
388
void MsWksDocument::sendZone(int zoneType)
389
1.54M
{
390
1.54M
  Zone zone=getZone(ZoneType(zoneType));
391
1.54M
  if (zone.m_zoneId >= 0)
392
1.54M
    m_graphParser->sendAll(zone.m_zoneId, zoneType==MsWksDocument::Z_MAIN);
393
1.54M
  if (zone.m_textId >= 0) sendText(zone.m_textId);
394
1.54M
}
395
396
void MsWksDocument::sendFootnoteContent(int noteId)
397
843
{
398
843
  if (!m_parserState->getMainListener()) return;
399
843
  if (!m_state->m_footnoteParser) {
400
536
    MWAW_DEBUG_MSG(("MsWksDocument::sendFootnoteContent: can not found the footnote parser\n"));
401
536
    m_parserState->getMainListener()->insertChar(' ');
402
536
    return;
403
536
  }
404
307
  m_state->m_footnoteParser->createZones(false);
405
307
  m_state->m_footnoteParser->readFootNote(noteId);
406
307
}
407
408
////////////////////////////////////////////////////////////
409
// page span function
410
////////////////////////////////////////////////////////////
411
bool MsWksDocument::hasHeader() const
412
58.5k
{
413
58.5k
  return m_state->m_hasHeader;
414
58.5k
}
415
416
bool MsWksDocument::hasFooter() const
417
53.6k
{
418
53.6k
  return m_state->m_hasFooter;
419
53.6k
}
420
421
float MsWksDocument::getHeaderFooterHeight(bool header) const
422
66.7k
{
423
66.7k
  return header ? float(m_state->m_headerHeight) : float(m_state->m_footerHeight);
424
66.7k
}
425
426
void MsWksDocument::getPageSpanList(std::vector<MWAWPageSpan> &pagesList, int &numPages)
427
71.3k
{
428
71.3k
  Zone mainZone=getZone(Z_MAIN);
429
  // first count the page
430
71.3k
  numPages = 1;
431
71.3k
  if (m_textParser3 && mainZone.m_textId >= 0 && m_textParser3->numPages(mainZone.m_textId) > numPages)
432
7.68k
    numPages = m_textParser3->numPages(mainZone.m_textId);
433
71.3k
  if (m_textParser4 && m_textParser4->numPages() > numPages)
434
1.17k
    numPages = m_textParser4->numPages();
435
71.3k
  if (m_graphParser->numPages(mainZone.m_zoneId) > numPages)
436
3.41k
    numPages = m_graphParser->numPages(mainZone.m_zoneId);
437
  // now update the page list
438
71.3k
  MWAWPageSpan ps(m_parserState->m_pageSpan);
439
71.3k
  int id = m_textParser3 ? m_textParser3->getHeader() : -1;
440
71.3k
  if (id >= 0) {
441
2.45k
    m_state->m_headerHeight=12;
442
2.45k
    MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
443
2.45k
    header.m_subDocument.reset
444
2.45k
    (new MsWksDocumentInternal::SubDocument
445
2.45k
     (*this, getInput(), MsWksDocumentInternal::SubDocument::Text, id));
446
2.45k
    ps.setHeaderFooter(header);
447
2.45k
  }
448
68.9k
  else if (getZone(MsWksDocument::Z_HEADER).m_zoneId >= 0) {
449
17.1k
    MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
450
17.1k
    header.m_subDocument.reset
451
17.1k
    (new MsWksDocumentInternal::SubDocument
452
17.1k
     (*this, getInput(), MsWksDocumentInternal::SubDocument::Zone, int(MsWksDocument::Z_HEADER)));
453
17.1k
    ps.setHeaderFooter(header);
454
17.1k
  }
455
51.7k
  else if (m_state->m_headerParser) {
456
2.59k
    MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
457
2.59k
    header.m_subDocument.reset
458
2.59k
    (new MsWksDocumentInternal::SubDocument
459
2.59k
     (*this, getInput(), MsWksDocumentInternal::SubDocument::OLE, int(MsWksDocument::Z_HEADER)));
460
2.59k
    ps.setHeaderFooter(header);
461
2.59k
  }
462
71.3k
  id = m_textParser3 ? m_textParser3->getFooter() : -1;
463
71.3k
  if (id >= 0) {
464
1.85k
    m_state->m_footerHeight=12;
465
1.85k
    MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
466
1.85k
    footer.m_subDocument.reset
467
1.85k
    (new MsWksDocumentInternal::SubDocument
468
1.85k
     (*this, getInput(), MsWksDocumentInternal::SubDocument::Text, id));
469
1.85k
    ps.setHeaderFooter(footer);
470
1.85k
  }
471
69.5k
  else if (getZone(MsWksDocument::Z_FOOTER).m_zoneId >= 0) {
472
4.48k
    MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
473
4.48k
    footer.m_subDocument.reset
474
4.48k
    (new MsWksDocumentInternal::SubDocument
475
4.48k
     (*this, getInput(), MsWksDocumentInternal::SubDocument::Zone, int(MsWksDocument::Z_FOOTER)));
476
4.48k
    ps.setHeaderFooter(footer);
477
4.48k
  }
478
65.0k
  else if (m_state->m_footerParser) {
479
1.91k
    MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
480
1.91k
    footer.m_subDocument.reset
481
1.91k
    (new MsWksDocumentInternal::SubDocument
482
1.91k
     (*this, getInput(), MsWksDocumentInternal::SubDocument::OLE, int(MsWksDocument::Z_FOOTER)));
483
1.91k
    ps.setHeaderFooter(footer);
484
1.91k
  }
485
71.3k
  ps.setPageSpan(numPages+1);
486
71.3k
  pagesList=std::vector<MWAWPageSpan>(1,ps);
487
71.3k
}
488
489
////////////////////////////////////////////////////////////
490
// interface via callback
491
////////////////////////////////////////////////////////////
492
void MsWksDocument::newPage(int page, bool softBreak)
493
30.4k
{
494
30.4k
  if (m_parentDocument)
495
39
    return m_parentDocument->newPage(page, softBreak);
496
30.4k
  if (!m_newPage) {
497
115
    MWAW_DEBUG_MSG(("MsWksDocument::newPage: can not find the newPage callback\n"));
498
115
    return;
499
115
  }
500
30.3k
  (m_parser->*m_newPage)(page, softBreak);
501
30.3k
}
502
503
void MsWksDocument::sendFootnote(int id)
504
843
{
505
843
  if (m_parentDocument)
506
0
    return m_parentDocument->sendFootnote(id);
507
843
  if (!m_parserState->getMainListener()) return;
508
509
843
  MWAWSubDocumentPtr subdoc(new MsWksDocumentInternal::SubDocument
510
843
                            (*this, getInput(), MsWksDocumentInternal::SubDocument::Note, id));
511
843
  m_parserState->getMainListener()->insertNote(MWAWNote(MWAWNote::FootNote), subdoc);
512
843
}
513
514
void MsWksDocument::sendOLE(int id, MWAWPosition const &pictPos, MWAWGraphicStyle const &style)
515
78.2k
{
516
78.2k
  if (m_parentDocument)
517
0
    return m_parentDocument->sendOLE(id, pictPos, style);
518
519
78.2k
  if (!m_parserState->getMainListener()) return;
520
78.2k
  librevenge::RVNGBinaryData data;
521
78.2k
  MWAWPosition pos;
522
78.2k
  std::string type;
523
78.2k
  if (!m_state->m_oleParser || !m_state->m_oleParser->getObject(id, data, pos, type)) {
524
78.2k
    MWAW_DEBUG_MSG(("MsWksDocument::sendOLE: can not find OLE%d\n", id));
525
78.2k
    return;
526
78.2k
  }
527
0
  m_parserState->getMainListener()->insertPicture(pictPos, MWAWEmbeddedObject(data, type), style);
528
0
}
529
530
void MsWksDocument::sendRBIL(int id, MWAWVec2i const &sz)
531
0
{
532
0
  MsWksGraph::SendData sendData;
533
0
  sendData.m_type = MsWksGraph::SendData::RBIL;
534
0
  sendData.m_id = id;
535
0
  sendData.m_anchor =  MWAWPosition::Char;
536
0
  sendData.m_size = sz;
537
0
  m_graphParser->sendObjects(sendData);
538
0
}
539
540
void MsWksDocument::sendTextbox(MWAWEntry const &entry, std::string const &frame)
541
187k
{
542
187k
  if (m_parentDocument)
543
0
    return m_parentDocument->sendTextbox(entry,frame);
544
187k
  MWAWListenerPtr listener=m_parserState->getMainListener();
545
187k
  if (!listener) return;
546
547
187k
  if (entry.length()==0) {
548
98.8k
    listener->insertChar(' ');
549
98.8k
    return;
550
98.8k
  }
551
552
88.3k
  MsWks4Zone *parser = nullptr;
553
88.3k
  if (frame == "QHdr") parser=m_state->m_headerParser.get();
554
85.7k
  else if (frame == "QFtr") parser=m_state->m_footerParser.get();
555
83.7k
  else {
556
83.7k
    auto frameIt = m_state->m_frameParserMap.find(frame);
557
83.7k
    if (frameIt != m_state->m_frameParserMap.end())
558
773
      parser = frameIt->second.get();
559
83.7k
  }
560
88.3k
  if (!parser || (entry.length() && parser->getTextPosition().length() < entry.end())) {
561
83.0k
    MWAW_DEBUG_MSG(("MsWksDocument::sendTextbox: can not find frame ole: %s\n", frame.c_str()));
562
83.0k
    listener->insertChar(' ');
563
83.0k
    return;
564
83.0k
  }
565
566
  // ok, create the entry
567
5.25k
  MWAWEntry ent(entry);
568
5.25k
  ent.setBegin(entry.begin()+parser->getTextPosition().begin());
569
5.25k
  parser->createZones(false);
570
5.25k
  parser->readContentZones(ent, false);
571
5.25k
}
572
573
////////////////////////////////////////////////////////////
574
//
575
// Intermediate level
576
//
577
////////////////////////////////////////////////////////////
578
std::vector<MWAWColor> const &MsWksDocument::getPalette(int vers)
579
86.8M
{
580
86.8M
  return m_state->getPalette(vers);
581
86.8M
}
582
583
bool MsWksDocument::getColor(int id, MWAWColor &col, int vers)
584
86.8M
{
585
86.8M
  std::vector<MWAWColor> const &palette = getPalette(vers);
586
86.8M
  if (palette.size()==0 || id < 0 || id >= int(palette.size()) ||
587
58.0M
      (vers==2 && id==0)) {
588
58.0M
    static bool first = true;
589
58.0M
    if (first) {
590
36
      MWAW_DEBUG_MSG(("MsWksDocument::getColor: unknown color=%d\n", id));
591
36
      first = false;
592
36
    }
593
58.0M
    return false;
594
58.0M
  }
595
28.7M
  col = palette[size_t(id)];
596
28.7M
  return true;
597
86.8M
}
598
599
////////////////////////////////////////////////////////////
600
//
601
// Low level
602
//
603
////////////////////////////////////////////////////////////
604
605
////////////////////////////////////////////////////////////
606
// create the ole structures or get list of unparsed zones
607
////////////////////////////////////////////////////////////
608
std::vector<std::string> const &MsWksDocument::getUnparsedOLEZones() const
609
4.59k
{
610
4.59k
  return m_state->m_unparsedOlesName;
611
4.59k
}
612
613
bool MsWksDocument::createOLEZones(MWAWInputStreamPtr input)
614
7.82k
{
615
7.82k
  if (!input || !input->isStructured()) return false;
616
7.82k
  m_state->m_oleParser.reset(new MWAWOLEParser("MN0", m_parserState->m_fontConverter, 3));
617
618
7.82k
  if (!m_state->m_oleParser->parse(input)) return false;
619
620
  // normally,
621
  // MacWorks/QHdr, MacWorks/QFtr, MacWorks/QFootnotes, MacWorks/QFrm<number>
622
  // MN0 (the main header), MM (almost empty)
623
  // MatOST/MatadorObject?/*: the picture
624
  // but no metadata
625
7.82k
  auto unparsed = m_state->m_oleParser->getNotParse();
626
627
7.82k
  unparsed.push_back("MN0");
628
629
22.9k
  for (auto const &name : unparsed) {
630
    // separated the directory and the name
631
    //    MatOST/MatadorObject1/Ole10Native
632
    //      -> dir="MatOST/MatadorObject1", base="Ole10Native"
633
22.9k
    auto pos = name.find_last_of('/');
634
22.9k
    std::string dir, base;
635
22.9k
    if (pos == std::string::npos) base = name;
636
13.8k
    else if (pos == 0) base = name.substr(1);
637
13.8k
    else {
638
13.8k
      dir = name.substr(0,pos);
639
13.8k
      base = name.substr(pos+1);
640
13.8k
    }
641
642
22.9k
    if (dir == "" && base == "MN0") continue;
643
15.1k
    bool ok = false;
644
15.1k
    bool isFrame = false;
645
15.1k
    if (dir == "MacWorks") {
646
12.6k
      ok = (base == "QHdr" || base == "QFtr" || base == "QFootnotes");
647
12.6k
      if (!ok && strncmp(base.c_str(),"QFrm",4)==0)
648
1.75k
        ok = isFrame = true;
649
12.6k
    }
650
15.1k
    if (!ok) {
651
3.45k
      m_state->m_unparsedOlesName.push_back(name);
652
3.45k
      continue;
653
3.45k
    }
654
655
11.7k
    MWAWInputStreamPtr ole = input->getSubStreamByName(name.c_str());
656
11.7k
    if (!ole.get()) {
657
0
      MWAW_DEBUG_MSG(("MsWksDocument::createOLEZones: error: can not find OLE part: \"%s\"\n", name.c_str()));
658
0
      continue;
659
0
    }
660
661
11.7k
    std::shared_ptr<MsWks4Zone> newParser(new MsWks4Zone(ole, m_parserState, *m_parser, name));
662
11.7k
    try {
663
11.7k
      ok = newParser->createZones(false);
664
11.7k
    }
665
11.7k
    catch (...) {
666
1.21k
      ok = false;
667
1.21k
    }
668
669
11.7k
    if (!ok) {
670
4.05k
      MWAW_DEBUG_MSG(("MsWksDocument::createOLEZones: error: can not parse OLE: \"%s\"\n", name.c_str()));
671
4.05k
      continue;
672
4.05k
    }
673
674
    // first update the parent document
675
7.66k
    newParser->m_document->m_parentDocument=this;
676
7.66k
    if (base == "QHdr") m_state->m_headerParser = newParser;
677
5.06k
    else if (base == "QFtr") m_state->m_footerParser = newParser;
678
3.14k
    else if (isFrame) {
679
1.49k
      auto frameIt = m_state->m_frameParserMap.find(base);
680
1.49k
      if (frameIt != m_state->m_frameParserMap.end()) {
681
0
        MWAW_DEBUG_MSG(("MsWksDocument::createOLEZones: error: oops, I already find a frame zone %s\n", base.c_str()));
682
0
      }
683
1.49k
      else
684
1.49k
        m_state->m_frameParserMap[base] = newParser;
685
1.49k
    }
686
1.65k
    else if (base == "QFootnotes") m_state->m_footnoteParser = newParser;
687
7.66k
  }
688
689
7.82k
  return true;
690
7.82k
}
691
692
////////////////////////////////////////////////////////////
693
// read the print info
694
////////////////////////////////////////////////////////////
695
bool MsWksDocument::readPrintInfo()
696
16.7M
{
697
16.7M
  MWAWInputStreamPtr input = getInput();
698
16.7M
  long pos = input->tell();
699
16.7M
  libmwaw::DebugStream f;
700
  // print info
701
16.7M
  libmwaw::PrinterInfo info;
702
16.7M
  if (!input->checkPosition(pos+0x78+8) || !info.read(input)) return false;
703
9.43M
  f << "Entries(PrintInfo):"<< info;
704
705
9.43M
  MWAWVec2i paperSize = info.paper().size();
706
9.43M
  MWAWVec2i pageSize = info.page().size();
707
9.43M
  if (pageSize.x() <= 0 || pageSize.y() <= 0 ||
708
7.27M
      paperSize.x() <= 0 || paperSize.y() <= 0) return false;
709
710
  // now read the margin
711
5.07M
  int margin[4];
712
5.07M
  int maxSize = paperSize.x() > paperSize.y() ? paperSize.x() : paperSize.y();
713
5.07M
  f << ", margin=(";
714
12.0M
  for (int i = 0; i < 4; i++) {
715
12.0M
    margin[i] = int(72.f/120.f *float(input->readLong(2)));
716
12.0M
    if (margin[i] < -maxSize || margin[i] > maxSize) return false;
717
6.94M
    f << margin[i];
718
6.94M
    if (i != 3) f << ", ";
719
6.94M
  }
720
9.87k
  f << ")";
721
722
  // fixme: compute the real page length here...
723
  // define margin from print info
724
9.87k
  MWAWVec2i lTopMargin(margin[0],margin[1]), rBotMargin(margin[2],margin[3]);
725
9.87k
  lTopMargin += paperSize - pageSize;
726
727
9.87k
  int leftMargin = lTopMargin.x();
728
9.87k
  int topMargin = lTopMargin.y();
729
730
  // decrease a little right and bottom margins Margin
731
9.87k
  int rightMarg = rBotMargin.x()-50;
732
9.87k
  if (rightMarg < 0) {
733
5.38k
    leftMargin -= (-rightMarg);
734
5.38k
    if (leftMargin < 0) leftMargin=0;
735
5.38k
    rightMarg=0;
736
5.38k
  }
737
9.87k
  int botMarg = rBotMargin.y()-50;
738
9.87k
  if (botMarg < 0) {
739
5.34k
    topMargin -= (-botMarg);
740
5.34k
    if (topMargin < 0) topMargin=0;
741
5.34k
    botMarg=0;
742
5.34k
  }
743
744
9.87k
  m_parserState->m_pageSpan.setMarginTop(topMargin/72.0);
745
9.87k
  m_parserState->m_pageSpan.setMarginBottom(botMarg/72.0);
746
9.87k
  m_parserState->m_pageSpan.setMarginLeft(leftMargin/72.0);
747
9.87k
  m_parserState->m_pageSpan.setMarginRight(rightMarg/72.0);
748
9.87k
  m_parserState->m_pageSpan.setFormLength(paperSize.y()/72.);
749
9.87k
  m_parserState->m_pageSpan.setFormWidth(paperSize.x()/72.);
750
751
9.87k
  ascii().addPos(pos);
752
9.87k
  ascii().addNote(f.str().c_str());
753
9.87k
  input->seek(pos+0x78+8, librevenge::RVNG_SEEK_SET);
754
755
9.87k
  return true;
756
5.07M
}
757
758
////////////////////////////////////////////////////////////
759
// read the document info
760
////////////////////////////////////////////////////////////
761
bool MsWksDocument::readDocumentInfo(long sz)
762
21.3M
{
763
21.3M
  MWAWInputStreamPtr input = getInput();
764
21.3M
  long pos = input->tell();
765
21.3M
  libmwaw::DebugStream f;
766
767
21.3M
  int vers = m_parserState->m_kind==MWAWDocument::MWAW_K_DATABASE ? 2 : version();
768
21.3M
  int docId = 0;
769
21.3M
  int docExtra = 0;
770
21.3M
  int flag = 0;
771
21.3M
  int expectedSz = 0x80;
772
21.3M
  if (sz<=0) {
773
21.2M
    if (input->readLong(1) != 2)
774
59.6k
      return false;
775
21.2M
    docId = static_cast<int>(input->readULong(1));
776
21.2M
    docExtra = static_cast<int>(input->readULong(1));
777
21.2M
    flag = static_cast<int>(input->readULong(1));
778
21.2M
    sz = long(input->readULong(2));
779
21.2M
    expectedSz = vers<=2 ? 0x15e : 0x9a;
780
21.2M
  }
781
21.2M
  long endPos = input->tell()+sz;
782
21.2M
  if (!input->checkPosition(endPos))
783
2.76M
    return false;
784
785
18.4M
  if (sz < expectedSz) {
786
16.7M
    if (sz < 0x78+8) {
787
1.74M
      MWAW_DEBUG_MSG(("MsWksDocument::readDocumentInfo: size is too short\n"));
788
1.74M
      return false;
789
1.74M
    }
790
15.0M
    MWAW_DEBUG_MSG(("MsWksDocument::readDocumentInfo: size is too short: try to continue\n"));
791
15.0M
  }
792
793
16.7M
  f << "Entries(DocInfo):";
794
16.7M
  if (docId) f << "id=0x"<< std::hex << docId << ",";
795
16.7M
  if (docExtra) f << "unk=" << docExtra << ","; // in v3: find 3, 7, 1x
796
16.7M
  if (flag) f << "fl=" << flag << ","; // in v3: find 80, 84, e0
797
16.7M
  ascii().addPos(pos);
798
16.7M
  ascii().addNote(f.str().c_str());
799
800
16.7M
  if (!readPrintInfo()) {
801
16.7M
    input->seek(pos, librevenge::RVNG_SEEK_SET);
802
16.7M
    return true;
803
16.7M
  }
804
805
9.87k
  if (sz < 0x9a) {
806
391
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
807
391
    return true;
808
391
  }
809
9.48k
  pos = input->tell();
810
9.48k
  f.str("");
811
9.48k
  f << "DocInfo-1:";
812
9.48k
  auto val = static_cast<int>(input->readLong(2));
813
9.48k
  if ((val & 0x0400) && vers >= 3) {
814
222
    f << "titlepage,";
815
222
    val &= 0xFBFF;
816
222
  }
817
9.48k
  if (val) f << "unkn=" << val << ",";
818
9.48k
  if (vers <= 2) {
819
20.7k
    for (int wh = 0; wh < 2; wh++) {
820
13.8k
      long debPos = input->tell();
821
13.8k
      std::string name(wh==0 ? "header" : "footer");
822
13.8k
      std::string text = getTextParser3()->readHeaderFooterString(wh==0);
823
13.8k
      if (text.size()) f << name << "="<< text << ",";
824
825
13.8k
      long remain = debPos+100 - input->tell();
826
1.09M
      for (long i = 0; i < remain; i++) {
827
1.08M
        auto c = static_cast<unsigned char>(input->readULong(1));
828
1.08M
        if (c == 0) continue;
829
486k
        f << std::dec << "f"<< i << "=" << static_cast<int>(c) << ",";
830
486k
      }
831
13.8k
    }
832
6.90k
    f << "defFid=" << input->readULong(2) << ",";
833
6.90k
    f << "defFsz=" << input->readULong(2)/2 << ",";
834
6.90k
    val = static_cast<int>(input->readULong(2)); // 0 or 8
835
6.90k
    if (val) f << "#unkn=" << val << ",";
836
6.90k
    int dim[2];
837
13.8k
    for (auto &d : dim) d = static_cast<int>(input->readULong(2));
838
6.90k
    f << "dim=" << dim[0] << "x" << dim[1] << ",";
839
    /* followed by 0 (v1) or 0|0x21|0* (v2)*/
840
6.90k
    ascii().addPos(pos);
841
6.90k
    ascii().addNote(f.str().c_str());
842
6.90k
    pos = input->tell();
843
6.90k
    f.str("");
844
6.90k
    f << "DocInfo-2:";
845
6.90k
  }
846
847
  // last data ( normally 26)
848
9.48k
  auto numData = int((endPos - input->tell())/2);
849
139k
  for (int i = 0; i < numData; i++) {
850
129k
    val = static_cast<int>(input->readLong(2));
851
129k
    switch (i) {
852
3.04k
    case 2:
853
3.04k
      if (val!=1) f << "firstPageNumber=" << val << ",";
854
3.04k
      break;
855
3.02k
    case 3:
856
3.02k
      if (val!=1) f << "firstNoteNumber=" << val << ",";
857
3.02k
      break;
858
123k
    default:
859
123k
      if (val)
860
50.0k
        f << "g" << i << "=" << val << ",";
861
123k
      break;
862
129k
    }
863
129k
  }
864
9.48k
  ascii().addPos(pos);
865
9.48k
  ascii().addNote(f.str().c_str());
866
867
9.48k
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
868
869
9.48k
  return true;
870
9.48k
}
871
872
////////////////////////////////////////////////////////////
873
// read a header/footer zone info
874
////////////////////////////////////////////////////////////
875
bool MsWksDocument::readGroupHeaderFooter(bool header, int check)
876
66.4k
{
877
66.4k
  if (version() < 3) return false;
878
879
66.4k
  MWAWInputStreamPtr input=getInput();
880
66.4k
  long debPos = input->tell();
881
882
66.4k
  auto ptr = long(input->readULong(2));
883
66.4k
  if (input->isEnd()) return false;
884
65.0k
  if (ptr) {
885
49.5k
    if (check == 49) return false;
886
49.5k
    if (check == 99) {
887
49.5k
      MWAW_DEBUG_MSG(("MsWksDocument::readGroupHeaderFooter: find ptr=0x%lx\n", static_cast<long unsigned int>(ptr)));
888
49.5k
    }
889
49.5k
  }
890
891
65.0k
  libmwaw::DebugStream f;
892
893
65.0k
  int size = static_cast<int>(input->readLong(2))+4;
894
65.0k
  int realSize = 0x11;
895
65.0k
  if (size < realSize) return false;
896
53.2k
  if (input->readLong(2) != 0) return false;
897
42.8k
  f << "Entries(GroupHInfo)";
898
42.8k
  if (header)
899
34.0k
    f << "[header]";
900
8.80k
  else
901
8.80k
    f << "[footer]";
902
42.8k
  f << ": size=" << std::hex << size << std::dec << " BTXT";
903
904
42.8k
  if (!input->checkPosition(debPos+size)) return false;
905
906
41.3k
  input->seek(debPos+6, librevenge::RVNG_SEEK_SET);
907
41.3k
  auto N=static_cast<int>(input->readLong(2));
908
41.3k
  f << ", N=" << N;
909
41.3k
  int dim[4];
910
165k
  for (auto &d : dim) d = static_cast<int>(input->readLong(2));
911
912
41.3k
  MWAWBox2i box(MWAWVec2i(dim[1], dim[0]), MWAWVec2i(dim[3], dim[2]));
913
41.3k
  if (box.size().x() < -2000 || box.size().y() < -2000 ||
914
40.4k
      box.size().x() > 2000 || box.size().y() > 2000 ||
915
39.4k
      box.min().x() < -200 || box.min().y() < -200) return false;
916
39.0k
  if (check == 49 && box.size().x() == 0 &&  box.size().y() == 0) return false;
917
39.0k
  f << ", BDBox =" << box;
918
39.0k
  auto val = static_cast<int>(input->readULong(1));
919
39.0k
  if (val) f << ", flag=" << val;
920
921
39.0k
  input->seek(debPos+size, librevenge::RVNG_SEEK_SET);
922
39.0k
  if (check < 99) return true;
923
39.0k
  if (header) m_state->m_headerHeight = box.size().y();
924
6.80k
  else m_state->m_footerHeight = box.size().y();
925
39.0k
  std::multimap<int, MsWksDocument::Zone> &typeZoneMap=getTypeZoneMap();
926
39.0k
  MsWksDocument::ZoneType type=
927
39.0k
    header ? MsWksDocument::Z_HEADER : MsWksDocument::Z_FOOTER;
928
39.0k
  MsWksDocument::Zone zone(type, int(typeZoneMap.size()));
929
930
39.0k
  ascii().addPos(debPos);
931
39.0k
  ascii().addNote(f.str().c_str());
932
39.0k
  ascii().addPos(input->tell());
933
934
39.0k
  input->seek(debPos+realSize, librevenge::RVNG_SEEK_SET);
935
39.0k
  input->pushLimit(debPos+size);
936
39.0k
  bool limitSet = true;
937
82.0M
  for (int i = 0; i < N; i++) {
938
81.9M
    long pos = input->tell();
939
81.9M
    if (limitSet && pos==debPos+size) {
940
28.3k
      limitSet = false;
941
28.3k
      input->popLimit();
942
28.3k
    }
943
81.9M
    if (readZone(zone)) continue;
944
65.2M
    input->seek(pos, librevenge::RVNG_SEEK_SET);
945
65.2M
    zone.m_textId = getTextParser3()->createZones(N-i, false);
946
65.2M
    if (zone.m_textId >= 0)
947
12.4k
      break;
948
65.2M
    MWAW_DEBUG_MSG(("MsWksDocument::readGroupHeaderFooter: can not find end of group\n"));
949
65.2M
    input->seek(pos, librevenge::RVNG_SEEK_SET);
950
65.2M
  }
951
39.0k
  if (limitSet) input->popLimit();
952
39.0k
  if (long(input->tell()) < debPos+size) {
953
10.1k
    ascii().addPos(input->tell());
954
10.1k
    ascii().addNote("GroupHInfo-II");
955
956
10.1k
    input->seek(debPos+size, librevenge::RVNG_SEEK_SET);
957
958
10.1k
    ascii().addPos(debPos + size);
959
10.1k
    ascii().addNote("_");
960
10.1k
  }
961
  //  getGraphParser()->addDeltaToPositions(zone.m_zoneId, -1*box[0]);
962
39.0k
  if (typeZoneMap.find(int(type)) != typeZoneMap.end()) {
963
0
    MWAW_DEBUG_MSG(("MsWksDocument::readGroupHeaderFooter: the zone already exists\n"));
964
0
  }
965
39.0k
  else
966
39.0k
    typeZoneMap.insert(std::multimap<int, MsWksDocument::Zone>::value_type(int(type),zone));
967
39.0k
  return true;
968
39.0k
}
969
970
////////////////////////////////////////////////////////////
971
// read a generic zone
972
////////////////////////////////////////////////////////////
973
bool MsWksDocument::readZone(MsWksDocument::Zone &zone)
974
82.8M
{
975
82.8M
  MWAWInputStreamPtr input = getInput();
976
82.8M
  if (input->isEnd()) return false;
977
80.8M
  long pos = input->tell();
978
80.8M
  MWAWEntry pict;
979
80.8M
  auto val = static_cast<int>(input->readLong(1));
980
80.8M
  input->seek(-1, librevenge::RVNG_SEEK_CUR);
981
80.8M
  switch (val) {
982
39.0M
  case 0: {
983
39.0M
    if (m_graphParser->getEntryPicture(zone.m_zoneId, pict)>=0) {
984
757k
      input->seek(pict.end(), librevenge::RVNG_SEEK_SET);
985
757k
      return true;
986
757k
    }
987
38.3M
    break;
988
39.0M
  }
989
38.3M
  case 1: {
990
9.00M
    if (m_graphParser->getEntryPictureV1(zone.m_zoneId, pict)>=0) {
991
31.5k
      input->seek(pict.end(), librevenge::RVNG_SEEK_SET);
992
31.5k
      return true;
993
31.5k
    }
994
8.97M
    break;
995
9.00M
  }
996
21.2M
  case 2:
997
21.2M
    if (readDocumentInfo())
998
16.7M
      return true;
999
4.49M
    break;
1000
4.49M
  case 3: {
1001
    // checkme, ok for text but is it also ok for other ?
1002
2.35M
    MWAWEntry group;
1003
2.35M
    group.setId(zone.m_zoneId);
1004
2.35M
    group.setName("RBDR");
1005
2.35M
    if (m_graphParser->readRB(input, group, 2))
1006
22.2k
      return true;
1007
2.33M
    break;
1008
2.35M
  }
1009
9.20M
  default:
1010
9.20M
    break;
1011
80.8M
  }
1012
1013
63.3M
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1014
63.3M
  return false;
1015
80.8M
}
1016
1017
////////////////////////////////////////////////////////////
1018
// read the header
1019
////////////////////////////////////////////////////////////
1020
bool MsWksDocument::checkHeader3(MWAWHeader *header, bool strict)
1021
352k
{
1022
352k
  *m_state = MsWksDocumentInternal::State();
1023
352k
  MWAWInputStreamPtr input = getInput();
1024
352k
  if (!input || !input->hasDataFork())
1025
63
    return false;
1026
1027
352k
  int numError = 0, val;
1028
1029
352k
  const int headerSize = 0x20;
1030
1031
352k
  libmwaw::DebugStream f;
1032
1033
352k
  input->seek(0,librevenge::RVNG_SEEK_SET);
1034
1035
352k
  m_state->m_hasHeader = m_state->m_hasFooter = false;
1036
352k
  auto vers = static_cast<int>(input->readULong(4));
1037
352k
  switch (vers) {
1038
61.2k
  case 11:
1039
61.2k
    setVersion(4);
1040
61.2k
    break;
1041
155k
  case 9:
1042
155k
    setVersion(3);
1043
155k
    break;
1044
80.6k
  case 8:
1045
80.6k
    setVersion(2);
1046
80.6k
    break;
1047
53.5k
  case 4:
1048
53.5k
    setVersion(1);
1049
53.5k
    break;
1050
1.32k
  default:
1051
1.32k
    if (strict) return false;
1052
1053
793
    MWAW_DEBUG_MSG(("MsWksDocument::checkHeader3: find unknown version 0x%x\n", static_cast<unsigned int>(vers)));
1054
    // must we stop in this case, or can we continue ?
1055
793
    if (vers < 0 || vers > 14) {
1056
158
      MWAW_DEBUG_MSG(("MsWksDocument::checkHeader3: version too big, we stop\n"));
1057
158
      return false;
1058
158
    }
1059
635
    setVersion((vers < 4) ? 1 : (vers < 8) ? 2 : (vers < 11) ? 3 : 4);
1060
352k
  }
1061
351k
  if (input->seek(headerSize,librevenge::RVNG_SEEK_SET) != 0 || input->isEnd())
1062
496
    return false;
1063
1064
351k
  if (input->seek(12,librevenge::RVNG_SEEK_SET) != 0) return false;
1065
1066
1.40M
  for (int i = 0; i < 3; i++) {
1067
1.05M
    val = static_cast<int>(input->readLong(1));
1068
1.05M
    if (val < -10 || val > 10) {
1069
111k
      MWAW_DEBUG_MSG(("MsWksDocument::checkHeader3: find odd val%d=0x%x: not implemented\n", i, static_cast<unsigned int>(val)));
1070
111k
      numError++;
1071
111k
    }
1072
1.05M
  }
1073
351k
  input->seek(1,librevenge::RVNG_SEEK_CUR);
1074
351k
  auto type = static_cast<int>(input->readLong(2));
1075
351k
  switch (type) {
1076
  // Text document
1077
208k
  case 1:
1078
208k
    break;
1079
16.4k
  case 2:
1080
16.4k
    m_state->m_kind = MWAWDocument::MWAW_K_DATABASE;
1081
16.4k
    break;
1082
26.4k
  case 3:
1083
26.4k
    m_state->m_kind = MWAWDocument::MWAW_K_SPREADSHEET;
1084
26.4k
    break;
1085
100k
  case 12:
1086
100k
    m_state->m_kind = MWAWDocument::MWAW_K_DRAW;
1087
100k
    break;
1088
89
  default:
1089
89
    MWAW_DEBUG_MSG(("MsWksDocument::checkHeader3: find odd type=%d: not implemented\n", type));
1090
89
    return false;
1091
351k
  }
1092
1093
351k
  if (version() < 1 || version() > 4)
1094
0
    return false;
1095
1096
  //
1097
351k
  input->seek(0,librevenge::RVNG_SEEK_SET);
1098
351k
  f << "FileHeader: ";
1099
351k
  f << "version= " << input->readULong(4);
1100
351k
  long dim[4];
1101
1.40M
  for (auto &d : dim) d = input->readLong(2);
1102
351k
  bool checkDatabaseFileHSize=false;
1103
351k
  if (dim[2] <= dim[0] || dim[3] <= dim[1]) {
1104
    // bdbox(0,0,0,0) can appear if there is no document info, so do not consider this an error for database file
1105
200k
    if (dim[0]==0 && dim[1]==0 && dim[2]==0 && dim[3]==0 && m_state->m_kind == MWAWDocument::MWAW_K_DATABASE) {
1106
432
      checkDatabaseFileHSize=true;
1107
432
    }
1108
200k
    else {
1109
200k
      MWAW_DEBUG_MSG(("MsWksDocument::checkHeader3: find odd bdbox\n"));
1110
200k
      numError++;
1111
200k
    }
1112
200k
  }
1113
351k
  f << ", windowdbdbox?=(";
1114
1.40M
  for (auto d : dim) f << d <<",";
1115
351k
  f << "),";
1116
351k
  auto fileHeaderSize=long(input->readULong(4));
1117
351k
  if (checkDatabaseFileHSize && strict) {
1118
216
    if ((version()==2 && fileHeaderSize!=0x516) || (version()==3&&fileHeaderSize!=0x50))
1119
115
      return false;
1120
216
  }
1121
351k
  if (fileHeaderSize)
1122
259k
    f << "headerSize=" << std::hex << fileHeaderSize << std::dec << ",";
1123
1124
351k
  if (m_state->m_kind==MWAWDocument::MWAW_K_SPREADSHEET) {
1125
    /* CHECKME: normally 0x56c, but find one time 0x56a in a v2 file
1126
       which seems to imply that the spreadsheet begins earlier */
1127
26.4k
    if (fileHeaderSize!=0x56c && fileHeaderSize > long(0x550) && input->checkPosition(fileHeaderSize+0x20))
1128
999
      m_state->m_fileHeaderSize=fileHeaderSize;
1129
25.4k
    else
1130
25.4k
      m_state->m_fileHeaderSize=0x56c;
1131
26.4k
  }
1132
351k
  type = static_cast<int>(input->readULong(2));
1133
351k
  f << std::dec;
1134
351k
  switch (type) {
1135
208k
  case 1:
1136
208k
    f << "doc,";
1137
208k
    break;
1138
16.2k
  case 2:
1139
16.2k
    f << "database,";
1140
16.2k
    break; // with ##v3=50
1141
26.4k
  case 3:
1142
26.4k
    f << "spreadsheet,";
1143
26.4k
    break; // with ##v2=5,##v3=6c
1144
100k
  case 12:
1145
100k
    f << "draw,";
1146
100k
    break;
1147
0
  default:
1148
0
    f << "###type=" << type << ",";
1149
0
    break;
1150
351k
  }
1151
351k
  f << "numlines?=" << input->readLong(2) << ",";
1152
351k
  val = static_cast<int>(input->readLong(1)); // 0, v2: 0, 4 or -4
1153
351k
  if (val)  f << "f0=" << val << ",";
1154
351k
  val = static_cast<int>(input->readLong(1)); // almost always 1
1155
351k
  if (val != 1) f << "f1=" << val << ",";
1156
2.10M
  for (int i = 11; i < headerSize/2; i++) { // v1: 0, 0, v2: 0, 0|1
1157
1.75M
    val = static_cast<int>(input->readULong(2));
1158
1.75M
    if (!val) continue;
1159
1.15M
    f << "f" << i << "=" << std::hex << val << std::dec;
1160
1.15M
    if (version() >= 3 && i == 12) {
1161
204k
      if (val & 0x100) {
1162
153k
        m_state->m_hasHeader = true;
1163
153k
        f << "(Head)";
1164
153k
      }
1165
204k
      if (val & 0x200) {
1166
112k
        m_state->m_hasFooter = true;
1167
112k
        f << "(Foot)";
1168
112k
      }
1169
204k
    }
1170
1.15M
    f << ",";
1171
1.15M
  }
1172
1173
351k
  if (header)
1174
166k
    header->reset(MWAWDocument::MWAW_T_MICROSOFTWORKS, version(), m_state->m_kind);
1175
1176
351k
  ascii().addPos(0);
1177
351k
  ascii().addNote(f.str().c_str());
1178
351k
  ascii().addPos(headerSize);
1179
1180
351k
  input->seek(headerSize,librevenge::RVNG_SEEK_SET);
1181
351k
  return strict ? (numError==0) : (numError < 3);
1182
351k
}
1183
1184
////////////////////////////////////////////////////////////
1185
// spreadsheet/database function
1186
////////////////////////////////////////////////////////////
1187
bool MsWksDocument::readDBString(long endPos, std::string &res)
1188
56.7k
{
1189
56.7k
  MWAWInputStreamPtr input=getInput();
1190
56.7k
  res = "";
1191
56.7k
  int error = 0;
1192
56.7k
  int ok = 0;
1193
1.97M
  while (!input->isEnd() && input->tell() < endPos) {
1194
1.92M
    auto c = char(input->readLong(1));
1195
1.92M
    if (c < 27 && c != '\t' && c != '\n') error++;
1196
1.18M
    else ok++;
1197
1.92M
    res += c;
1198
1.92M
  }
1199
56.7k
  return ok >= error;
1200
56.7k
}
1201
1202
bool MsWksDocument::readDBNumber(long endPos, double &res, bool &isNan, std::string &str)
1203
45.2k
{
1204
45.2k
  MWAWInputStreamPtr input=getInput();
1205
45.2k
  res = 0;
1206
45.2k
  str="";
1207
45.2k
  long pos = input->tell();
1208
45.2k
  if (endPos > pos+10 && !readDBString(endPos-10,str)) return false;
1209
38.6k
  return input->tell() == endPos-10 && input->readDouble10(res,isNan);
1210
45.2k
}
1211
1212
bool MsWksDocument::readCellInFormula(MWAWCellContent::FormulaInstruction &instr, bool is2D)
1213
6.60k
{
1214
6.60k
  MWAWInputStreamPtr input=getInput();
1215
6.60k
  instr=MWAWCellContent::FormulaInstruction();
1216
6.60k
  instr.m_type=MWAWCellContent::FormulaInstruction::F_Cell;
1217
6.60k
  bool ok = true;
1218
6.60k
  if (is2D) {
1219
305
    bool absolute[2] = { false, false};
1220
305
    auto type = static_cast<int>(input->readULong(1));
1221
305
    if (type & 0x80) {
1222
135
      absolute[0] = true;
1223
135
      type &= 0x7F;
1224
135
    }
1225
305
    if (type & 0x40) {
1226
121
      absolute[1] = true;
1227
121
      type &= 0xBF;
1228
121
    }
1229
305
    if (type) {
1230
203
      MWAW_DEBUG_MSG(("MSWksSSParser::readCellInFormula:Pb find fl=%d when reading a cell\n", type));
1231
203
      ok = false;
1232
203
    }
1233
305
    int pos[2]= {1,0};
1234
610
    for (auto &p : pos) p = static_cast<int>(input->readULong(1));
1235
1236
305
    if (pos[0] < 1 || pos[1] < 0) {
1237
24
      if (ok) {
1238
6
        MWAW_DEBUG_MSG(("MSWksSSParser::readCellInFormula: can not read cell position\n"));
1239
6
      }
1240
24
      return false;
1241
24
    }
1242
281
    instr.m_position[0]=MWAWVec2i(pos[1],pos[0]-1);
1243
281
    instr.m_positionRelative[0]=MWAWVec2b(!absolute[1], !absolute[0]);
1244
281
  }
1245
6.30k
  else
1246
6.30k
    instr.m_position[0]=MWAWVec2i(static_cast<int>(input->readULong(1)),0);
1247
6.58k
  return ok;
1248
6.60k
}
1249
1250
bool MsWksDocument::readFormula(long endPos, MWAWCellContent &content, std::string &extra)
1251
8.72k
{
1252
8.72k
  MWAWInputStreamPtr input=getInput();
1253
8.72k
  long pos = input->tell();
1254
8.72k
  extra="";
1255
8.72k
  if (pos == endPos) return false;
1256
8.72k
  if (!input->checkPosition(endPos)) {
1257
0
    MWAW_DEBUG_MSG(("MsWksDocument::readFormula: bad endPos\n"));
1258
0
    extra="###endPos";
1259
0
    return false;
1260
0
  }
1261
1262
8.72k
  std::stringstream f;
1263
8.72k
  auto &formula=content.m_formula;
1264
8.72k
  bool is2D=m_parserState->m_kind!=MWAWDocument::MWAW_K_DATABASE;
1265
8.72k
  bool ok=true;
1266
30.2k
  while (input->tell() < endPos && !input->isEnd()) {
1267
26.1k
    pos = input->tell();
1268
26.1k
    auto code = static_cast<int>(input->readLong(1));
1269
26.1k
    MWAWCellContent::FormulaInstruction instr;
1270
26.1k
    bool findEnd=false;
1271
26.1k
    switch (code) {
1272
2.52k
    case 0x0:
1273
8.57k
    case 0x2:
1274
9.79k
    case 0x4:
1275
11.9k
    case 0x6: {
1276
11.9k
      static char const* const wh[]= {"+","-","*","/"};
1277
11.9k
      instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
1278
11.9k
      instr.m_content=wh[code/2];
1279
11.9k
      break;
1280
9.79k
    }
1281
494
    case 0x8: { // a number
1282
494
      auto sz = static_cast<int>(input->readULong(1));
1283
494
      std::string s;
1284
494
      double val;
1285
494
      bool isNan;
1286
494
      if (pos+sz+12 <= endPos && readDBNumber((pos+2)+sz+10, val, isNan, s)) {
1287
144
        instr.m_type=MWAWCellContent::FormulaInstruction::F_Double;
1288
144
        instr.m_doubleValue=val;
1289
144
        break;
1290
144
      }
1291
350
      f << "###number" << s;
1292
350
      ok=false;
1293
350
      break;
1294
494
    }
1295
3.33k
    case 0x0a: { // a cell
1296
3.33k
      if (!readCellInFormula(instr, is2D))
1297
125
        f << "#";
1298
3.33k
      break;
1299
494
    }
1300
1.97k
    case 0x0c: { // function
1301
1.97k
      auto v = static_cast<int>(input->readULong(1));
1302
1.97k
      static char const* const listFunc [0x41] = {
1303
1.97k
        "Abs", "Sum", "Na", "Error", "ACos", "And", "ASin", "ATan",
1304
1.97k
        "ATan2", "Average", "Choose", "Cos", "Count", "Exp", "False", "FV",
1305
1.97k
        "HLookup", "If", "Index", "Int", "IRR", "IsBlank", "IsError", "IsNa",
1306
1.97k
        "##Funct[30]", "Ln", "Lookup", "Log10", "Max", "Min", "Mod", "Not",
1307
1.97k
        "NPer", "NPV", "Or", "Pi", "Pmt", "PV", "Rand", "Round",
1308
1.97k
        "Sign", "Sin", "Sqrt", "StDev", "Tan", "True", "Var", "VLookup",
1309
1.97k
        "Match", "MIRR", "Rate", "Type", "Radians", "Degrees", "Sum" /*"SSum: checkme"*/, "Date",
1310
1.97k
        "Day", "Hour", "Minute", "Month", "Now", "Second", "Time", "Weekday",
1311
1.97k
        "Year"
1312
1.97k
      };
1313
1.97k
      instr.m_type=MWAWCellContent::FormulaInstruction::F_Function;
1314
1.97k
      if ((v%2) == 0 && v >= 0 && v/2 <= 0x40)
1315
1.87k
        instr.m_content=listFunc[v/2];
1316
100
      else {
1317
100
        f << "###";
1318
100
        MWAW_DEBUG_MSG(("MSWksSSParser::readFormula: find unknown function %x\n", static_cast<unsigned int>(v)));
1319
100
        std::stringstream s;
1320
100
        s << "Funct" << std::hex << v << std::dec;
1321
100
        instr.m_content=s.str();
1322
100
      }
1323
1.97k
      break;
1324
494
    }
1325
2.00k
    case 0x0e: { // list of cell
1326
2.00k
      MWAWCellContent::FormulaInstruction instr2;
1327
2.00k
      if (endPos-pos< (is2D ? 9 : 5)) {
1328
330
        f << "###list cell short";
1329
330
        ok = false;
1330
330
        break;
1331
330
      }
1332
1.67k
      if (!readCellInFormula(instr, is2D) || !readCellInFormula(instr2, is2D))
1333
84
        f << "#";
1334
1.67k
      instr.m_type=MWAWCellContent::FormulaInstruction::F_CellList;
1335
1.67k
      instr.m_position[1]=instr2.m_position[0];
1336
1.67k
      instr.m_positionRelative[1]=instr2.m_positionRelative[0];
1337
1.67k
      break;
1338
2.00k
    }
1339
405
    case 0x16:
1340
405
      findEnd=true;
1341
405
      input->seek(-1, librevenge::RVNG_SEEK_CUR);
1342
405
      f << ",";
1343
405
      break;
1344
6.00k
    default:
1345
6.00k
      if ((code%2)==0 && code>=0x10 && code<=0x22) {
1346
2.47k
        static char const* const wh[]= {"(", ")", ";", "end", "<", ">", "=", "<=", ">=", "<>" };
1347
2.47k
        instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
1348
2.47k
        instr.m_content=wh[(code-0x10)/2];
1349
2.47k
        break;
1350
2.47k
      }
1351
3.53k
      f << "###" << std::hex << code << std::dec;
1352
3.53k
      ok = false;
1353
3.53k
      break;
1354
26.1k
    }
1355
26.1k
    if (!ok || findEnd)
1356
4.62k
      break;
1357
21.5k
    f << instr;
1358
21.5k
    formula.push_back(instr);
1359
21.5k
  }
1360
8.72k
  extra=f.str();
1361
8.72k
  pos = input->tell();
1362
8.72k
  if (endPos - pos < 21)
1363
7.09k
    return ok;
1364
  // test if we have the value
1365
1.62k
  if (input->readLong(1) != 0x16) {
1366
1.23k
    input->seek(-1, librevenge::RVNG_SEEK_CUR);
1367
1.23k
    return true;
1368
1.23k
  }
1369
1370
394
  f.str("");
1371
394
  f << std::dec << "unk1=[";
1372
  // looks a little as a zone of cell ?? but this seems eroneous
1373
1.18k
  for (int i = 0; i < 2; i++) {
1374
788
    auto v = static_cast<int>(input->readULong(1));
1375
1376
788
    auto n0 = static_cast<int>(input->readULong(1));
1377
788
    auto n1 = static_cast<int>(input->readULong(1));
1378
788
    if (i == 1) f << ":";
1379
788
    f << n0 << "x" << n1;
1380
788
    if (v) f << "##v";
1381
788
  }
1382
394
  f << std::hex << "],unk2=["; // 0, followed by a small number between 1 and 140
1383
1.18k
  for (int i = 0; i < 2; i++)
1384
788
    f << input->readULong(2) << ",";
1385
394
  f << "]";
1386
1387
  // the value
1388
394
  double value;
1389
394
  bool isNan;
1390
394
  std::string res;
1391
394
  if (!readDBNumber(endPos, value, isNan, res)) {
1392
277
    MWAW_DEBUG_MSG(("MsWksDocument::readFormula: can not read val number\n"));
1393
277
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1394
277
    f << ",###";
1395
277
  }
1396
117
  else {
1397
117
    content.setValue(value);
1398
117
    f << ":" << value << ",";
1399
117
  }
1400
394
  extra += f.str();
1401
394
  return true;
1402
1.62k
}
1403
1404
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: