Coverage Report

Created: 2026-07-20 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/MsWksDRParser.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 "MWAWGraphicListener.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 "MsWksDRParser.hxx"
56
57
/** Internal: the structures of a MsWksDRParser */
58
namespace MsWksDRParserInternal
59
{
60
////////////////////////////////////////
61
//! Internal: the state of a MsWksDRParser
62
struct State {
63
  //! constructor
64
  State()
65
174k
    : m_mainZoneId(0)
66
174k
    , m_actPage(0)
67
174k
    , m_numPages(0)
68
174k
  {
69
174k
  }
70
71
  /** the main zone */
72
  int m_mainZoneId;
73
  int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
74
};
75
}
76
77
78
////////////////////////////////////////////////////////////
79
// constructor/destructor, ...
80
////////////////////////////////////////////////////////////
81
MsWksDRParser::MsWksDRParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
82
73.5k
  : MWAWGraphicParser(input, rsrcParser, header)
83
73.5k
  , m_state()
84
73.5k
  , m_listZones()
85
73.5k
  , m_document()
86
73.5k
{
87
73.5k
  MWAWInputStreamPtr mainInput=input;
88
73.5k
  if (input->isStructured()) {
89
4.28k
    MWAWInputStreamPtr mainOle = input->getSubStreamByName("MN0");
90
4.28k
    if (mainOle)
91
4.28k
      mainInput=mainOle;
92
4.28k
  }
93
73.5k
  m_document.reset(new MsWksDocument(mainInput, *this));
94
73.5k
  init();
95
73.5k
}
96
97
MsWksDRParser::~MsWksDRParser()
98
73.5k
{
99
73.5k
}
100
101
void MsWksDRParser::init()
102
73.5k
{
103
73.5k
  resetGraphicListener();
104
73.5k
  setAsciiName("main-1");
105
106
73.5k
  m_state.reset(new MsWksDRParserInternal::State);
107
108
  // reduce the margin (in case, the page is not defined)
109
73.5k
  getPageSpan().setMargins(0.1);
110
111
73.5k
  m_document->m_newPage=static_cast<MsWksDocument::NewPage>(&MsWksDRParser::newPage);
112
73.5k
}
113
114
////////////////////////////////////////////////////////////
115
// new page
116
////////////////////////////////////////////////////////////
117
void MsWksDRParser::newPage(int number, bool softBreak)
118
3.42M
{
119
3.42M
  if (!getGraphicListener() || number < m_state->m_actPage || number > m_state->m_numPages)
120
46
    return;
121
122
3.42M
  long pos = m_document->getInput()->tell();
123
3.42M
  int const vers=version();
124
6.85M
  while (m_state->m_actPage <= number) {
125
3.42M
    if (++m_state->m_actPage!=1) {
126
3.42M
      if (softBreak)
127
0
        getGraphicListener()->insertBreak(MWAWGraphicListener::SoftPageBreak);
128
3.42M
      else
129
3.42M
        getGraphicListener()->insertBreak(MWAWGraphicListener::PageBreak);
130
3.42M
    }
131
    // first, send the background
132
3.42M
    if (vers==4) {
133
947k
      MsWksGraph::SendData sendData;
134
947k
      sendData.m_type = MsWksGraph::SendData::RBIL;
135
947k
      sendData.m_anchor =  MWAWPosition::Page;
136
947k
      sendData.m_id = 0;
137
947k
      sendData.m_page = -1;
138
947k
      m_document->getGraphParser()->sendObjects(sendData);
139
947k
    }
140
3.42M
    MsWksGraph::SendData sendData;
141
3.42M
    sendData.m_type = MsWksGraph::SendData::RBDR;
142
3.42M
    sendData.m_anchor =  MWAWPosition::Page;
143
3.42M
    sendData.m_page = m_state->m_actPage;
144
3.42M
    m_document->getGraphParser()->sendObjects(sendData);
145
3.42M
  }
146
3.42M
  m_document->getInput()->seek(pos, librevenge::RVNG_SEEK_SET);
147
3.42M
}
148
149
////////////////////////////////////////////////////////////
150
// the parser
151
////////////////////////////////////////////////////////////
152
void MsWksDRParser::parse(librevenge::RVNGDrawingInterface *docInterface)
153
27.0k
{
154
27.0k
  if (!checkHeader(nullptr) || !m_document || !m_document->getInput())  throw(libmwaw::ParseException());
155
27.0k
  bool ok = true;
156
27.0k
  try {
157
    // create the asciiFile
158
27.0k
    m_document->initAsciiFile(asciiName());
159
160
27.0k
    checkHeader(nullptr);
161
27.0k
    ok = createZones();
162
27.0k
    if (ok) {
163
9.67k
      createDocument(docInterface);
164
3.43M
      for (int i=0; i< m_state->m_numPages; ++i)
165
3.42M
        newPage(i);
166
#if 0 && defined(DEBUG)
167
      if (version()<=3)
168
        m_document->getTextParser3()->flushExtra();
169
      m_document->getGraphParser()->flushExtra();
170
#endif
171
9.67k
    }
172
27.0k
    m_document->ascii().reset();
173
27.0k
  }
174
27.0k
  catch (...) {
175
0
    MWAW_DEBUG_MSG(("MsWksDRParser::parse: exception catched when parsing\n"));
176
0
    ok = false;
177
0
  }
178
179
27.0k
  resetGraphicListener();
180
27.0k
  if (!ok) throw(libmwaw::ParseException());
181
27.0k
}
182
183
////////////////////////////////////////////////////////////
184
// create the document
185
////////////////////////////////////////////////////////////
186
void MsWksDRParser::createDocument(librevenge::RVNGDrawingInterface *documentInterface)
187
9.67k
{
188
9.67k
  if (!documentInterface) return;
189
9.67k
  if (getGraphicListener()) {
190
0
    MWAW_DEBUG_MSG(("MsWksDRParser::createDocument: listener already exist\n"));
191
0
    return;
192
0
  }
193
194
9.67k
  std::vector<MWAWPageSpan> pageList;
195
9.67k
  m_state->m_actPage = 0;
196
9.67k
  m_document->getPageSpanList(pageList, m_state->m_numPages);
197
9.67k
  MWAWGraphicListenerPtr listen(new MWAWGraphicListener(*getParserState(), pageList, documentInterface));
198
9.67k
  setGraphicListener(listen);
199
9.67k
  listen->startDocument();
200
  // time to send page information the graph parser and the text parser
201
9.67k
  m_document->getGraphParser()->setPageLeftTop
202
9.67k
  (MWAWVec2f(72.f *float(getPageSpan().getMarginLeft()),
203
9.67k
             72.f *float(getPageSpan().getMarginTop())+m_document->getHeaderFooterHeight(true)));
204
9.67k
}
205
206
////////////////////////////////////////////////////////////
207
//
208
// Intermediate level
209
//
210
////////////////////////////////////////////////////////////
211
bool MsWksDRParser::createZones()
212
27.0k
{
213
27.0k
  if (getInput()->isStructured())
214
1.95k
    m_document->createOLEZones(getInput());
215
27.0k
  MWAWInputStreamPtr input = m_document->getInput();
216
27.0k
  long pos = input->tell();
217
27.0k
  if (!m_document->readDocumentInfo(0x9a))
218
7.37k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
219
27.0k
  if (m_document->hasHeader() && !m_document->readGroupHeaderFooter(true,99))
220
1.77k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
221
27.0k
  pos = input->tell();
222
27.0k
  if (m_document->hasFooter() && !m_document->readGroupHeaderFooter(false,99))
223
8.43k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
224
225
27.0k
  if (!readDrawHeader()) return false;
226
227
11.8k
  libmwaw::DebugFile &ascFile = m_document->ascii();
228
11.8k
  auto &typeZoneMap=m_document->getTypeZoneMap();
229
11.8k
  MWAWEntry group;
230
231
  // now the main group of draw shape
232
11.8k
  m_state->m_mainZoneId= version()==4 ? 0 : m_document->getNewZoneId();
233
11.8k
  typeZoneMap.insert(std::multimap<int, MsWksDocument::Zone>::value_type
234
11.8k
                     (MsWksDocument::Z_MAIN,MsWksDocument::Zone(MsWksDocument::Z_MAIN, m_state->m_mainZoneId)));
235
11.8k
  if (version()==4) {
236
3.43k
    pos=input->tell();
237
3.43k
    int id=m_document->getNewZoneId();
238
3.43k
    typeZoneMap.insert(std::multimap<int, MsWksDocument::Zone>::value_type
239
3.43k
                       (MsWksDocument::Z_NONE,MsWksDocument::Zone(MsWksDocument::Z_NONE, id)));
240
3.43k
    group.setId(m_state->m_mainZoneId);
241
3.43k
    group.setName("RBIL");
242
3.43k
    if (!m_document->m_graphParser->readRB(input,group,1)) {
243
571
      MWAW_DEBUG_MSG(("MsWksDRParser::createZones: can not read RBIL group\n"));
244
571
      ascFile.addPos(pos);
245
571
      ascFile.addNote("Entries(RBIL):###");
246
571
      return false;
247
571
    }
248
3.43k
  }
249
250
11.2k
  pos=input->tell();
251
11.2k
  group.setId(m_state->m_mainZoneId);
252
11.2k
  group.setName("RBDR");
253
11.2k
  if (!m_document->m_graphParser->readRB(input,group,1)) {
254
1.60k
    MWAW_DEBUG_MSG(("MsWksDRParser::createZones: can not read RBDR group\n"));
255
1.60k
    ascFile.addPos(pos);
256
1.60k
    ascFile.addNote("Entries(RBDR):###");
257
1.60k
    return false;
258
1.60k
  }
259
260
  // normally, the file is now parsed, let check for potential remaining zones
261
9.67k
  if (!input->isEnd()) {
262
8.15k
    MWAW_DEBUG_MSG(("MsWksDRParser::createZones: find some extra data\n"));
263
57.5k
    while (!input->isEnd()) {
264
57.4k
      pos = input->tell();
265
57.4k
      MsWksDocument::Zone unknown;
266
57.4k
      if (!m_document->readZone(unknown) || input->tell()<=pos) {
267
8.08k
        ascFile.addPos(pos);
268
8.08k
        ascFile.addNote("Entries(End)");
269
8.08k
        ascFile.addPos(pos+100);
270
8.08k
        ascFile.addNote("_");
271
8.08k
        break;
272
8.08k
      }
273
57.4k
    }
274
8.15k
  }
275
276
9.67k
  std::vector<int> linesH, pagesH;
277
9.67k
  m_document->getGraphParser()->computePositions(m_state->m_mainZoneId, linesH, pagesH);
278
279
9.67k
  return true;
280
11.2k
}
281
282
////////////////////////////////////////////////////////////
283
//
284
// Low level
285
//
286
////////////////////////////////////////////////////////////
287
288
////////////////////////////////////////////////////////////
289
// read the header
290
////////////////////////////////////////////////////////////
291
bool MsWksDRParser::readDrawHeader()
292
27.0k
{
293
27.0k
  MWAWInputStreamPtr input=m_document->getInput();
294
295
27.0k
  int const vers=version();
296
27.0k
  long pos = input->tell();
297
27.0k
  auto N = static_cast<int>(input->readULong(2));
298
27.0k
  int headerSize = vers == 3 ? 4 : 88;
299
27.0k
  int dataSize = vers == 3 ? 4 : 51;
300
27.0k
  libmwaw::DebugStream f;
301
27.0k
  f << "FileHeader(A)";
302
27.0k
  if (!input->checkPosition(pos+headerSize+dataSize*N)) {
303
11.5k
    f << "###";
304
11.5k
    MWAW_DEBUG_MSG(("MsWksDRParser::readDrawHeader: Unknown header find N=%d\n", N));
305
11.5k
    m_document->ascii().addPos(pos);
306
11.5k
    m_document->ascii().addNote(f.str().c_str());
307
11.5k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
308
11.5k
    return false;
309
11.5k
  }
310
311
15.4k
  f << "N = " << N << ", v0 = " << input->readLong(2);
312
  // version 4
313
  // begin always by
314
  // v0=1, v1=[2|6|9], v2=180, v3=1, v6=0x300, v7=0x400, v8=1, v11=0x300, v12=0x400, v14=255
315
  // followed by
316
  // v16=4003, v20=8, v31=100
317
  // or v15=6, v16=-1, v17=-1, v19=0x300, v20=8, v21=1, v24=44, v25=6, v31=100
318
15.4k
  if (vers == 4) {
319
180k
    for (int i = 1; i < 35; i++) {
320
175k
      auto val = static_cast<int>(input->readLong(2));
321
175k
      if (val) f << ", v" << i << "=" << val;
322
175k
    }
323
    //    [0,0,1,1,0,0,0,0,1,1,0,1,1,1,1,0,]
324
    // or [0,0,3,1,0,0,0,0,1,1,0,1,1,1,1,0,]
325
5.17k
    f << ",fl=[";
326
87.9k
    for (int i = 0; i < 16; i++) {
327
82.7k
      f << input->readLong(1) << ",";
328
82.7k
    }
329
5.17k
    f << "]";
330
5.17k
  }
331
15.4k
  m_document->ascii().addPos(pos);
332
15.4k
  m_document->ascii().addNote(f.str().c_str());
333
334
15.4k
  input->seek(pos+headerSize,librevenge::RVNG_SEEK_SET);
335
17.3k
  for (int i = 0; i < N; i++) {
336
5.49k
    pos = input->tell();
337
5.49k
    f.str("");
338
5.49k
    f << "FileHeader(A)[" << i << "]:";
339
5.49k
    auto v = static_cast<int>(input->readULong(2));    // normally 0xe or 0x800e
340
5.49k
    auto id = static_cast<int>(input->readLong(2));
341
5.49k
    f << std::hex << v << std::dec;
342
343
5.49k
    if (id != i+1) {
344
3.61k
      MWAW_DEBUG_MSG(("MsWksDRParser::readDrawHeader: bad data %i\n", id));
345
3.61k
      f << "###";
346
3.61k
      m_document->ascii().addPos(pos);
347
3.61k
      m_document->ascii().addNote(f.str().c_str());
348
3.61k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
349
3.61k
      return false;
350
3.61k
    }
351
352
1.87k
    if (vers==4) {
353
      // always v0=255, v2=4003, v6=8, v16=-1, w2=3, w4=4
354
36.3k
      for (int j = 0; j < 20; j++) {
355
34.6k
        auto val = static_cast<int>(input->readLong(2));
356
34.6k
        if (val) f << ", v" << j << "=" << val;
357
34.6k
      }
358
13.8k
      for (int j = 0; j < 7; j++) {
359
12.1k
        auto val = static_cast<int>(input->readLong(1));
360
12.1k
        if (val) f << ", w" << j << "=" << val;
361
12.1k
      }
362
1.73k
    }
363
364
1.87k
    m_document->ascii().addPos(pos);
365
1.87k
    m_document->ascii().addNote(f.str().c_str());
366
1.87k
    input->seek(pos+dataSize, librevenge::RVNG_SEEK_SET);
367
1.87k
  }
368
369
11.8k
  return true;
370
15.4k
}
371
372
////////////////////////////////////////////////////////////
373
// read the header
374
////////////////////////////////////////////////////////////
375
bool MsWksDRParser::checkHeader(MWAWHeader *header, bool strict)
376
100k
{
377
100k
  *m_state = MsWksDRParserInternal::State();
378
100k
  if (!m_document || !m_document->checkHeader3(header, strict)) return false;
379
83.6k
  if (m_document->getKind() != MWAWDocument::MWAW_K_DRAW)
380
1
    return false;
381
83.6k
  if (version() < 2 || version() > 4)
382
130
    return false;
383
83.5k
  return true;
384
83.6k
}
385
386
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: