Coverage Report

Created: 2026-10-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/MsWksParser.cxx
Line
Count
Source
1
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3
/* libmwaw
4
* Version: MPL 2.0 / LGPLv2+
5
*
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 2.0 (the "License"); you may not use this file except in compliance with
8
* the License or as specified alternatively below. You may obtain a copy of
9
* the License at http://www.mozilla.org/MPL/
10
*
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
14
* License.
15
*
16
* Major Contributor(s):
17
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20
* Copyright (C) 2006, 2007 Andrew Ziem
21
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22
*
23
*
24
* All Rights Reserved.
25
*
26
* For minor contributions see the git repository.
27
*
28
* Alternatively, the contents of this file may be used under the terms of
29
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30
* in which case the provisions of the LGPLv2+ are applicable
31
* instead of those above.
32
*/
33
34
#include <iomanip>
35
#include <iostream>
36
#include <limits>
37
#include <sstream>
38
39
#include <librevenge/librevenge.h>
40
41
42
#include "MWAWTextListener.hxx"
43
#include "MWAWHeader.hxx"
44
#include "MWAWFont.hxx"
45
#include "MWAWFontConverter.hxx"
46
#include "MWAWPictMac.hxx"
47
#include "MWAWPrinter.hxx"
48
#include "MWAWSubDocument.hxx"
49
50
#include "MsWksGraph.hxx"
51
#include "MsWksDocument.hxx"
52
#include "MsWks3Text.hxx"
53
#include "MsWks4Zone.hxx"
54
55
#include "MsWksParser.hxx"
56
57
/** Internal: the structures of a MsWksParser */
58
namespace MsWksParserInternal
59
{
60
////////////////////////////////////////
61
//! Internal: the state of a MsWksParser
62
struct State {
63
  //! constructor
64
  State()
65
181k
    : m_mn0Parser()
66
181k
    , m_actPage(0)
67
181k
    , m_numPages(0)
68
181k
  {
69
181k
  }
70
71
  std::shared_ptr<MsWks4Zone> m_mn0Parser /**parser of main text ole (v4 document)*/;
72
  int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
73
};
74
}
75
76
77
////////////////////////////////////////////////////////////
78
// constructor/destructor, ...
79
////////////////////////////////////////////////////////////
80
MsWksParser::MsWksParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
81
181k
  : MWAWTextParser(input, rsrcParser, header)
82
181k
  , m_state(new MsWksParserInternal::State)
83
181k
  , m_listZones()
84
181k
  , m_document()
85
181k
{
86
181k
  if (input && input->isStructured()) { // potential v4 file
87
14.6k
    MWAWInputStreamPtr mainOle = getInput()->getSubStreamByName("MN0");
88
14.6k
    if (!mainOle) return;
89
90
14.6k
    m_state->m_mn0Parser.reset(new MsWks4Zone(mainOle, getParserState(), *this, "MN0"));
91
14.6k
    m_document=m_state->m_mn0Parser->m_document;
92
14.6k
  }
93
166k
  else
94
166k
    m_document.reset(new MsWksDocument(input, *this));
95
181k
  m_document->m_newPage=static_cast<MsWksDocument::NewPage>(&MsWksParser::newPage);
96
181k
  init();
97
181k
}
98
99
MsWksParser::~MsWksParser()
100
181k
{
101
181k
}
102
103
void MsWksParser::init()
104
181k
{
105
181k
  resetTextListener();
106
181k
  setAsciiName("main-1");
107
108
  // reduce the margin (in case, the page is not defined)
109
181k
  getPageSpan().setMargins(0.1);
110
181k
}
111
112
////////////////////////////////////////////////////////////
113
// new page
114
////////////////////////////////////////////////////////////
115
void MsWksParser::newPage(int number, bool softBreak)
116
33.8k
{
117
33.8k
  if (m_state->m_mn0Parser) // v4 document
118
2.72k
    return m_state->m_mn0Parser->newPage(number, softBreak);
119
120
31.1k
  if (number <= m_state->m_actPage || number > m_state->m_numPages)
121
0
    return;
122
123
70.3k
  while (m_state->m_actPage < number) {
124
39.2k
    m_state->m_actPage++;
125
39.2k
    if (!getTextListener() || m_state->m_actPage == 1)
126
8.12k
      continue;
127
31.1k
    if (softBreak)
128
26.8k
      getTextListener()->insertBreak(MWAWTextListener::SoftPageBreak);
129
4.24k
    else
130
4.24k
      getTextListener()->insertBreak(MWAWTextListener::PageBreak);
131
31.1k
  }
132
31.1k
}
133
134
////////////////////////////////////////////////////////////
135
// the parser
136
////////////////////////////////////////////////////////////
137
void MsWksParser::parse(librevenge::RVNGTextInterface *docInterface)
138
66.8k
{
139
66.8k
  if (!checkHeader(nullptr) || !m_document || !m_document->getInput())  throw(libmwaw::ParseException());
140
141
66.8k
  int const vers=version();
142
66.8k
  bool ok = true;
143
66.8k
  try {
144
    // create the asciiFile for v1-v3 document
145
66.8k
    if (vers<=3)
146
59.8k
      m_document->initAsciiFile(asciiName());
147
148
66.8k
    checkHeader(nullptr);
149
66.8k
    ok = createZones();
150
66.8k
    if (ok) {
151
66.4k
      createDocument(docInterface);
152
66.4k
      if (vers<=3)
153
59.8k
        m_document->sendZone(MsWksDocument::Z_MAIN);
154
6.55k
      else
155
6.55k
        m_state->m_mn0Parser->readContentZones(MWAWEntry(), true);
156
66.4k
      flushExtra();
157
66.4k
    }
158
66.8k
    m_document->ascii().reset();
159
66.8k
  }
160
66.8k
  catch (...) {
161
369
    MWAW_DEBUG_MSG(("MsWksParser::parse: exception catched when parsing\n"));
162
369
    ok = false;
163
369
  }
164
165
66.8k
  resetTextListener();
166
66.8k
  if (!ok) throw(libmwaw::ParseException());
167
66.8k
}
168
169
////////////////////////////////////////////////////////////
170
// flush the extra data
171
////////////////////////////////////////////////////////////
172
void MsWksParser::flushExtra()
173
66.4k
{
174
66.4k
  MWAWTextListenerPtr listener=getTextListener();
175
66.4k
  if (!listener) return;
176
177
66.4k
  if (version()<=3) {
178
59.8k
    m_document->getTextParser3()->flushExtra();
179
59.8k
    m_document->getGraphParser()->flushExtra();
180
59.8k
    return;
181
59.8k
  }
182
183
  // send the final data
184
6.55k
  std::vector<std::string> unparsedOLEs=m_document->getUnparsedOLEZones();
185
6.55k
  if (unparsedOLEs.empty()) return;
186
187
1.94k
  bool first = true;
188
3.24k
  for (auto &name : unparsedOLEs) {
189
3.24k
    MWAWInputStreamPtr ole = getInput()->getSubStreamByName(name.c_str());
190
3.24k
    if (!ole.get()) {
191
0
      MWAW_DEBUG_MSG(("MsWksParser::flushExtra: error: can not find OLE part: \"%s\"\n", name.c_str()));
192
0
      continue;
193
0
    }
194
195
3.24k
    std::shared_ptr<MsWks4Zone> newParser(new MsWks4Zone(ole, getParserState(), *this, name));
196
3.24k
    bool ok = true;
197
3.24k
    try {
198
3.24k
      ok = newParser->createZones(false);
199
3.24k
      if (ok) {
200
        // FIXME: add a message here
201
1.81k
        if (first) {
202
1.37k
          first = false;
203
1.37k
          listener->setFont(MWAWFont(20,20));
204
1.37k
          librevenge::RVNGString message("--------- The original document has some extra ole: -------- ");
205
1.37k
          listener->insertUnicodeString(message);
206
1.37k
          listener->insertEOL();
207
1.37k
        }
208
1.81k
        newParser->readContentZones(MWAWEntry(), false);
209
1.81k
      }
210
3.24k
    }
211
3.24k
    catch (...) {
212
311
      ok = false;
213
311
    }
214
215
3.24k
    if (ok) continue;
216
1.43k
    MWAW_DEBUG_MSG(("MsWksParser::flushExtra: error: can not parse OLE: \"%s\"\n", name.c_str()));
217
1.43k
  }
218
1.94k
}
219
220
////////////////////////////////////////////////////////////
221
// create the document
222
////////////////////////////////////////////////////////////
223
void MsWksParser::createDocument(librevenge::RVNGTextInterface *documentInterface)
224
66.4k
{
225
66.4k
  if (!documentInterface || getTextListener()) {
226
0
    MWAW_DEBUG_MSG(("MsWksParser::createDocument: listener already exist\n"));
227
0
    throw (libmwaw::ParseException());
228
0
  }
229
230
66.4k
  if (m_state->m_mn0Parser) {
231
6.55k
    MWAWTextListenerPtr listener = m_state->m_mn0Parser->createListener(documentInterface);
232
6.55k
    if (!listener) {
233
0
      MWAW_DEBUG_MSG(("MsWksParser::parse: can not create a listener\n"));
234
0
      throw (libmwaw::ParseException());
235
0
    }
236
6.55k
    setTextListener(listener);
237
6.55k
    listener->startDocument();
238
6.55k
    return;
239
6.55k
  }
240
241
59.8k
  std::vector<MWAWPageSpan> pageList;
242
59.8k
  m_state->m_actPage = 0;
243
59.8k
  m_document->getPageSpanList(pageList, m_state->m_numPages);
244
59.8k
  MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
245
59.8k
  setTextListener(listen);
246
59.8k
  listen->startDocument();
247
  // time to send page information the graph parser and the text parser
248
59.8k
  m_document->getGraphParser()->setPageLeftTop
249
59.8k
  (MWAWVec2f(72.f *float(getPageSpan().getMarginLeft()),
250
59.8k
             72.f *float(getPageSpan().getMarginTop())+m_document->getHeaderFooterHeight(true)));
251
59.8k
}
252
253
////////////////////////////////////////////////////////////
254
//
255
// Intermediate level
256
//
257
////////////////////////////////////////////////////////////
258
bool MsWksParser::createZones()
259
66.8k
{
260
66.8k
  MWAWInputStreamPtr input = m_document->getInput();
261
66.8k
  if (!input) return false;
262
263
66.8k
  int const vers=version();
264
66.8k
  if (vers==4) {
265
6.97k
    if (!m_state->m_mn0Parser || !m_state->m_mn0Parser->createZones(true))
266
49
      return false;
267
6.92k
    m_state->m_mn0Parser->m_document->createOLEZones(getInput());
268
6.92k
    return true;
269
6.97k
  }
270
271
59.8k
  long pos = input->tell();
272
59.8k
  if (vers>=3) {
273
29.8k
    bool ok = true;
274
29.8k
    if (m_document->hasHeader())
275
20.4k
      ok = m_document->readGroupHeaderFooter(true,99);
276
29.8k
    if (ok) pos = input->tell();
277
4.88k
    else input->seek(pos, librevenge::RVNG_SEEK_SET);
278
29.8k
    if (ok && m_document->hasFooter())
279
13.6k
      ok = m_document->readGroupHeaderFooter(false,99);
280
29.8k
    if (!ok) input->seek(pos, librevenge::RVNG_SEEK_SET);
281
29.8k
  }
282
283
59.8k
  auto &typeZoneMap=m_document->getTypeZoneMap();
284
59.8k
  auto mainId=int(typeZoneMap.size()); // FIXME: use m_document->getNewZoneId();
285
59.8k
  typeZoneMap.insert(std::multimap<int, MsWksDocument::Zone>::value_type
286
59.8k
                     (MsWksDocument::Z_MAIN,MsWksDocument::Zone(MsWksDocument::Z_MAIN, mainId)));
287
59.8k
  auto &mainZone = typeZoneMap.find(MsWksDocument::Z_MAIN)->second;
288
289
59.8k
  libmwaw::DebugFile &ascFile = m_document->ascii();
290
  /* now normally
291
     in v1: a list of pictures, document info zone, ..., without any way to know the limit
292
     in v2: a group zone (which regroup the graphic) and a document info zone
293
     in v3: a document info zone and a group zone (the graphic zone)
294
   */
295
345k
  while (!input->isEnd()) {
296
343k
    pos = input->tell();
297
343k
    if (!m_document->readZone(mainZone)) {
298
55.9k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
299
55.9k
      break;
300
55.9k
    }
301
287k
    if (input->tell() <= pos) {
302
1.84k
      MWAW_DEBUG_MSG(("MsWksParser::createZones: no data were read from zone at pos %ld\n", pos));
303
1.84k
      break;
304
1.84k
    }
305
287k
  }
306
307
59.8k
  mainZone.m_textId = m_document->getTextParser3()->createZones(-1, true);
308
309
59.8k
  pos = input->tell();
310
311
59.8k
  if (!input->isEnd()) {
312
56.2k
    ascFile.addPos(pos);
313
56.2k
    ascFile.addNote("Entries(End)");
314
56.2k
    ascFile.addPos(pos+100);
315
56.2k
    ascFile.addNote("_");
316
56.2k
  }
317
318
  // ok, prepare the data
319
59.8k
  m_state->m_numPages = 1;
320
59.8k
  std::vector<int> linesH, pagesH;
321
59.8k
  if (m_document->getTextParser3()->getLinesPagesHeight(mainZone.m_textId, linesH, pagesH))
322
22.8k
    m_document->getGraphParser()->computePositions(mainId, linesH, pagesH);
323
324
59.8k
  return true;
325
66.8k
}
326
327
328
////////////////////////////////////////////////////////////
329
//
330
// Low level
331
//
332
////////////////////////////////////////////////////////////
333
334
////////////////////////////////////////////////////////////
335
// read the header
336
////////////////////////////////////////////////////////////
337
bool MsWksParser::checkHeader(MWAWHeader *header, bool strict)
338
248k
{
339
248k
  MWAWInputStreamPtr &input= getInput();
340
248k
  if (!input || !input->hasDataFork() || !m_document)
341
0
    return false;
342
343
248k
  if (!input->isStructured()) {
344
    // first check v1-v3 file
345
226k
    if (!m_document->checkHeader3(header, strict)) return false;
346
185k
    if (m_document->getKind() != MWAWDocument::MWAW_K_TEXT)
347
0
      return false;
348
185k
    if (version() < 1 || version() > 3)
349
146
      return false;
350
185k
    return true;
351
185k
  }
352
353
  // now check for version 4 file
354
21.6k
  MWAWInputStreamPtr mmOle = input->getSubStreamByName("MM");
355
21.6k
  if (!mmOle) return false;
356
21.5k
  mmOle->seek(0, librevenge::RVNG_SEEK_SET);
357
21.5k
  if (mmOle->readULong(2) != 0x444e) return false;
358
359
21.3k
  MWAWInputStreamPtr mainOle = m_document->getInput();
360
21.3k
  if (!mainOle) return false;
361
21.3k
  mainOle->seek(0, librevenge::RVNG_SEEK_SET);
362
21.3k
  if (mainOle->readULong(4)!=0x43484e4b) // CHNKINK
363
0
    return false;
364
21.3k
  setVersion(4);
365
21.3k
  if (header)
366
7.45k
    header->reset(MWAWDocument::MWAW_T_MICROSOFTWORKS, 4);
367
21.3k
  return true;
368
369
21.3k
}
370
371
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: