Coverage Report

Created: 2026-09-06 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/MsWks4Zone.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 <vector>
37
38
#include <librevenge/librevenge.h>
39
40
#include "MWAWTextListener.hxx"
41
#include "MWAWFont.hxx"
42
#include "MWAWFontConverter.hxx"
43
#include "MWAWPosition.hxx"
44
#include "MWAWPrinter.hxx"
45
#include "MWAWSection.hxx"
46
#include "MWAWSubDocument.hxx"
47
48
#include "MsWksGraph.hxx"
49
#include "MsWksDocument.hxx"
50
51
#include "MsWks4Text.hxx"
52
53
#include "MsWks4Zone.hxx"
54
55
56
/** Internal: the structures of a MsWks4Zone */
57
namespace MsWks4ZoneInternal
58
{
59
/**Internal: some data are dupplicated, an enum to know which picture to ignored.
60
 *
61
 * For intance, the header text is stored in a separate ole, and we find
62
 * in the main ole, a picture which represents the headers */
63
enum { IgnoreFrame = -4 };
64
65
//! Internal: a frame ( position, type, ...)
66
struct Frame {
67
  //! the type of the frame which can represent header, footer, textbox, ...
68
  enum Type { Unknown = 0, Header, Footer, Table, Object, Textbox };
69
70
  //! constructor
71
  Frame()
72
1.80k
    : m_type(Unknown)
73
1.80k
    , m_position()
74
1.80k
    , m_pictId()
75
1.80k
    , m_error("")
76
1.80k
  {
77
1.80k
    m_position.setPage(-3);
78
1.80k
  }
79
  //! operator<<
80
  friend std::ostream &operator<<(std::ostream &o, Frame const &ft);
81
82
  //! the frame type
83
  Type m_type;
84
85
  //! the position of the frame in the document
86
  MWAWPosition m_position;
87
  //! some frames are associated with a picture stored in this entry ( name + id)
88
  MWAWEntry m_pictId;
89
  //! a string to store unparsed data
90
  std::string m_error;
91
};
92
//! friend operator<< for frame
93
std::ostream &operator<<(std::ostream &o, Frame const &ft)
94
0
{
95
0
  switch (ft.m_type) {
96
0
  case Frame::Header:
97
0
    o<< "header,";
98
0
    break;
99
0
  case Frame::Footer:
100
0
    o<< "footer,";
101
0
    break;
102
0
  case Frame::Table:
103
0
    o<< "table,";
104
0
    break;
105
0
  case Frame::Textbox:
106
0
    o<< "textbox,";
107
0
    break;
108
0
  case Frame::Object:
109
0
    o<< "object,";
110
0
    break;
111
0
  case Frame::Unknown:
112
#if !defined(__clang__)
113
  default:
114
#endif
115
0
    break;
116
0
  }
117
118
0
  int page = ft.m_position.page();
119
0
  switch (page) {
120
0
  case -1:
121
0
    o << "allpages,";
122
0
    break;
123
0
  case -2:
124
0
    o << "undef,";
125
0
    break;
126
0
  case -3:
127
0
    o << "def,";
128
0
    break;
129
0
  default:
130
0
    if (page <= 0) o << "###page=" << page << ",";
131
0
    break;
132
0
  }
133
0
  if (ft.m_pictId.name().length())
134
0
    o << "pict='" << ft.m_pictId.name() << "':" << ft.m_pictId.id() << ",";
135
136
0
  o << ft.m_position;
137
0
  if (!ft.m_error.empty()) o << "errors=(" << ft.m_error << ")";
138
0
  return o;
139
0
}
140
141
//!Internal: the state of a MsWks4Zone
142
struct State {
143
  //! constructor
144
  State()
145
25.1k
    : m_mainOle(false)
146
25.1k
    , m_numColumns(1)
147
25.1k
    , m_hasColumnSep(false)
148
25.1k
    , m_parsed(false)
149
25.1k
    , m_actPage(0)
150
25.1k
    , m_numPages(0)
151
25.1k
    , m_defFont(20,12)
152
25.1k
    , m_framesList()
153
25.1k
  {
154
25.1k
  }
155
156
  //! true if we parse the main MN0
157
  bool m_mainOle;
158
159
  //! the number of column
160
  int m_numColumns;
161
  //! true if a line is added to separated the column
162
  bool m_hasColumnSep;
163
  //! a flag to known if the ole is already parsed
164
  bool m_parsed;
165
166
  int m_actPage /** the actual page*/, m_numPages /* the number of pages */;
167
168
  //! the default font
169
  MWAWFont m_defFont;
170
171
  //! the list of frames
172
  std::vector<Frame> m_framesList;
173
174
};
175
}
176
177
////////////////////////////////////////////////////////////
178
// constructor/destructor, ...
179
////////////////////////////////////////////////////////////
180
MsWks4Zone::MsWks4Zone(MWAWInputStreamPtr const &input, MWAWParserStatePtr const &parserState,
181
                       MWAWParser &parser, std::string const &oleName)
182
25.1k
  : m_mainParser(&parser)
183
25.1k
  , m_parserState(parserState)
184
25.1k
  , m_state()
185
25.1k
  , m_document()
186
25.1k
{
187
25.1k
  m_document.reset(new MsWksDocument(input, parser));
188
25.1k
  setAscii(oleName);
189
25.1k
  m_parserState->m_version=4;
190
25.1k
  init();
191
25.1k
}
192
193
MsWks4Zone::~MsWks4Zone()
194
25.1k
{
195
25.1k
}
196
197
////////////////////////////////////////////////////////////
198
// small helper to manage interaction between parent and child, ...
199
////////////////////////////////////////////////////////////
200
void MsWks4Zone::init()
201
25.1k
{
202
25.1k
  m_state.reset(new MsWks4ZoneInternal::State);
203
25.1k
  m_document->getTextParser4()->setDefault(m_state->m_defFont);
204
25.1k
}
205
206
MWAWInputStreamPtr MsWks4Zone::getInput()
207
0
{
208
0
  return m_document->m_input;
209
0
}
210
211
void MsWks4Zone::setAscii(std::string const &oleName)
212
25.1k
{
213
25.1k
  std::string fName = libmwaw::Debug::flattenFileName(oleName);
214
25.1k
  m_document->initAsciiFile(fName);
215
25.1k
}
216
217
libmwaw::DebugFile &MsWks4Zone::ascii()
218
0
{
219
0
  return m_document->ascii();
220
0
}
221
222
void MsWks4Zone::readFootNote(int id)
223
301
{
224
301
  m_document->getTextParser4()->readFootNote(m_document->getInput(), id);
225
301
}
226
227
////////////////////////////////////////////////////////////
228
// position and height
229
////////////////////////////////////////////////////////////
230
double MsWks4Zone::getTextHeight() const
231
4.77k
{
232
4.77k
  return m_parserState->m_pageSpan.getPageLength();
233
4.77k
}
234
235
////////////////////////////////////////////////////////////
236
// text positions
237
////////////////////////////////////////////////////////////
238
void MsWks4Zone::newPage(int number, bool /*soft*/)
239
1.70k
{
240
1.70k
  if (number <= m_state->m_actPage || number > m_state->m_numPages)
241
0
    return;
242
243
1.70k
  long pos = m_document->getInput()->tell();
244
4.61k
  while (m_state->m_actPage < number) {
245
2.91k
    m_state->m_actPage++;
246
2.91k
    if (!m_parserState->getMainListener() || m_state->m_actPage == 1)
247
1.21k
      continue;
248
    // FIXME: find a way to force the page break to happen
249
    //    ie. graphParser must add a space to force it :-~
250
1.70k
    if (m_state->m_mainOle) m_parserState->getMainListener()->insertBreak(MWAWTextListener::PageBreak);
251
252
1.70k
    MsWksGraph::SendData sendData;
253
1.70k
    sendData.m_type = MsWksGraph::SendData::RBDR;
254
1.70k
    sendData.m_anchor =  MWAWPosition::Page;
255
1.70k
    sendData.m_page = m_state->m_actPage;
256
1.70k
    m_document->getGraphParser()->sendObjects(sendData);
257
1.70k
  }
258
1.70k
  m_document->getInput()->seek(pos, librevenge::RVNG_SEEK_SET);
259
1.70k
}
260
261
MWAWEntry MsWks4Zone::getTextPosition() const
262
10.6k
{
263
10.6k
  return m_document->getTextParser4()->m_textPositions;
264
10.6k
}
265
266
////////////////////////////////////////////////////////////
267
// create the main listener ( given a header and a footer document)
268
////////////////////////////////////////////////////////////
269
MWAWTextListenerPtr MsWks4Zone::createListener(librevenge::RVNGTextInterface *interface)
270
4.77k
{
271
4.77k
  std::vector<MWAWPageSpan> pageList;
272
4.77k
  m_state->m_actPage = 0;
273
4.77k
  m_document->getPageSpanList(pageList, m_state->m_numPages);
274
4.77k
  MWAWTextListenerPtr res(new MWAWTextListener(*m_parserState, pageList, interface));
275
276
  // ok, now we can update the page position
277
4.77k
  std::vector<int> linesH, pagesH;
278
4.77k
  pagesH.resize(size_t(m_state->m_numPages), int(72.*getTextHeight()));
279
4.77k
  m_document->getGraphParser()->computePositions(-1, linesH, pagesH);
280
4.77k
  m_document->getGraphParser()->setPageLeftTop
281
4.77k
  (MWAWVec2f(72.f *float(m_parserState->m_pageSpan.getMarginLeft()),
282
4.77k
             72.f *float(m_parserState->m_pageSpan.getMarginTop())));
283
284
4.77k
  return res;
285
4.77k
}
286
287
////////////////////////////////////////////////////////////
288
//
289
// entry management
290
//
291
////////////////////////////////////////////////////////////
292
bool MsWks4Zone::parseHeaderIndexEntry(MWAWInputStreamPtr &input)
293
146k
{
294
146k
  long pos = input->tell();
295
146k
  m_document->ascii().addPos(pos);
296
297
146k
  libmwaw::DebugStream f;
298
299
146k
  auto cch = uint16_t(input->readULong(2));
300
301
  // check if the entry can be read
302
146k
  input->seek(pos + cch, librevenge::RVNG_SEEK_SET);
303
146k
  if (input->tell() != pos+cch) {
304
3.16k
    MWAW_DEBUG_MSG(("MsWks4Zone:parseHeaderIndexEntry error: incomplete entry\n"));
305
3.16k
    m_document->ascii().addNote("###IndexEntry incomplete (ignored)");
306
3.16k
    return false;
307
3.16k
  }
308
143k
  input->seek(pos + 2, librevenge::RVNG_SEEK_SET);
309
310
143k
  if (0x18 != cch) {
311
2.51k
    if (cch < 0x18) {
312
875
      input->seek(pos + cch, librevenge::RVNG_SEEK_SET);
313
875
      m_document->ascii().addNote("MsWks4Zone:parseHeaderIndexEntry: ###IndexEntry too short(ignored)");
314
875
      if (cch < 10) throw libmwaw::ParseException();
315
136
      return true;
316
875
    }
317
2.51k
  }
318
319
142k
  std::string name;
320
321
  // sanity check
322
704k
  for (size_t i =0; i < 4; i++) {
323
564k
    name.append(1, char(input->readULong(1)));
324
325
564k
    if (name[i] != 0 && name[i] != 0x20 &&
326
535k
        (41 > static_cast<uint8_t>(name[i]) || static_cast<uint8_t>(name[i]) > 90)) {
327
1.96k
      MWAW_DEBUG_MSG(("MsWks4Zone:parseHeaderIndexEntry: bad character=%u (0x%02x) in name in header index\n",
328
1.96k
                      static_cast<unsigned int>(name[i]), static_cast<unsigned int>(name[i])));
329
1.96k
      m_document->ascii().addNote("###IndexEntry bad name(ignored)");
330
331
1.96k
      input->seek(pos + cch, librevenge::RVNG_SEEK_SET);
332
1.96k
      return true;
333
1.96k
    }
334
564k
  }
335
336
140k
  f << "Entries("<<name<<")";
337
140k
  if (cch != 24) f << ", ###size=" << int(cch);
338
140k
  auto id = static_cast<int>(input->readULong(2));
339
140k
  f << ", id=" << id << ", (";
340
420k
  for (int i = 0; i < 2; i ++) {
341
280k
    auto val = static_cast<int>(input->readLong(2));
342
280k
    f << val << ",";
343
280k
  }
344
345
140k
  std::string name2;
346
700k
  for (size_t i =0; i < 4; i++)
347
560k
    name2.append(1, char(input->readULong(1)));
348
140k
  f << "), " << name2;
349
350
140k
  MWAWEntry hie;
351
140k
  hie.setName(name);
352
140k
  hie.setType(name2);
353
140k
  hie.setId(id);
354
140k
  hie.setBegin(long(input->readULong(4)));
355
140k
  hie.setLength(long(input->readULong(4)));
356
357
140k
  f << ", offset=" << std::hex << hie.begin() << ", length=" << hie.length();
358
359
140k
  if (cch != 0x18) {
360
565
    m_document->ascii().addDelimiter(pos+0x18, '|');
361
565
    f << ",#extraData";
362
565
  }
363
364
140k
  input->seek(hie.end(), librevenge::RVNG_SEEK_SET);
365
140k
  if (input->tell() != hie.end()) {
366
23.1k
    f << ", ###ignored";
367
23.1k
    m_document->ascii().addNote(f.str().c_str());
368
23.1k
    input->seek(pos + cch, librevenge::RVNG_SEEK_SET);
369
23.1k
    return true;
370
23.1k
  }
371
372
373
117k
  m_document->getEntryMap().insert(std::multimap<std::string, MWAWEntry>::value_type(name, hie));
374
375
117k
  m_document->ascii().addPos(pos);
376
117k
  m_document->ascii().addNote(f.str().c_str());
377
378
117k
  m_document->ascii().addPos(hie.begin());
379
117k
  f.str("");
380
117k
  f << name;
381
117k
  if (name != name2) f << "/" << name2;
382
117k
  f << ":" << std::dec << id;
383
117k
  m_document->ascii().addNote(f.str().c_str());
384
385
117k
  m_document->ascii().addPos(hie.end());
386
117k
  m_document->ascii().addNote("_");
387
388
117k
  input->seek(pos + cch, librevenge::RVNG_SEEK_SET);
389
117k
  return true;
390
140k
}
391
392
bool MsWks4Zone::parseHeaderIndex(MWAWInputStreamPtr &input)
393
19.4k
{
394
19.4k
  m_document->getEntryMap().clear();
395
19.4k
  input->seek(0x08, librevenge::RVNG_SEEK_SET);
396
397
19.4k
  long pos = input->tell();
398
19.4k
  auto i0 = static_cast<int>(input->readLong(2));
399
19.4k
  auto i1 = static_cast<int>(input->readLong(2));
400
19.4k
  auto n_entries = static_cast<uint16_t>(input->readULong(2));
401
  // fixme: sanity check n_entries
402
403
19.4k
  libmwaw::DebugStream f;
404
19.4k
  f << "Header: N=" << n_entries << ", " << i0 << ", " << i1 << "(";
405
406
97.0k
  for (int i = 0; i < 4; i++)
407
77.6k
    f << std::hex << input->readLong(2) << ",";
408
19.4k
  f << "), ";
409
19.4k
  f << "end=" << std::hex << input->readLong(2);
410
411
19.4k
  m_document->ascii().addPos(pos);
412
19.4k
  m_document->ascii().addNote(f.str().c_str());
413
414
19.4k
  input->seek(0x18, librevenge::RVNG_SEEK_SET);
415
19.4k
  bool readSome = false;
416
19.4k
  do {
417
19.4k
    if (input->isEnd()) return readSome;
418
419
19.3k
    pos = input->tell();
420
19.3k
    f.str("");
421
19.3k
    auto unknown1 = static_cast<uint16_t>(input->readULong(2)); // function of the size of the entries ?
422
423
19.3k
    auto n_entries_local = static_cast<uint16_t>(input->readULong(2));
424
19.3k
    f << "Header("<<std::hex << unknown1<<"): N=" << std::dec << n_entries_local;
425
426
19.3k
    if (n_entries_local > 0x20) {
427
1.80k
      MWAW_DEBUG_MSG(("MsWks4Zone::parseHeaderIndex: error: n_entries_local=%i\n", n_entries_local));
428
1.80k
      return readSome;
429
1.80k
    }
430
431
17.5k
    auto next_index_table = static_cast<uint32_t>(input->readULong(4));
432
17.5k
    f << std::hex << ", nextHeader=" << next_index_table;
433
17.5k
    if (next_index_table != 0xFFFFFFFF && long(next_index_table) < pos) {
434
1.71k
      MWAW_DEBUG_MSG(("MsWks4Zone::parseHeaderIndex: error: next_index_table=%x decreasing !!!\n", next_index_table));
435
1.71k
      return readSome;
436
1.71k
    }
437
438
15.8k
    m_document->ascii().addPos(pos);
439
15.8k
    m_document->ascii().addNote(f.str().c_str());
440
441
146k
    do {
442
146k
      if (!parseHeaderIndexEntry(input)) return readSome;
443
444
143k
      readSome=true;
445
143k
      n_entries--;
446
143k
      n_entries_local--;
447
143k
    }
448
143k
    while (n_entries > 0 && n_entries_local);
449
450
12.7k
    if (0xFFFFFFFF == next_index_table && n_entries > 0) {
451
469
      MWAW_DEBUG_MSG(("MsWks4Zone::parseHeaderIndex: error: expected more header index entries\n"));
452
469
      return true;
453
469
    }
454
455
12.2k
    if (0xFFFFFFFF == next_index_table) break;
456
929
    if (long(next_index_table) < input->tell()) {
457
26
      MWAW_DEBUG_MSG(("MsWks4Zone::parseHeaderIndex: error: next_index_table=%x decreasing !!!\n", next_index_table));
458
26
      return readSome;
459
26
    }
460
461
903
    if (input->seek(long(next_index_table), librevenge::RVNG_SEEK_SET) != 0) return readSome;
462
903
  }
463
19.4k
  while (n_entries > 0);
464
465
12.1k
  return true;
466
19.4k
}
467
468
////////////////////////////////////////////////////////////
469
// parse the ole part and create the main part
470
////////////////////////////////////////////////////////////
471
bool MsWks4Zone::createZones(bool mainOle)
472
25.0k
{
473
25.0k
  if (m_state->m_parsed) return true;
474
475
19.4k
  auto &entryMap=m_document->getEntryMap();
476
19.4k
  MWAWInputStreamPtr input = m_document->getInput();
477
19.4k
  m_state->m_parsed = true;
478
479
19.4k
  entryMap.clear();
480
481
19.4k
  m_document->ascii().addPos(0);
482
19.4k
  m_document->ascii().addNote("FileHeader");
483
  /* header index */
484
19.4k
  if (!parseHeaderIndex(input)) return false;
485
  // the text structure
486
15.4k
  if (!m_document->getTextParser4()->readStructures(input, mainOle)) return !mainOle;
487
488
  // DOP, PRNT
489
15.4k
  auto pos = entryMap.find("PRNT");
490
15.4k
  if (entryMap.end() != pos) {
491
3.97k
    pos->second.setParsed(true);
492
3.97k
    MWAWPageSpan page;
493
3.97k
    if (readPRNT(input, pos->second, page) && mainOle) m_parserState->m_pageSpan = page;
494
3.97k
  }
495
15.4k
  pos = entryMap.find("DOP ");
496
15.4k
  if (entryMap.end() != pos) {
497
10.6k
    MWAWPageSpan page;
498
10.6k
    if (readDOP(input, pos->second, page) && mainOle) m_parserState->m_pageSpan = page;
499
10.6k
  }
500
501
  // RLRB
502
15.4k
  pos = entryMap.lower_bound("RLRB");
503
15.9k
  while (pos != entryMap.end()) {
504
14.1k
    MWAWEntry const &entry = pos++->second;
505
14.1k
    if (!entry.hasName("RLRB")) break;
506
489
    if (!entry.hasType("RLRB")) continue;
507
508
387
    readRLRB(input, entry);
509
387
  }
510
511
  // SELN
512
15.4k
  pos = entryMap.lower_bound("SELN");
513
16.0k
  while (entryMap.end() != pos) {
514
14.2k
    MWAWEntry const &entry = pos++->second;
515
14.2k
    if (!entry.hasName("SELN")) break;
516
538
    if (!entry.hasType("SELN")) continue;
517
528
    readSELN(input, entry);
518
528
  }
519
520
  // FRAM
521
15.4k
  m_state->m_framesList.resize(0);
522
15.4k
  pos = entryMap.lower_bound("FRAM");
523
16.6k
  while (entryMap.end() != pos) {
524
14.8k
    MWAWEntry const &entry = pos++->second;
525
14.8k
    if (!entry.hasName("FRAM")) break;
526
1.17k
    if (!entry.hasType("FRAM")) continue;
527
1.11k
    readFRAM(input, entry);
528
1.11k
  }
529
530
  /* Graph data */
531
15.4k
  pos = entryMap.lower_bound("RBDR");
532
19.3k
  while (pos != entryMap.end()) {
533
17.6k
    MWAWEntry const &entry = pos++->second;
534
17.6k
    if (!entry.hasName("RBDR")) break;
535
3.92k
    if (!entry.hasType("RBDR")) continue;
536
537
3.87k
    if (m_document->getGraphParser()->readRB(input, entry, 0))
538
3.82k
      entry.setParsed(true);
539
3.87k
  }
540
15.4k
  pos = entryMap.lower_bound("RBIL");
541
15.4k
  while (pos != entryMap.end()) {
542
13.7k
    MWAWEntry const &entry = pos++->second;
543
13.7k
    if (!entry.hasName("RBIL")) break;
544
3
    if (!entry.hasType("RBIL")) continue;
545
546
0
    if (m_document->getGraphParser()->readRB(input, entry, 0))
547
0
      entry.setParsed(true);
548
0
  }
549
550
  /* read the pictures */
551
  // in the main block, pict are used to representant the
552
  // header/footer, so we skip it.
553
  // In the others block, maybe there can be interesting, so, we read them
554
15.4k
  pos = entryMap.lower_bound("PICT");
555
15.4k
  while (pos != entryMap.end()) {
556
13.7k
    MWAWEntry const &entry = pos++->second;
557
13.7k
    if (!entry.hasName("PICT")) break;
558
0
    m_document->getGraphParser()->readPictureV4(input, entry);
559
0
  }
560
15.4k
  return true;
561
15.4k
}
562
563
////////////////////////////////////////////////////////////
564
// send all the data corresponding to a zone
565
////////////////////////////////////////////////////////////
566
void MsWks4Zone::readContentZones(MWAWEntry const &entry, bool mainOle)
567
11.2k
{
568
11.2k
  MWAWInputStreamPtr input = m_document->getInput();
569
11.2k
  bool oldMain = m_state->m_mainOle;
570
11.2k
  m_state->m_mainOle = mainOle;
571
572
11.2k
  MsWksGraph::SendData sendData;
573
11.2k
  sendData.m_type = MsWksGraph::SendData::RBDR;
574
11.2k
  sendData.m_anchor = mainOle ? MWAWPosition::Page : MWAWPosition::Paragraph;
575
11.2k
  sendData.m_page = 0;
576
11.2k
  m_document->getGraphParser()->sendObjects(sendData);
577
578
11.2k
  if (mainOle && m_parserState->getMainListener() && m_state->m_numColumns > 1) {
579
20
    if (m_parserState->getMainListener()->isSectionOpened())
580
0
      m_parserState->getMainListener()->closeSection();
581
20
    MWAWSection sec;
582
20
    sec.setColumns(m_state->m_numColumns, m_mainParser->getPageWidth()/double(m_state->m_numColumns), librevenge::RVNG_INCH);
583
20
    if (m_state->m_hasColumnSep)
584
0
      sec.m_columnSeparator=MWAWBorder();
585
20
    m_parserState->getMainListener()->openSection(sec);
586
20
  }
587
588
11.2k
  MWAWEntry ent(entry);
589
11.2k
  if (!ent.valid()) ent = m_document->getTextParser4()->m_textPositions;
590
11.2k
  m_document->getTextParser4()->readText(input, ent, mainOle);
591
592
11.2k
  if (!mainOle) {
593
6.51k
    m_state->m_mainOle = oldMain;
594
6.51k
    return;
595
6.51k
  }
596
597
  // send the final data
598
#ifdef DEBUG
599
  newPage(m_state->m_numPages);
600
  m_document->getTextParser4()->flushExtra(input);
601
602
  sendData.m_type = MsWksGraph::SendData::ALL;
603
  sendData.m_anchor = MWAWPosition::Char;
604
  m_document->getGraphParser()->sendObjects(sendData);
605
#endif
606
4.77k
  m_state->m_mainOle = oldMain;
607
608
#ifdef DEBUG
609
  // check if we have parsed all zones
610
  auto const &entryMap=m_document->getEntryMap();
611
612
  for (auto const &it : entryMap) {
613
    MWAWEntry const &zone = it.second;
614
    if (zone.isParsed() ||
615
        zone.hasName("TEXT") || // TEXT entries are managed directly by MsWks4Text
616
        zone.hasName("INK ")) // INK Zone are ignored: always = 2*99
617
      continue;
618
    MWAW_DEBUG_MSG(("MsWks4Zone::readContentZones: WARNING zone %s ignored\n",
619
                    zone.name().c_str()));
620
  }
621
#endif
622
4.77k
}
623
624
////////////////////////////////////////////////////////////
625
//
626
// low level
627
//
628
////////////////////////////////////////////////////////////
629
630
////////////////////////////////////////////////////////////
631
// the printer definition
632
////////////////////////////////////////////////////////////
633
bool MsWks4Zone::readPRNT(MWAWInputStreamPtr input, MWAWEntry const &entry, MWAWPageSpan &page)
634
3.97k
{
635
3.97k
  page = MWAWPageSpan();
636
3.97k
  if (!entry.hasType("PRR ")) {
637
77
    MWAW_DEBUG_MSG(("Works: unknown PRNT type='%s'\n", entry.type().c_str()));
638
77
    return false;
639
77
  }
640
641
3.89k
  long debPos = entry.begin();
642
3.89k
  input->seek(debPos, librevenge::RVNG_SEEK_SET);
643
3.89k
  libmwaw::PrinterInfo info;
644
3.89k
  if (!info.read(input)) {
645
2.42k
    MWAW_DEBUG_MSG(("Works: error: can not read PRNT\n"));
646
2.42k
    return false;
647
2.42k
  }
648
1.47k
  else {
649
1.47k
    MWAWVec2i paperSize = info.paper().size();
650
1.47k
    MWAWVec2i pageSize = info.page().size();
651
1.47k
    MWAWVec2i margin = paperSize - pageSize;
652
1.47k
    margin.set(margin.x()/2, margin.y()/2);
653
654
1.47k
    page.setMarginTop(margin.y()/72.0);
655
1.47k
    page.setMarginBottom(margin.y()/72.0);
656
1.47k
    page.setMarginLeft(margin.x()/72.0);
657
1.47k
    page.setMarginRight(margin.x()/72.0);
658
1.47k
    page.setFormLength(pageSize.y()/72.);
659
1.47k
    page.setFormWidth(pageSize.x()/72.);
660
661
1.47k
    if (paperSize.y() > paperSize.x())
662
1.27k
      page.setFormOrientation(MWAWPageSpan::PORTRAIT);
663
202
    else
664
202
      page.setFormOrientation(MWAWPageSpan::LANDSCAPE);
665
666
1.47k
    libmwaw::DebugStream f;
667
1.47k
    f << info;
668
669
1.47k
    m_document->ascii().addPos(debPos);
670
1.47k
    m_document->ascii().addNote(f.str().c_str());
671
1.47k
  }
672
1.47k
  return true;
673
3.89k
}
674
675
////////////////////////////////////////////////////////////
676
// document
677
////////////////////////////////////////////////////////////
678
bool MsWks4Zone::readDOP(MWAWInputStreamPtr input, MWAWEntry const &entry, MWAWPageSpan &page)
679
10.6k
{
680
10.6k
  if (!entry.hasType("DOP ")) {
681
582
    MWAW_DEBUG_MSG(("Works: unknown DOP type='%s'\n", entry.type().c_str()));
682
582
    return false;
683
582
  }
684
10.0k
  entry.setParsed(true);
685
686
  // FIXME: update the page
687
10.0k
  page=MWAWPageSpan();
688
689
10.0k
  libmwaw::DebugStream f, f2;
690
691
10.0k
  long debPage = entry.begin();
692
10.0k
  long length = entry.length();
693
10.0k
  long endPage = entry.end();
694
695
10.0k
  input->seek(debPage, librevenge::RVNG_SEEK_SET);
696
10.0k
  auto sz = static_cast<int>(input->readULong(1));
697
10.0k
  if (sz != length-1) f << "###sz=" << sz << ",";
698
699
10.0k
  bool ok = true;
700
10.0k
  double dim[6] = {-1., -1., -1., -1., -1., -1. };
701
76.4k
  while (input->tell() < endPage) {
702
71.9k
    long debPos = input->tell();
703
71.9k
    auto val = static_cast<int>(input->readULong(1));
704
705
71.9k
    switch (val) {
706
5.30k
    case 0x41: // w (or 4000 when frame)
707
10.8k
    case 0x42: // h (or 4000 when frame)
708
16.0k
    case 0x43: // and margin (y,x)
709
21.1k
    case 0x44:
710
26.1k
    case 0x45:
711
31.1k
    case 0x46: {
712
31.1k
      if (debPos+2 > endPage) {
713
17
        ok = false;
714
17
        break;
715
17
      }
716
31.1k
      long d = static_cast<int>(input->readULong(2));
717
31.1k
      if (d == 4000) break;
718
30.2k
      dim[val-0x41] = double(d)/72.;
719
30.2k
      break;
720
31.1k
    }
721
722
65
    case 0x4d: { // cols-1 ?
723
65
      if (debPos+2 > endPage) {
724
1
        ok = false;
725
1
        break;
726
1
      }
727
64
      auto v = static_cast<int>(input->readULong(2));
728
64
      m_state->m_numColumns=v+1;
729
64
      if (v) f2 << "cols=" << m_state->m_numColumns << ",";
730
64
      break;
731
65
    }
732
97
    case 0x4f: {
733
97
      if (debPos+1 > endPage) {
734
0
        ok = false;
735
0
        break;
736
0
      }
737
97
      auto v = static_cast<int>(input->readLong(1));
738
97
      if (v==1) {
739
8
        m_state->m_hasColumnSep=true;
740
8
        f2 << "hasColSep,";
741
8
      }
742
89
      else if (v)
743
52
        f2 << "#hasColSeps=" <<  v << ",";
744
97
      break;
745
746
97
    }
747
4.89k
    case 0x60: // always 1 ?
748
10.3k
    case 0x61: {
749
10.3k
      if (debPos+2 > endPage) {
750
6
        ok = false;
751
6
        break;
752
6
      }
753
10.3k
      auto v = static_cast<int>(input->readULong(2));
754
10.3k
      f2 << "f" << val;
755
10.3k
      if (v != 1) f2 << "=" << v << "#";
756
10.3k
      f2 << ",";
757
10.3k
      break;
758
10.3k
    }
759
760
4.80k
    case 0x62: { // some flag : 1| 11| 31| 42| 44| 80 | 100 ?
761
4.80k
      if (debPos+2 > endPage) {
762
8
        ok = false;
763
8
        break;
764
8
      }
765
4.79k
      auto v = static_cast<int>(input->readULong(2));
766
4.79k
      f2 << "f" << val << "=" << std::hex << v << std::dec << ",";
767
4.79k
      break;
768
4.80k
    }
769
770
4.80k
    case 0x6c: // always follows by three 0xFFFF ? white color ?
771
4.80k
      if (debPos+6 > endPage) {
772
410
        ok = false;
773
410
        break;
774
410
      }
775
4.39k
      f2 << "bkcol?=(";
776
17.5k
      for (int i = 0; i < 3; i++) {
777
13.1k
        f2 << input->readULong(1) << ",";
778
13.1k
        input->readULong(1);
779
13.1k
      }
780
4.39k
      f2 << "),";
781
4.39k
      break;
782
783
5.45k
    case 0x48: // 12
784
10.3k
    case 0x4c: { // 36
785
10.3k
      if (debPos+2 > endPage) {
786
35
        ok = false;
787
35
        break;
788
35
      }
789
10.3k
      auto v = static_cast<int>(input->readLong(2));
790
10.3k
      f2 << "f" << val << "=" <<  v << ",";
791
10.3k
      break;
792
10.3k
    }
793
794
4.64k
    case 0x6a: { // 1
795
4.64k
      if (debPos+1 > endPage) {
796
0
        ok = false;
797
0
        break;
798
0
      }
799
4.64k
      auto v = static_cast<int>(input->readLong(1));
800
4.64k
      f2 << "f" << val << "=" <<  v << ",";
801
4.64k
      break;
802
4.64k
    }
803
804
42
    case 0x5f: // -1 if header|footer ?
805
283
    case 0x63: // -1 if header ?
806
363
    case 0x64: // -1 if footer ?
807
469
    case 0x66: // 1
808
571
    case 0x67: { // 1
809
571
      if (debPos+2 > endPage) {
810
1
        ok = false;
811
1
        break;
812
1
      }
813
570
      auto v = static_cast<int>(input->readLong(1));
814
570
      f2 << "f" << val << "=" <<  v << ",";
815
570
      break;
816
571
    }
817
818
5.07k
    default:
819
5.07k
      ok = false;
820
5.07k
      break;
821
71.9k
    }
822
823
71.9k
    if (ok) continue;
824
825
5.54k
    m_document->ascii().addPos(debPos);
826
5.54k
    m_document->ascii().addNote("DOP ###");
827
828
5.54k
    break;
829
71.9k
  }
830
10.0k
  bool dimOk=dim[1]>2.*(dim[2]+dim[4]) && dim[0]>2.*(dim[3]+dim[5]);
831
50.0k
  for (int i = 2; i < 6; i++)
832
40.0k
    dimOk = dimOk && dim[i]>=0;
833
10.0k
  if (dimOk) {
834
4.38k
    if (dim[1] > dim[0])
835
2.49k
      page.setFormOrientation(MWAWPageSpan::PORTRAIT);
836
1.89k
    else
837
1.89k
      page.setFormOrientation(MWAWPageSpan::LANDSCAPE);
838
4.38k
    page.setFormLength(dim[1]);
839
4.38k
    page.setFormWidth(dim[0]);
840
4.38k
    page.setMarginTop(dim[2]);
841
4.38k
    page.setMarginBottom(dim[4]);
842
4.38k
    page.setMarginLeft(dim[3]);
843
4.38k
    page.setMarginRight(dim[5]);
844
4.38k
  }
845
10.0k
  if (dim[0] > 0. || dim[1] > 0.)
846
4.86k
    f << "sz=" << dim[0] << "x" << dim[1] << ",";
847
10.0k
  bool hasMargin = false;
848
29.2k
  for (int i = 2; i < 6; i++) {
849
24.4k
    if (dim[i] > 0.0) {
850
5.26k
      hasMargin = true;
851
5.26k
      break;
852
5.26k
    }
853
24.4k
  }
854
10.0k
  if (hasMargin) {
855
5.26k
    f << "margin=(";
856
26.3k
    for (int i = 2; i < 6; i++) {
857
21.0k
      if (dim[i] < 0) f << "_";
858
20.2k
      else f << dim[i];
859
21.0k
      if (i == 2 || i == 4) f << "x";
860
10.5k
      else f << " ";
861
21.0k
    }
862
5.26k
    f << "),";
863
5.26k
  }
864
10.0k
  f << f2.str();
865
10.0k
  m_document->ascii().addPos(debPage);
866
10.0k
  m_document->ascii().addNote(f.str().c_str());
867
868
10.0k
  return dimOk;
869
10.0k
}
870
871
// the position in the page ? Constant size 0x28c ?
872
// link to the graphic entry RBDR ?
873
// does this means RL -> RB while RBDR means RB->DRawing
874
bool MsWks4Zone::readRLRB(MWAWInputStreamPtr input, MWAWEntry const &entry)
875
387
{
876
387
  if (entry.length() < 13+32) return false;
877
382
  entry.setParsed(true);
878
879
382
  long debPos = entry.begin();
880
382
  input->seek(debPos, librevenge::RVNG_SEEK_SET);
881
382
  libmwaw::DebugStream f;
882
382
  f << "BDB1=("; // some kind of bdbox: BDB1
883
1.91k
  for (int i = 0; i < 4; i++)
884
1.52k
    f << input->readLong(2) << ", ";
885
382
  f << "), ";
886
382
  f << input->readLong(1) << ", ";
887
382
  f << input->readLong(2) << ", ";
888
1.14k
  for (int i = 0; i < 2; i++)
889
764
    f << input->readLong(1) << ", ";
890
891
382
  m_document->ascii().addPos(debPos);
892
382
  m_document->ascii().addNote(f.str().c_str());
893
382
  m_document->ascii().addPos(input->tell());
894
382
  m_document->ascii().addNote("RLRB(2)");
895
896
897
382
  debPos = entry.end()-32;
898
382
  input->seek(debPos, librevenge::RVNG_SEEK_SET);
899
382
  f.str("");
900
  // v0=[29|72..79|189], v1=[-1|2|4|33], (v2xv3) = dim?=(~700x~1000)
901
  // seems related to BDB1
902
382
  f << "RLRB(3):BDB2(";
903
1.91k
  for (int i = 0; i< 4; i++)
904
1.52k
    f << input->readLong(2) << ",";
905
382
  f << ")," << input->readLong(1) << ","; // always 1 ?
906
382
  f << "unk1=(" << std::hex;
907
3.82k
  for (int i = 0; i < 9; i++)
908
3.43k
    f << std::setw(2) << input->readULong(1) << ",";
909
382
  f << ")," << input->readLong(1); // always 1 ?
910
382
  f << ",unk2=(" << std::hex;
911
2.29k
  for (int i = 0; i < 5; i++)
912
1.91k
    f << std::setw(2) << input->readULong(1) << ",";
913
  // header? : [4f|50|5e|75], 0, 0, 0,[80|8b]
914
  // footer? : 48, 0, 0, 0,8b
915
  // qfrm, main : no specific form ?
916
382
  f << "),dims=(" << std::dec;
917
1.91k
  for (int i = 0; i < 4; i++)
918
1.52k
    f << input->readLong(2) << ", ";
919
382
  f << "), ";
920
921
382
  m_document->ascii().addPos(debPos);
922
382
  m_document->ascii().addNote(f.str().c_str());
923
924
382
  return true;
925
387
}
926
927
// the frame position
928
bool MsWks4Zone::readFRAM(MWAWInputStreamPtr input, MWAWEntry const &entry)
929
1.11k
{
930
1.11k
  libmwaw::DebugStream f;
931
932
1.11k
  long debPage = entry.begin();
933
1.11k
  long endPage = entry.end();
934
935
1.11k
  input->seek(debPage, librevenge::RVNG_SEEK_SET);
936
1.11k
  auto numFram = static_cast<int>(input->readULong(2));
937
1.11k
  if (numFram <= 0) return false;
938
988
  entry.setParsed(true);
939
940
988
  f << "N=" << numFram;
941
942
988
  m_document->ascii().addPos(debPage);
943
988
  m_document->ascii().addNote(f.str().c_str());
944
945
2.79k
  for (int i = 0; i < numFram; i++) {
946
2.30k
    long debPos = input->tell();
947
2.30k
    auto size = static_cast<int>(input->readULong(1));
948
2.30k
    if (size <= 0) break;
949
950
2.22k
    f.str("");
951
952
2.22k
    bool ok = true;
953
2.22k
    long endPos = debPos+1+size;
954
2.22k
    if (endPos > endPage) break;
955
956
1.80k
    MsWks4ZoneInternal::Frame frame;
957
1.80k
    MWAWVec2f fOrig, fSiz;
958
6.23k
    while (input->tell() < endPos) {
959
5.98k
      auto val = static_cast<int>(input->readULong(1));
960
5.98k
      long pos = input->tell();
961
962
5.98k
      int sz = 0;
963
5.98k
      bool done = true;
964
5.98k
      switch (val) {
965
476
      case 0x2e: // x
966
937
      case 0x2f: // y
967
937
        if (pos+2 > endPos) {
968
2
          ok = false;
969
2
          break;
970
2
        }
971
935
        if (val == 0x2e) fOrig.setX(float(input->readLong(2))/72.f);
972
459
        else fOrig.setY(float(input->readLong(2))/72.f);
973
935
        break;
974
456
      case 0x30: // width
975
906
      case 0x31: // height
976
906
        if (pos+2 > endPos) {
977
3
          ok = false;
978
3
          break;
979
3
        }
980
903
        if (val == 0x30) fSiz.setX(float(input->readLong(2))/72.f);
981
449
        else fSiz.setY(float(input->readLong(2))/72.f);
982
903
        break;
983
618
      case 0x3c: { // type : checkme ?
984
618
        if (pos+2 > endPos) {
985
7
          ok = false;
986
7
          break;
987
7
        }
988
611
        auto value = static_cast<int>(input->readLong(2));
989
611
        switch (value) {
990
252
        case 1:
991
252
          frame.m_type = MsWks4ZoneInternal::Frame::Textbox;
992
252
          break;
993
0
        case 2:
994
0
          frame.m_type = MsWks4ZoneInternal::Frame::Header;
995
0
          break;
996
1
        case 4:
997
1
          frame.m_type = MsWks4ZoneInternal::Frame::Footer;
998
1
          break;
999
358
        default:
1000
358
          f << "###type=" << value << ",";
1001
611
        }
1002
611
        break;
1003
611
      }
1004
611
      case 0x29: { // always -1 ?
1005
487
        if (pos+2 > endPos) {
1006
4
          ok = false;
1007
4
          break;
1008
4
        }
1009
483
        auto value = static_cast<int>(input->readLong(2));
1010
483
        if (value != -1) f << "###29=" << value << ",";
1011
483
      }
1012
0
      break;
1013
483
      case 0x2a: // in main 2a=6, in frame 2a=4, 2b=4
1014
952
      case 0x2b:
1015
952
        done = false;
1016
952
        sz = 1;
1017
952
        break;
1018
269
      case 0x3d: // only in textBox, related to the field id of Graphics15
1019
269
        if (pos+4 > endPos) {
1020
3
          ok = false;
1021
3
          break;
1022
3
        }
1023
266
        f << "textBoxId=X" << std::hex << input->readLong(4) << "," << std::dec;
1024
266
        break;
1025
250
      case 0x3e: { // appear only in QFrm with value ~ 1/2, 2000
1026
250
        if (pos+4 > endPos) {
1027
2
          ok = false;
1028
2
          break;
1029
2
        }
1030
248
        f << std::hex << val << "=(" << std::dec;
1031
744
        for (int j = 0; j < 2; j++)
1032
496
          f << input->readLong(2) << ",";
1033
248
        f << "),";
1034
248
      }
1035
0
      break;
1036
1037
29
      case 0x37: {
1038
29
        if (pos+6 > endPos) {
1039
2
          ok = false;
1040
2
          break;
1041
2
        }
1042
27
        std::string fName("");
1043
135
        for (int j = 0; j < 4; j++) {
1044
108
          auto c = char(input->readULong(1));
1045
108
          if (c == '\0') continue;
1046
92
          fName +=  c;
1047
92
        }
1048
27
        frame.m_pictId.setName(fName);
1049
27
        frame.m_pictId.setId(static_cast<int>(input->readULong(2)));
1050
27
        done = true;
1051
27
      }
1052
0
      break;
1053
1.53k
      default:
1054
1.53k
        done = false;
1055
1.53k
        break;
1056
5.98k
      }
1057
1058
5.98k
      if (!ok) break;
1059
5.96k
      if (done) continue;
1060
1061
2.49k
      if (sz == 0 || pos + sz > endPos) {
1062
1.53k
        input->seek(-1, librevenge::RVNG_SEEK_CUR);
1063
1.53k
        ok = false;
1064
1.53k
        break;
1065
1.53k
      }
1066
1067
952
      f << std::hex << val << "=" << std::dec;
1068
952
      auto v2 = static_cast<int>(input->readLong(sz)); // sz=1
1069
952
      f << v2 << ", ";
1070
952
    }
1071
1072
1.80k
    frame.m_position=MWAWPosition(fOrig, fSiz);
1073
1.80k
    frame.m_position.setPage(-3);
1074
1.80k
    frame.m_error = f.str();
1075
1.80k
    f.str("");
1076
1.80k
    m_state->m_framesList.push_back(frame);
1077
1078
1.80k
    f << "FRAM(" << size << "):" << frame;
1079
1.80k
    m_document->ascii().addPos(debPos);
1080
1.80k
    m_document->ascii().addNote(f.str().c_str());
1081
1.80k
    if (!ok) {
1082
1.56k
      m_document->ascii().addPos(input->tell());
1083
1.56k
      m_document->ascii().addNote("FRAM###");
1084
1.56k
    }
1085
1086
1.80k
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
1087
1.80k
  }
1088
1089
988
  return true;
1090
988
}
1091
1092
// the selection
1093
bool MsWks4Zone::readSELN(MWAWInputStreamPtr input, MWAWEntry const &entry)
1094
528
{
1095
528
  libmwaw::DebugStream f;
1096
1097
528
  long debPage = entry.begin();
1098
528
  long endPage = entry.end();
1099
1100
528
  input->seek(debPage, librevenge::RVNG_SEEK_SET);
1101
528
  if (endPage-debPage <= 12) {
1102
7
    MWAW_DEBUG_MSG(("MsWks4Zone::readSELN: SELN size=%ld too short\n", endPage-debPage));
1103
7
    return false;
1104
7
  }
1105
521
  entry.setParsed(true);
1106
1107
521
  auto type = static_cast<int>(input->readLong(1));
1108
  // 2,3
1109
521
  switch (type) {
1110
256
  case 2:
1111
256
    f << "textPoint, ";
1112
256
    break;
1113
0
  case 3:
1114
0
    f << "textZone, ";
1115
0
    break;
1116
265
  default:
1117
265
    f << "type=###" << type << ",";
1118
265
    break;
1119
521
  }
1120
  // 0 0xFF 0
1121
2.08k
  for (int i = 0; i < 3; i++) {
1122
1.56k
    auto val = static_cast<int>(input->readLong(1));
1123
1.56k
    if ((i%2)+val)
1124
566
      f << "unk" << i << "=" << val << ",";
1125
1.56k
  }
1126
  // textBegin, textEnd
1127
521
  f << "textPos?=(";
1128
1.56k
  for (int i = 0; i < 2; i++) {
1129
1.04k
    f << input->readLong(4);
1130
1.04k
    if (i == 0) f << "<->";
1131
1.04k
  }
1132
521
  f << ")";
1133
1134
  // I find f2=-1,f20=-1,f21=-1 and f22=0|1
1135
  // maybe f22=0 -> mainTextZone, f22=1 -> header, ...
1136
13.5k
  for (long i = 0; i < (endPage-debPage-12)/2; i++) {
1137
12.9k
    auto val = static_cast<int>(input->readLong(2));
1138
12.9k
    if (val) f << ",f" << i << "=" << val;
1139
12.9k
  }
1140
1141
521
  m_document->ascii().addPos(debPage);
1142
521
  m_document->ascii().addNote(f.str().c_str());
1143
1144
521
  if (long(input->tell()) != endPage) {
1145
19
    m_document->ascii().addPos(input->tell());
1146
19
    m_document->ascii().addNote("SELN###");
1147
19
  }
1148
521
  return true;
1149
521
}
1150
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: