Coverage Report

Created: 2026-06-13 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/GreatWksParser.cxx
Line
Count
Source
1
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3
/* libmwaw
4
* Version: MPL 2.0 / LGPLv2+
5
*
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 2.0 (the "License"); you may not use this file except in compliance with
8
* the License or as specified alternatively below. You may obtain a copy of
9
* the License at http://www.mozilla.org/MPL/
10
*
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
14
* License.
15
*
16
* Major Contributor(s):
17
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20
* Copyright (C) 2006, 2007 Andrew Ziem
21
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22
*
23
*
24
* All Rights Reserved.
25
*
26
* For minor contributions see the git repository.
27
*
28
* Alternatively, the contents of this file may be used under the terms of
29
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30
* in which case the provisions of the LGPLv2+ are applicable
31
* instead of those above.
32
*/
33
34
#include <iomanip>
35
#include <iostream>
36
#include <limits>
37
#include <set>
38
#include <sstream>
39
40
#include <librevenge/librevenge.h>
41
42
#include "MWAWTextListener.hxx"
43
#include "MWAWHeader.hxx"
44
#include "MWAWParagraph.hxx"
45
#include "MWAWPosition.hxx"
46
#include "MWAWPrinter.hxx"
47
#include "MWAWSection.hxx"
48
#include "MWAWRSRCParser.hxx"
49
#include "MWAWSubDocument.hxx"
50
51
#include "GreatWksDocument.hxx"
52
#include "GreatWksGraph.hxx"
53
#include "GreatWksText.hxx"
54
55
#include "GreatWksParser.hxx"
56
57
/** Internal: the structures of a GreatWksParser */
58
namespace GreatWksParserInternal
59
{
60
61
////////////////////////////////////////
62
//! Internal: the state of a GreatWksParser
63
struct State {
64
  //! constructor
65
  State()
66
120k
    : m_columnsWidth()
67
120k
    , m_hasColSep(false)
68
120k
    , m_actPage(0)
69
120k
    , m_numPages(0)
70
120k
    , m_headerHeight(0)
71
120k
    , m_footerHeight(0)
72
120k
  {
73
480k
    for (auto &fl : m_hfFlags) fl=false;
74
120k
  }
75
  //! returns the number of expected header/footer zones
76
  int numHeaderFooters() const
77
22.1k
  {
78
22.1k
    int num=0;
79
22.1k
    if (m_hfFlags[2]) num++; // header
80
22.1k
    if (m_hfFlags[3]) num++; // footer
81
22.1k
    if (m_hfFlags[1]) num*=2; // lf page
82
22.1k
    return num;
83
22.1k
  }
84
85
  //! returns a section
86
  MWAWSection getSection() const
87
4.15k
  {
88
4.15k
    MWAWSection sec;
89
4.15k
    size_t numCols = m_columnsWidth.size()/2;
90
4.15k
    if (numCols <= 1)
91
1.20k
      return sec;
92
2.94k
    sec.m_columns.resize(size_t(numCols));
93
2.94k
    if (m_hasColSep)
94
67
      sec.m_columnSeparator=MWAWBorder();
95
29.9k
    for (size_t c=0; c < numCols; c++) {
96
27.0k
      double wSep=0;
97
27.0k
      if (c)
98
24.0k
        wSep += sec.m_columns[c].m_margins[libmwaw::Left]=
99
24.0k
                  double(m_columnsWidth[2*c]-m_columnsWidth[2*c-1])/72./2.;
100
27.0k
      if (c+1!=numCols)
101
24.0k
        wSep+=sec.m_columns[c].m_margins[libmwaw::Right]=
102
24.0k
                double(m_columnsWidth[2*c+2]-m_columnsWidth[2*c+1])/72./2.;
103
27.0k
      sec.m_columns[c].m_width =
104
27.0k
        double(m_columnsWidth[2*c+1]-m_columnsWidth[2*c])+72.*wSep;
105
27.0k
      sec.m_columns[c].m_widthUnit = librevenge::RVNG_POINT;
106
27.0k
    }
107
2.94k
    return sec;
108
4.15k
  }
109
110
  //! the columns dimension
111
  std::vector<double> m_columnsWidth;
112
  //! flags to define header/footer (titlePage, l/rPage, header, footer)
113
  bool m_hfFlags[4];
114
  //! true if columns have columns separator
115
  bool m_hasColSep;
116
  int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
117
118
  int m_headerHeight /** the header height if known */,
119
      m_footerHeight /** the footer height if known */;
120
};
121
122
////////////////////////////////////////
123
//! Internal: the subdocument of a GreatWksParser
124
class SubDocument final : public MWAWSubDocument
125
{
126
public:
127
  SubDocument(GreatWksParser &pars, MWAWInputStreamPtr const &input, int zoneId)
128
1.21k
    : MWAWSubDocument(&pars, input, MWAWEntry())
129
1.21k
    , m_id(zoneId) {}
130
131
  //! destructor
132
0
  ~SubDocument() final {}
133
134
  //! operator!=
135
  bool operator!=(MWAWSubDocument const &doc) const final;
136
137
  //! the parser function
138
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
139
140
protected:
141
  //! the subdocument id
142
  int m_id;
143
};
144
145
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type)
146
1.21k
{
147
1.21k
  if (!listener.get()) {
148
0
    MWAW_DEBUG_MSG(("GreatWksParserInternal::SubDocument::parse: no listener\n"));
149
0
    return;
150
0
  }
151
1.21k
  if (type!=libmwaw::DOC_HEADER_FOOTER) {
152
0
    MWAW_DEBUG_MSG(("GreatWksParserInternal::SubDocument::parse: unknown type\n"));
153
0
    return;
154
0
  }
155
1.21k
  auto *parser=dynamic_cast<GreatWksParser *>(m_parser);
156
1.21k
  if (!parser) {
157
0
    MWAW_DEBUG_MSG(("GreatWksParserInternal::SubDocument::parse: no parser\n"));
158
0
    return;
159
0
  }
160
161
1.21k
  long pos = m_input->tell();
162
1.21k
  parser->sendHF(m_id);
163
1.21k
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
164
1.21k
}
165
166
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
167
0
{
168
0
  if (MWAWSubDocument::operator!=(doc)) return true;
169
0
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
170
0
  if (!sDoc) return true;
171
0
  if (m_id != sDoc->m_id) return true;
172
0
  return false;
173
0
}
174
}
175
176
////////////////////////////////////////////////////////////
177
// constructor/destructor, ...
178
////////////////////////////////////////////////////////////
179
GreatWksParser::GreatWksParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
180
50.9k
  : MWAWTextParser(input, rsrcParser, header)
181
50.9k
  , m_state()
182
50.9k
  , m_document()
183
50.9k
{
184
50.9k
  init();
185
50.9k
}
186
187
GreatWksParser::~GreatWksParser()
188
50.9k
{
189
50.9k
}
190
191
void GreatWksParser::init()
192
50.9k
{
193
50.9k
  resetTextListener();
194
50.9k
  setAsciiName("main-1");
195
196
50.9k
  m_state.reset(new GreatWksParserInternal::State);
197
198
  // reduce the margin (in case, the page is not defined)
199
50.9k
  getPageSpan().setMargins(0.1);
200
201
50.9k
  m_document.reset(new GreatWksDocument(*this));
202
50.9k
  m_document->m_newPage=static_cast<GreatWksDocument::NewPage>(&GreatWksParser::newPage);
203
50.9k
  m_document->m_getMainSection=static_cast<GreatWksDocument::GetMainSection>(&GreatWksParser::getMainSection);
204
50.9k
}
205
206
////////////////////////////////////////////////////////////
207
// interface with the text parser
208
////////////////////////////////////////////////////////////
209
MWAWSection GreatWksParser::getMainSection() const
210
4.15k
{
211
4.15k
  return m_state->getSection();
212
4.15k
}
213
214
bool GreatWksParser::sendHF(int id)
215
1.21k
{
216
1.21k
  return m_document->getTextParser()->sendHF(id);
217
1.21k
}
218
219
////////////////////////////////////////////////////////////
220
// new page
221
////////////////////////////////////////////////////////////
222
void GreatWksParser::newPage(int number)
223
24.3k
{
224
24.3k
  if (number <= m_state->m_actPage || number > m_state->m_numPages)
225
9.40k
    return;
226
227
29.8k
  while (m_state->m_actPage < number) {
228
14.9k
    m_state->m_actPage++;
229
14.9k
    if (!getTextListener() || m_state->m_actPage == 1)
230
4.15k
      continue;
231
10.7k
    getTextListener()->insertBreak(MWAWTextListener::PageBreak);
232
10.7k
  }
233
14.9k
}
234
235
////////////////////////////////////////////////////////////
236
// the parser
237
////////////////////////////////////////////////////////////
238
void GreatWksParser::parse(librevenge::RVNGTextInterface *docInterface)
239
18.0k
{
240
18.0k
  if (!getInput().get() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
241
18.0k
  bool ok = false;
242
18.0k
  try {
243
    // create the asciiFile
244
18.0k
    ascii().setStream(getInput());
245
18.0k
    ascii().open(asciiName());
246
247
18.0k
    checkHeader(nullptr);
248
18.0k
    ok = createZones();
249
18.0k
    if (ok) {
250
4.15k
      createDocument(docInterface);
251
4.15k
      m_document->getGraphParser()->sendPageGraphics();
252
4.15k
      m_document->getTextParser()->sendMainText();
253
#ifdef DEBUG
254
      m_document->getTextParser()->flushExtra();
255
#endif
256
4.15k
    }
257
18.0k
    ascii().reset();
258
18.0k
  }
259
18.0k
  catch (...) {
260
0
    MWAW_DEBUG_MSG(("GreatWksParser::parse: exception catched when parsing\n"));
261
0
    ok = false;
262
0
  }
263
264
18.0k
  resetTextListener();
265
18.0k
  if (!ok) throw(libmwaw::ParseException());
266
18.0k
}
267
268
////////////////////////////////////////////////////////////
269
// create the document
270
////////////////////////////////////////////////////////////
271
void GreatWksParser::createDocument(librevenge::RVNGTextInterface *documentInterface)
272
4.15k
{
273
4.15k
  if (!documentInterface) return;
274
4.15k
  if (getTextListener()) {
275
0
    MWAW_DEBUG_MSG(("GreatWksParser::createDocument: listener already exist\n"));
276
0
    return;
277
0
  }
278
279
  // update the page
280
4.15k
  m_state->m_actPage = 0;
281
282
  // create the page list
283
4.15k
  int numPages = 1;
284
4.15k
  if (m_document->getGraphParser()->numPages() > numPages)
285
0
    numPages = m_document->getGraphParser()->numPages();
286
4.15k
  if (m_document->getTextParser()->numPages() > numPages)
287
2.21k
    numPages = m_document->getTextParser()->numPages();
288
4.15k
  m_state->m_numPages = numPages;
289
290
4.15k
  MWAWPageSpan ps(getPageSpan());
291
4.15k
  int numHF=m_state->numHeaderFooters();
292
4.15k
  if (numHF!=m_document->getTextParser()->numHFZones()) {
293
165
    MWAW_DEBUG_MSG(("GreatWksParser::createDocument: header/footer will be ignored\n"));
294
165
    numHF=0;
295
165
  }
296
4.15k
  std::vector<MWAWPageSpan> pageList;
297
4.15k
  if (numHF && m_state->m_hfFlags[0]) // title page have no header/footer
298
0
    pageList.push_back(ps);
299
4.15k
  else
300
4.15k
    numPages++;
301
4.15k
  if (numHF) {
302
1.21k
    int id=0;
303
3.64k
    for (int w=0; w<2; ++w) {
304
2.43k
      if (!m_state->m_hfFlags[w+2])
305
1.21k
        continue;
306
1.21k
      auto type= w==0 ? MWAWHeaderFooter::HEADER : MWAWHeaderFooter::FOOTER;
307
1.21k
      MWAWHeaderFooter hF;
308
1.21k
      if (m_state->m_hfFlags[1]==false) {
309
1.21k
        hF=MWAWHeaderFooter(type, MWAWHeaderFooter::ALL);
310
1.21k
        hF.m_subDocument.reset(new GreatWksParserInternal::SubDocument(*this, getInput(), id++));
311
1.21k
        ps.setHeaderFooter(hF);
312
1.21k
        continue;
313
1.21k
      }
314
0
      hF=MWAWHeaderFooter(type, MWAWHeaderFooter::ODD);
315
0
      hF.m_subDocument.reset(new GreatWksParserInternal::SubDocument(*this, getInput(), id++));
316
0
      ps.setHeaderFooter(hF);
317
0
      hF=MWAWHeaderFooter(type, MWAWHeaderFooter::EVEN);
318
0
      hF.m_subDocument.reset(new GreatWksParserInternal::SubDocument(*this, getInput(), id++));
319
0
      ps.setHeaderFooter(hF);
320
0
    }
321
1.21k
  }
322
4.15k
  ps.setPageSpan(numPages);
323
4.15k
  pageList.push_back(ps);
324
4.15k
  MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
325
4.15k
  setTextListener(listen);
326
4.15k
  listen->startDocument();
327
4.15k
}
328
329
////////////////////////////////////////////////////////////
330
//
331
// Intermediate level
332
//
333
////////////////////////////////////////////////////////////
334
bool GreatWksParser::createZones()
335
18.0k
{
336
18.0k
  m_document->readRSRCZones();
337
18.0k
  MWAWInputStreamPtr input = getInput();
338
18.0k
  long pos=36;
339
18.0k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
340
18.0k
  if (!readDocInfo()) {
341
51
    ascii().addPos(pos);
342
51
    ascii().addNote("Entries(DocInfo):###");
343
51
    return false;
344
51
  }
345
346
18.0k
  bool ok=m_document->getTextParser()->createZones(m_state->numHeaderFooters());
347
18.0k
  if (input->isEnd()) // v1 file end here
348
594
    return ok;
349
350
17.4k
  pos = input->tell();
351
17.4k
  if (!m_document->getGraphParser()->readGraphicZone())
352
14.8k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
353
17.4k
  if (!input->isEnd()) {
354
17.1k
    pos = input->tell();
355
17.1k
    MWAW_DEBUG_MSG(("GreatWksParser::createZones: find some extra data\n"));
356
17.1k
    ascii().addPos(pos);
357
17.1k
    ascii().addNote("Entries(Loose):");
358
17.1k
    ascii().addPos(pos+200);
359
17.1k
    ascii().addNote("_");
360
17.1k
  }
361
362
17.4k
  return ok;
363
18.0k
}
364
365
////////////////////////////////////////////////////////////
366
//
367
// Low level
368
//
369
////////////////////////////////////////////////////////////
370
371
////////////////////////////////////////////////////////////
372
// read some unknown zone in data fork
373
////////////////////////////////////////////////////////////
374
bool GreatWksParser::readDocInfo()
375
18.0k
{
376
18.0k
  MWAWInputStreamPtr input = getInput();
377
18.0k
  long pos = input->tell();
378
18.0k
  int const vers=version();
379
18.0k
  if (!input->checkPosition(pos+46+(vers==2?6:0)+12+38*16)) {
380
51
    MWAW_DEBUG_MSG(("GreatWksParser::readDocInfo: the zone is too short\n"));
381
51
    return false;
382
51
  }
383
18.0k
  libmwaw::DebugStream f;
384
18.0k
  f << "Entries(DocInfo):";
385
18.0k
  int val;
386
90.1k
  for (int i=0; i < 4; ++i) {
387
72.1k
    static char const* const wh[]= {"fl0", "fl1", "smartquote","hidepict"};
388
72.1k
    val =static_cast<int>(input->readLong(1));
389
72.1k
    if (!val) continue;
390
44.7k
    if (val==1) f << wh[i] << ",";
391
34.7k
    else f << "#" << wh[i] << "=" << val << ",";
392
44.7k
  }
393
18.0k
  val =static_cast<int>(input->readLong(2));
394
18.0k
  if (val!=1) f << "first[page]=" << val << ",";
395
360k
  for (int i=0; i < 19; ++i) { // always 0
396
342k
    val =static_cast<int>(input->readLong(2));
397
342k
    if (val)
398
217k
      f << "f" << i+1 << "=" << val << ",";
399
342k
  }
400
18.0k
  ascii().addDelimiter(input->tell(),'|');
401
18.0k
  ascii().addPos(pos);
402
18.0k
  ascii().addNote(f.str().c_str());
403
404
18.0k
  pos+=46+(vers==2?6:0);
405
18.0k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
406
18.0k
  f.str("");
407
18.0k
  f << "DocInfo-II:";
408
90.1k
  for (int i=0; i < 4; ++i) {
409
72.1k
    val=static_cast<int>(input->readLong(1));
410
72.1k
    if (!val) continue;
411
36.0k
    static char const* const wh[]= {"titlePage", "left/rightPage", "header","footer"};
412
36.0k
    if (val!=1) {
413
31.5k
      f << "#" << wh[i] << "=" << val << ",";
414
31.5k
      continue;
415
31.5k
    }
416
4.52k
    f << wh[i] << ",";
417
4.52k
    m_state->m_hfFlags[i]=true;
418
4.52k
  }
419
420
18.0k
  val=static_cast<int>(input->readLong(2)); // f1=1|2
421
18.0k
  if (val)
422
14.5k
    f << "f0=" << val << ",";
423
18.0k
  f << "colSep[w]=" << float(input->readLong(4))/65536.f << ",";
424
18.0k
  val=static_cast<int>(input->readLong(1));
425
18.0k
  if (val==1) f << "same[colW]?,";
426
15.7k
  else if (val) f << "#same[colW]=" << val << ",";
427
18.0k
  val=static_cast<int>(input->readLong(1));
428
18.0k
  if (val==1) {
429
392
    f << "hasColSep,";
430
392
    m_state->m_hasColSep = true;
431
392
  }
432
17.6k
  else if (val) f << "#hasColSep=" << val << ",";
433
18.0k
  input->seek(pos+12, librevenge::RVNG_SEEK_SET);
434
18.0k
  ascii().addPos(pos);
435
18.0k
  ascii().addNote(f.str().c_str());
436
270k
  for (int i=0; i < 14; ++i) {
437
252k
    pos = input->tell();
438
252k
    f.str("");
439
252k
    if (i<4) {
440
72.1k
      static char const* const wh[]= {"margins", "header/footer", "1", "pageDim" };
441
72.1k
      f << "DocInfo[" << wh[i] << "]:";
442
72.1k
    }
443
180k
    else
444
180k
      f << "DocInfo[" << i << "]:";
445
446
252k
    double dim[4];
447
1.01M
    for (auto &d : dim) d=double(input->readLong(4))/65536.;
448
252k
    if (dim[0]>0 || dim[1]>0||dim[2]>0||dim[3]>0) {
449
210k
      f << "dim=" << dim[1] << "x" << dim[0] << "<->" << dim[3] << "x" << dim[2] << ",";
450
210k
      if (i==0) {
451
16.4k
        getPageSpan().setMarginTop(dim[0]/72.0);
452
16.4k
        getPageSpan().setMarginBottom(dim[2]/72.0);
453
16.4k
        getPageSpan().setMarginLeft(dim[1]/72.0);
454
16.4k
        getPageSpan().setMarginRight(dim[3]/72.0);
455
16.4k
      }
456
210k
    }
457
252k
    ascii().addPos(pos);
458
252k
    ascii().addNote(f.str().c_str());
459
252k
    input->seek(pos+16, librevenge::RVNG_SEEK_SET);
460
252k
  }
461
54.1k
  for (int st=0; st < 2; ++st) {
462
36.0k
    pos=input->tell();
463
36.0k
    f.str("");
464
36.0k
    if (st==0)
465
18.0k
      f << "DocInfo[leftPage]:";
466
18.0k
    else
467
18.0k
      f << "DocInfo[rightPage]:";
468
468k
    for (int i=0; i < 12; ++i) {
469
432k
      double dim[4];
470
1.73M
      for (auto &d : dim) d=double(input->readLong(4))/65536.;
471
432k
      if (dim[0]>0 || dim[1]>0||dim[2]>0||dim[3]>0) {
472
350k
        switch (i) {
473
29.7k
        case 0:
474
29.7k
          f << "header=";
475
29.7k
          break;
476
32.4k
        case 11:
477
32.4k
          f << "footer=";
478
32.4k
          break;
479
288k
        default:
480
288k
          f << "col" << i-1 << "=";
481
288k
          if (st==1)
482
144k
            continue;
483
143k
          m_state->m_columnsWidth.push_back(dim[1]);
484
143k
          m_state->m_columnsWidth.push_back(dim[3]);
485
143k
          break;
486
350k
        }
487
206k
        f << dim[1] << "x" << dim[0] << "<->" << dim[3] << "x" << dim[2] << ",";
488
206k
      }
489
432k
    }
490
36.0k
    ascii().addPos(pos);
491
36.0k
    ascii().addNote(f.str().c_str());
492
36.0k
  }
493
494
18.0k
  return true;
495
18.0k
}
496
497
////////////////////////////////////////////////////////////
498
// read the header
499
////////////////////////////////////////////////////////////
500
bool GreatWksParser::checkHeader(MWAWHeader *header, bool strict)
501
69.0k
{
502
69.0k
  *m_state = GreatWksParserInternal::State();
503
69.0k
  if (!m_document->checkHeader(header,strict)) return false;
504
55.5k
  return getParserState()->m_kind==MWAWDocument::MWAW_K_TEXT;
505
69.0k
}
506
507
508
509
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: