Coverage Report

Created: 2026-09-06 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/MsWrd1Parser.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
#include "MWAWTextListener.hxx"
42
#include "MWAWFont.hxx"
43
#include "MWAWFontConverter.hxx"
44
#include "MWAWHeader.hxx"
45
#include "MWAWParagraph.hxx"
46
#include "MWAWPictMac.hxx"
47
#include "MWAWPosition.hxx"
48
#include "MWAWPrinter.hxx"
49
#include "MWAWSection.hxx"
50
#include "MWAWSubDocument.hxx"
51
52
#include "MsWrd1Parser.hxx"
53
54
/** Internal: the structures of a MsWrd1Parser */
55
namespace MsWrd1ParserInternal
56
{
57
/** different types
58
 *
59
 * - FONT: font
60
 * - RULER: ruler
61
 * - PAGE: page break
62
 * - FOOTNOTE: footnote marker
63
 * - ZONE: unknown(zone4)
64
 */
65
enum PLCType { FONT=0, RULER, FOOTNOTE, PAGE, ZONE, UNKNOWN};
66
67
/** Internal: class to store the PLC: Pointer List Content ? */
68
struct PLC {
69
  //! constructor
70
  explicit PLC(PLCType type=UNKNOWN)
71
16.4k
    : m_type(type)
72
16.4k
    , m_id(-1)
73
16.4k
    , m_extras("")
74
16.4k
  {
75
16.4k
  }
76
  //! operator<<
77
  friend std::ostream &operator<<(std::ostream &o, PLC const &plc);
78
  //! the type
79
  PLCType m_type;
80
  //! the id
81
  int m_id;
82
  //! a string used to store the parsing extrass
83
  std::string m_extras;
84
};
85
86
std::ostream &operator<<(std::ostream &o, PLC const &plc)
87
0
{
88
0
  switch (plc.m_type) {
89
0
  case FONT:
90
0
    o << "F";
91
0
    break;
92
0
  case RULER:
93
0
    o << "P";
94
0
    break;
95
0
  case FOOTNOTE:
96
0
    o << "Fn";
97
0
    break;
98
0
  case PAGE:
99
0
    o << "Page";
100
0
    break;
101
0
  case ZONE:
102
0
    o << "Z";
103
0
    break;
104
0
  case UNKNOWN:
105
#if !defined(__clang__)
106
  default:
107
#endif
108
0
    o << "#type" << int(plc.m_type);
109
0
    break;
110
0
  }
111
0
  if (plc.m_id != -1) o << plc.m_id;
112
0
  else o << "_";
113
0
  if (!plc.m_extras.empty()) o << ":" << plc.m_extras;
114
0
  return o;
115
0
}
116
117
////////////////////////////////////////
118
//! Internal: the font of a MsWrd1Parser
119
struct Font {
120
  //! constructor
121
  Font()
122
332k
    : m_font()
123
332k
    , m_type(0)
124
332k
    , m_extras("")
125
332k
  {
126
332k
  }
127
  //! operator<<
128
  friend std::ostream &operator<<(std::ostream &o, Font const &ft);
129
  //! the basic font property
130
  MWAWFont m_font;
131
  //! a unknown int, maybe 0x80 means defined font
132
  int m_type;
133
  //! a string used to store the parsing extrass
134
  std::string m_extras;
135
};
136
137
std::ostream &operator<<(std::ostream &o, Font const &ft)
138
0
{
139
0
  if (ft.m_type) o << "type=" << std::hex << ft.m_type << std::dec << ",";
140
0
  if (!ft.m_extras.empty()) o << ft.m_extras;
141
0
  return o;
142
0
}
143
144
////////////////////////////////////////
145
//! Internal: the paragraph of a MsWrd1Parser
146
struct Paragraph final : public MWAWParagraph {
147
  //! constructor
148
  Paragraph()
149
215k
    : MWAWParagraph()
150
215k
    , m_type(0)
151
215k
    , m_type2(0)
152
215k
  {
153
215k
  }
154
123k
  Paragraph(Paragraph const &)=default;
155
  Paragraph &operator=(Paragraph const &)=default;
156
62.3k
  Paragraph &operator=(Paragraph &&)=default;
157
  //! destructor
158
  ~Paragraph() final;
159
  //! operator<<
160
  friend std::ostream &operator<<(std::ostream &o, Paragraph const &ft);
161
  //! the initial type
162
  int m_type;
163
  //! another type
164
  int m_type2;
165
};
166
167
Paragraph::~Paragraph()
168
339k
{
169
339k
}
170
171
std::ostream &operator<<(std::ostream &o, Paragraph const &para)
172
0
{
173
0
  o << static_cast<MWAWParagraph const &>(para);
174
  // 0|80 frequent: means redefine paragraph? find also a7 in a footnote
175
0
  if (para.m_type) o << "type=" << std::hex << para.m_type << std::dec << ",";
176
177
0
  if (para.m_type2 & 0xF0) {
178
0
    bool foot = (para.m_type2 & 0x10);
179
0
    if (foot) o << "footer/footnote[";
180
0
    else o << "header[";
181
0
    if (para.m_type2 & 0x20) o << (foot ? "even," : "odd,");
182
0
    if (para.m_type2 & 0x40) o << (foot ? "odd," : "even,");
183
0
    if (para.m_type2 & 0x80) o << "first,";
184
0
    o << "]";
185
0
  }
186
0
  if (para.m_type2 & 0xF)
187
0
    o << "#type2=" << std::hex << (para.m_type2 & 0xF) << std::dec << ",";
188
189
0
  return o;
190
0
}
191
192
////////////////////////////////////////
193
//! Internal: the state of a MsWrd1Parser
194
struct State {
195
  //! constructor
196
  State()
197
83.3k
    : m_eot(-1)
198
83.3k
    , m_numColumns(1)
199
83.3k
    , m_columnsSep(0)
200
83.3k
    , m_textZonesList()
201
83.3k
    , m_mainTextZonesList()
202
83.3k
    , m_fontsList()
203
83.3k
    , m_paragraphsList()
204
83.3k
    , m_endNote(false)
205
83.3k
    , m_footnotesList()
206
83.3k
    , m_plcMap()
207
83.3k
    , m_actPage(0)
208
83.3k
    , m_numPages(1)
209
83.3k
    , m_headersId()
210
83.3k
    , m_footersId()
211
83.3k
  {
212
583k
    for (auto &limit : m_fileZonesLimit) limit = -1;
213
83.3k
  }
214
215
  //! end of text
216
  long m_eot;
217
  //! the number of columns
218
  int m_numColumns;
219
  //! the column separator
220
  float m_columnsSep;
221
  //! the zones limits
222
  int m_fileZonesLimit[7];
223
  //! the list of text zones
224
  std::vector<MWAWVec2l> m_textZonesList;
225
  //! the list of main text zones
226
  std::vector<int> m_mainTextZonesList;
227
  //! the list of fonts
228
  std::vector<Font> m_fontsList;
229
  //! the list of paragraph
230
  std::vector<Paragraph> m_paragraphsList;
231
  //! a flag to know if we send endnote or footnote
232
  bool m_endNote;
233
  //! the footnote positions ( list of beginPos, endPos)
234
  std::vector<MWAWVec2l> m_footnotesList;
235
  //! the text correspondance zone ( filepos, plc )
236
  std::multimap<long, PLC> m_plcMap;
237
238
  int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
239
  /** the list of header id which corresponds to each page */
240
  std::vector<int> m_headersId;
241
  /** the list of footer id which corresponds to each page */
242
  std::vector<int> m_footersId;
243
};
244
245
////////////////////////////////////////
246
//! Internal: the subdocument of a MsWrdParser
247
class SubDocument final : public MWAWSubDocument
248
{
249
public:
250
  //! constructor for footnote, header
251
  SubDocument(MsWrd1Parser &pars, MWAWInputStreamPtr const &input, MWAWEntry const &position)
252
92.0k
    : MWAWSubDocument(&pars, input, position)
253
92.0k
  {
254
92.0k
  }
255
256
  //! destructor
257
0
  ~SubDocument() final {}
258
259
  //! operator!=
260
  bool operator!=(MWAWSubDocument const &doc) const final
261
0
  {
262
0
    return MWAWSubDocument::operator!=(doc);
263
0
  }
264
265
  //! the parser function
266
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
267
268
protected:
269
};
270
271
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType)
272
91.3k
{
273
91.3k
  if (!listener.get()) {
274
0
    MWAW_DEBUG_MSG(("MsWrd1ParserInternal::SubDocument::parse: no listener\n"));
275
0
    return;
276
0
  }
277
91.3k
  auto *parser=dynamic_cast<MsWrd1Parser *>(m_parser);
278
91.3k
  if (!parser) {
279
0
    MWAW_DEBUG_MSG(("MsWrd1ParserInternal::SubDocument::parse: no parser\n"));
280
0
    return;
281
0
  }
282
283
91.3k
  if (!m_zone.valid()) {
284
14.1k
    listener->insertChar(' ');
285
14.1k
    return;
286
14.1k
  }
287
77.1k
  long pos = m_input->tell();
288
77.1k
  parser->sendText(m_zone);
289
77.1k
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
290
77.1k
}
291
}
292
293
294
////////////////////////////////////////////////////////////
295
// constructor/destructor, ...
296
////////////////////////////////////////////////////////////
297
MsWrd1Parser::MsWrd1Parser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
298
36.2k
  : MWAWTextParser(input, rsrcParser, header)
299
36.2k
  , m_state()
300
36.2k
{
301
36.2k
  init();
302
36.2k
}
303
304
MsWrd1Parser::~MsWrd1Parser()
305
36.2k
{
306
36.2k
}
307
308
void MsWrd1Parser::init()
309
36.2k
{
310
36.2k
  resetTextListener();
311
36.2k
  setAsciiName("main-1");
312
313
36.2k
  m_state.reset(new MsWrd1ParserInternal::State);
314
315
  // reduce the margin (in case, the page is not defined)
316
36.2k
  getPageSpan().setMargins(0.1);
317
36.2k
}
318
319
////////////////////////////////////////////////////////////
320
// new page
321
////////////////////////////////////////////////////////////
322
void MsWrd1Parser::newPage(int number)
323
172k
{
324
172k
  if (number <= m_state->m_actPage || number > m_state->m_numPages)
325
145k
    return;
326
327
55.5k
  while (m_state->m_actPage < number) {
328
28.4k
    m_state->m_actPage++;
329
28.4k
    if (!getTextListener() || m_state->m_actPage == 1)
330
1.63k
      continue;
331
26.7k
    getTextListener()->insertBreak(MWAWTextListener::PageBreak);
332
26.7k
  }
333
27.1k
}
334
335
void MsWrd1Parser::removeLastCharIfEOL(MWAWEntry &entry)
336
92.9k
{
337
92.9k
  if (!entry.valid()) return;
338
78.9k
  MWAWInputStreamPtr input = getInput();
339
78.9k
  long actPos = input->tell();
340
78.9k
  input->seek(entry.end()-1, librevenge::RVNG_SEEK_SET);
341
78.9k
  if (input->readLong(1)==0xd)
342
8.74k
    entry.setLength(entry.length()-1);
343
78.9k
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
344
78.9k
}
345
////////////////////////////////////////////////////////////
346
// the parser
347
////////////////////////////////////////////////////////////
348
void MsWrd1Parser::parse(librevenge::RVNGTextInterface *docInterface)
349
10.8k
{
350
10.8k
  if (!getInput().get() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
351
10.8k
  bool ok = true;
352
10.8k
  try {
353
    // create the asciiFile
354
10.8k
    ascii().setStream(getInput());
355
10.8k
    ascii().open(asciiName());
356
10.8k
    checkHeader(nullptr);
357
10.8k
    ok = createZones();
358
10.8k
    if (ok) {
359
10.8k
      createDocument(docInterface);
360
10.8k
      sendMain();
361
10.8k
    }
362
363
10.8k
    ascii().reset();
364
10.8k
  }
365
10.8k
  catch (...) {
366
0
    MWAW_DEBUG_MSG(("MsWrd1Parser::parse: exception catched when parsing\n"));
367
0
    ok = false;
368
0
  }
369
370
10.8k
  resetTextListener();
371
10.8k
  if (!ok) throw(libmwaw::ParseException());
372
10.8k
}
373
374
////////////////////////////////////////////////////////////
375
// send the main zone
376
////////////////////////////////////////////////////////////
377
void MsWrd1Parser::sendMain()
378
10.8k
{
379
14.2k
  for (auto id : m_state->m_mainTextZonesList) {
380
14.2k
    if (id < 0 || id >= int(m_state->m_textZonesList.size()))
381
0
      continue;
382
14.2k
    MWAWEntry entry;
383
14.2k
    entry.setBegin(m_state->m_textZonesList[size_t(id)][0]);
384
14.2k
    entry.setEnd(m_state->m_textZonesList[size_t(id)][1]);
385
14.2k
    sendText(entry, true);
386
14.2k
  }
387
  // maybe need if we have no text ; if not, nobody will see it
388
10.8k
  if (getTextListener())
389
10.8k
    getTextListener()->insertChar(' ');
390
10.8k
}
391
392
////////////////////////////////////////////////////////////
393
// create the document
394
////////////////////////////////////////////////////////////
395
void MsWrd1Parser::createDocument(librevenge::RVNGTextInterface *documentInterface)
396
10.8k
{
397
10.8k
  if (!documentInterface) return;
398
10.8k
  if (getTextListener()) {
399
0
    MWAW_DEBUG_MSG(("MsWrd1Parser::createDocument: listener already exist\n"));
400
0
    return;
401
0
  }
402
403
  // update the page
404
10.8k
  m_state->m_actPage = 0;
405
  // create the page list
406
10.8k
  std::vector<MWAWPageSpan> pageList;
407
10.8k
  auto numHeaders=int(m_state->m_headersId.size());
408
10.8k
  auto numFooters=int(m_state->m_footersId.size());
409
60.0k
  for (int i = 0; i <= m_state->m_numPages;) {
410
49.2k
    int numSim[2]= {1,1};
411
49.2k
    MWAWPageSpan ps(getPageSpan());
412
49.2k
    while (i < numHeaders) {
413
38.3k
      int id = m_state->m_headersId[size_t(i)];
414
38.3k
      if (id < 0 || id >= int(m_state->m_textZonesList.size()))
415
28.7k
        break;
416
9.62k
      MWAWEntry entry;
417
9.62k
      entry.setBegin(m_state->m_textZonesList[size_t(id)][0]);
418
9.62k
      entry.setEnd(m_state->m_textZonesList[size_t(id)][1]);
419
9.62k
      removeLastCharIfEOL(entry);
420
9.62k
      if (!entry.valid()) break;
421
9.20k
      MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
422
9.20k
      header.m_subDocument.reset
423
9.20k
      (new MsWrd1ParserInternal::SubDocument(*this, getInput(), entry));
424
9.20k
      ps.setHeaderFooter(header);
425
9.20k
      int j = i+1;
426
87.8k
      while (j < numHeaders && m_state->m_headersId[size_t(j)]==id) {
427
78.6k
        numSim[0]++;
428
78.6k
        j++;
429
78.6k
      }
430
9.20k
      break;
431
9.62k
    }
432
49.2k
    while (i < int(numFooters)) {
433
38.3k
      int id = m_state->m_footersId[size_t(i)];
434
38.3k
      if (id < 0 || id >= int(m_state->m_textZonesList.size()))
435
27.6k
        break;
436
10.7k
      MWAWEntry entry;
437
10.7k
      entry.setBegin(m_state->m_textZonesList[size_t(id)][0]);
438
10.7k
      entry.setEnd(m_state->m_textZonesList[size_t(id)][1]);
439
10.7k
      removeLastCharIfEOL(entry);
440
10.7k
      if (!entry.valid()) break;
441
10.3k
      MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
442
10.3k
      footer.m_subDocument.reset
443
10.3k
      (new MsWrd1ParserInternal::SubDocument(*this, getInput(), entry));
444
10.3k
      ps.setHeaderFooter(footer);
445
10.3k
      int j = i+1;
446
163k
      while (j < numFooters && m_state->m_footersId[size_t(j)]==id) {
447
153k
        numSim[1]++;
448
153k
        j++;
449
153k
      }
450
10.3k
      break;
451
10.7k
    }
452
49.2k
    if (numSim[1] < numSim[0]) numSim[0]=numSim[1];
453
49.2k
    if (numSim[0] < 1) numSim[0]=1;
454
49.2k
    ps.setPageSpan(numSim[0]);
455
49.2k
    i+=numSim[0];
456
49.2k
    pageList.push_back(ps);
457
49.2k
  }
458
459
  //
460
10.8k
  MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
461
10.8k
  setTextListener(listen);
462
10.8k
  listen->startDocument();
463
10.8k
}
464
465
466
////////////////////////////////////////////////////////////
467
// Intermediate level
468
////////////////////////////////////////////////////////////
469
470
// create the different zones
471
bool MsWrd1Parser::createZones()
472
10.8k
{
473
10.8k
  libmwaw::DebugStream f;
474
10.8k
  if (m_state->m_eot < 0x80) return false;
475
476
10.8k
  ascii().addPos(0x80);
477
10.8k
  ascii().addNote("TextContent");
478
479
10.8k
  ascii().addPos(m_state->m_eot);
480
10.8k
  ascii().addNote("_");
481
482
10.8k
  MWAWInputStreamPtr input = getInput();
483
67.4k
  for (int z = 5; z >= 0; z--) {
484
59.0k
    if (m_state->m_fileZonesLimit[z] == m_state->m_fileZonesLimit[z+1])
485
37.0k
      continue;
486
21.9k
    if (!input->checkPosition(m_state->m_fileZonesLimit[z+1]*0x80) ||
487
19.5k
        m_state->m_fileZonesLimit[z] > m_state->m_fileZonesLimit[z+1]) {
488
2.44k
      f.str("");
489
2.44k
      f << "Entries(Zone" << z << "):###";
490
2.44k
      MWAW_DEBUG_MSG(("MsWrd1Parser::createZones: zone %d is too long\n",z));
491
2.44k
      ascii().addPos(m_state->m_fileZonesLimit[z]*0x80);
492
2.44k
      ascii().addNote(f.str().c_str());
493
2.44k
      break;
494
2.44k
    }
495
19.5k
    MWAWVec2i limit(m_state->m_fileZonesLimit[z],m_state->m_fileZonesLimit[z+1]);
496
19.5k
    bool done = false;
497
19.5k
    switch (z) {
498
4.35k
    case 0:
499
8.83k
    case 1:
500
8.83k
      done = readPLC(limit,z);
501
8.83k
      break;
502
3.14k
    case 2:
503
3.14k
      done = readFootnoteCorrespondance(limit);
504
3.14k
      break;
505
2.13k
    case 3:
506
2.13k
      done = readDocInfo(limit);
507
2.13k
      break;
508
2.69k
    case 4:
509
2.69k
      done = readZones(limit);
510
2.69k
      break;
511
2.72k
    case 5:
512
2.72k
      done = readPageBreak(limit);
513
2.72k
      break;
514
0
    default:
515
0
      break;
516
19.5k
    }
517
19.5k
    if (done) continue;
518
22.6k
    for (int p = m_state->m_fileZonesLimit[z], i=0; p < m_state->m_fileZonesLimit[z+1]; p++, i++) {
519
17.6k
      f.str("");
520
17.6k
      f << "Entries(Zone" << z << ")[" << i << "]:";
521
17.6k
      ascii().addPos(p*0x80);
522
17.6k
      ascii().addNote(f.str().c_str());
523
17.6k
    }
524
5.01k
    ascii().addPos(m_state->m_fileZonesLimit[z+1]*0x80);
525
5.01k
    ascii().addNote("_");
526
5.01k
  }
527
10.8k
  prepareTextZones();
528
10.8k
  return true;
529
10.8k
}
530
531
// try to read retrieve the header/footer zones ...
532
bool MsWrd1Parser::prepareTextZones()
533
10.8k
{
534
10.8k
  m_state->m_numPages = 1;
535
10.8k
  m_state->m_textZonesList.resize(0);
536
10.8k
  m_state->m_mainTextZonesList.resize(0);
537
10.8k
  m_state->m_headersId.resize(0);
538
10.8k
  m_state->m_footersId.resize(0);
539
10.8k
  long endMain = m_state->m_eot;
540
10.8k
  for (auto const &footnote : m_state->m_footnotesList) {
541
9.71k
    long pos = footnote[0];
542
9.71k
    if (pos >= 0x80 && pos < endMain)
543
1.93k
      endMain = pos;
544
9.71k
  }
545
10.8k
  if (endMain < 0x80) {
546
0
    MWAW_DEBUG_MSG(("MsWrd1Parser::sendText: oops problem computing the limit of the main section"));
547
0
    m_state->m_textZonesList.push_back(MWAWVec2l(0x80, m_state->m_eot));
548
0
    m_state->m_mainTextZonesList.push_back(0);
549
0
    return false;
550
0
  }
551
552
10.8k
  auto plcIt = m_state->m_plcMap.begin();
553
10.8k
  long pos = 0x80, prevMainPos=pos;
554
10.8k
  int actPage = 1;
555
10.8k
  int actType = 0;
556
10.8k
  MWAWVec2i headerId(-1, -1), footerId(-1, -1);
557
10.8k
  int firstHeaderId=-1, firstFooterId=-1;
558
749k
  while (pos < endMain) {
559
738k
    int newType = 0;
560
738k
    if (plcIt == m_state->m_plcMap.end() || plcIt->first>=endMain) {
561
9.28k
      pos = endMain;
562
9.28k
      newType = -1;
563
9.28k
    }
564
729k
    else {
565
729k
      pos = plcIt->first;
566
729k
      MsWrd1ParserInternal::PLC const &plc = plcIt++->second;
567
729k
      if (plc.m_type==MsWrd1ParserInternal::PAGE && pos!=0x80) {
568
27.7k
        if (actPage> int(m_state->m_headersId.size())) {
569
26.7k
          m_state->m_headersId.resize(size_t(actPage), -1);
570
26.7k
          m_state->m_headersId[size_t(actPage)-1] = headerId[(actPage%2)];
571
26.7k
        }
572
27.7k
        if (actPage> int(m_state->m_footersId.size())) {
573
26.9k
          m_state->m_footersId.resize(size_t(actPage), -1);
574
26.9k
          m_state->m_footersId[size_t(actPage)-1] = footerId[(actPage%2)];
575
26.9k
        }
576
27.7k
        actPage++;
577
27.7k
      }
578
729k
      if (plc.m_type!=MsWrd1ParserInternal::RULER) continue;
579
165k
      if (plc.m_id >= 0 && plc.m_id < int(m_state->m_paragraphsList.size()))
580
33.8k
        newType = (m_state->m_paragraphsList[size_t(plc.m_id)].m_type2>>4);
581
165k
      if (newType == actType)
582
148k
        continue;
583
165k
    }
584
25.7k
    if (pos==prevMainPos) {
585
3.69k
      actType = newType;
586
3.69k
      continue;
587
3.69k
    }
588
589
22.0k
    auto id = int(m_state->m_textZonesList.size());
590
22.0k
    m_state->m_textZonesList.push_back(MWAWVec2l(prevMainPos, pos));
591
22.0k
    prevMainPos=pos;
592
22.0k
    if (actType==0) {
593
14.2k
      m_state->m_mainTextZonesList.push_back(id);
594
14.2k
      actType = newType;
595
14.2k
      continue;
596
14.2k
    }
597
7.83k
    if (actType&1) {
598
3.06k
      if (actType&2) footerId[1]=id;
599
3.06k
      if (actType&4) footerId[0]=id;
600
3.06k
      if (actType&8) firstFooterId=id;
601
3.06k
      m_state->m_footersId.resize(size_t(actPage), -1);
602
3.06k
      m_state->m_footersId[size_t(actPage)-1] =
603
3.06k
        (actPage==1 && firstFooterId >= 0) ? firstFooterId :
604
3.06k
        (actPage%2) ? footerId[1] : footerId[0];
605
3.06k
    }
606
4.76k
    else {
607
4.76k
      if (actType&2) headerId[0]=id;
608
4.76k
      if (actType&4) headerId[1]=id;
609
4.76k
      if (actType&8) firstHeaderId=id;
610
4.76k
      m_state->m_headersId.resize(size_t(actPage), -1);
611
4.76k
      m_state->m_headersId[size_t(actPage)-1] =
612
4.76k
        (actPage==1 && firstHeaderId >= 0) ? firstHeaderId :
613
4.76k
        (actPage%2) ? headerId[1] : headerId[0];
614
4.76k
    }
615
7.83k
    actType = newType;
616
7.83k
  }
617
10.8k
  if (actPage> int(m_state->m_headersId.size())) {
618
9.80k
    m_state->m_headersId.resize(size_t(actPage), -1);
619
9.80k
    m_state->m_headersId[size_t(actPage)-1] = headerId[(actPage%2)];
620
9.80k
  }
621
10.8k
  if (actPage> int(m_state->m_footersId.size())) {
622
9.83k
    m_state->m_footersId.resize(size_t(actPage), -1);
623
9.83k
    m_state->m_footersId[size_t(actPage)-1] = footerId[(actPage%2)];
624
9.83k
  }
625
10.8k
  m_state->m_numPages = actPage;
626
10.8k
  return true;
627
10.8k
}
628
629
////////////////////////////////////////////////////////////
630
// try to read the different zones
631
////////////////////////////////////////////////////////////
632
633
// read the character property
634
bool MsWrd1Parser::readFont(long fPos, MsWrd1ParserInternal::Font &font)
635
75.5k
{
636
75.5k
  font = MsWrd1ParserInternal::Font();
637
75.5k
  libmwaw::DebugStream f;
638
75.5k
  MWAWInputStreamPtr input = getInput();
639
75.5k
  input->seek(fPos, librevenge::RVNG_SEEK_SET);
640
75.5k
  auto sz = static_cast<int>(input->readLong(1));
641
75.5k
  if (sz < 1 || sz > 0x7f || !input->checkPosition(fPos+1+sz)) {
642
19.6k
    MWAW_DEBUG_MSG(("MsWrd1Parser::readFont: the zone size seems bad\n"));
643
19.6k
    return false;
644
19.6k
  }
645
55.8k
  font.m_type = static_cast<int>(input->readULong(1));
646
55.8k
  int val;
647
55.8k
  uint32_t flags=0;
648
55.8k
  if (sz >= 2) {
649
49.2k
    val = static_cast<int>(input->readULong(1));
650
49.2k
    if (val & 0x80) flags |= MWAWFont::boldBit;
651
49.2k
    if (val & 0x40) flags |= MWAWFont::italicBit;
652
49.2k
    if (val & 0x3f)
653
46.1k
      font.m_font.setId((val & 0x3f));
654
49.2k
  }
655
55.8k
  if (sz >= 3) {
656
41.5k
    val = static_cast<int>(input->readULong(1));
657
41.5k
    if (val) font.m_font.setSize(float(val)/2.f);
658
41.5k
  }
659
55.8k
  if (sz >= 4) {
660
39.1k
    val = static_cast<int>(input->readULong(1));
661
39.1k
    if (val & 0x80) font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
662
39.1k
    switch ((val&0xc)>>2) {
663
26.0k
    case 0:
664
26.0k
      break;
665
3.26k
    case 3:
666
3.26k
      flags |= MWAWFont::uppercaseBit;
667
3.26k
      break;
668
9.87k
    default:
669
9.87k
      f << "#capBits=" << int((val&0xc)>>2) << ",";
670
39.1k
    }
671
    // find also &2 for footnote
672
39.1k
    if (val & 0x73)
673
24.7k
      f << "#flags1=" << std::hex << (val & 0x73) << std::dec << ",";
674
39.1k
  }
675
55.8k
  if (sz >= 5) {
676
35.5k
    val = static_cast<int>(input->readULong(1));
677
35.5k
    if (val & 0x10) flags |= MWAWFont::embossBit;
678
35.5k
    if (val & 0x8) flags |= MWAWFont::shadowBit;
679
35.5k
    if (val & 0xe7)
680
11.9k
      f << "#flags2=" << std::hex << (val & 0xe7) << std::dec << ",";
681
35.5k
  }
682
55.8k
  if (sz >= 6) { // vdepl
683
32.1k
    val = static_cast<int>(input->readLong(1));
684
32.1k
    if (val > 0) font.m_font.set(MWAWFont::Script::super100());
685
5.96k
    else if (val < 0) font.m_font.set(MWAWFont::Script::sub100());
686
32.1k
  }
687
55.8k
  if (sz >= 7) {
688
23.9k
    f << "###";
689
23.9k
    ascii().addDelimiter(input->tell(),'|');
690
23.9k
  }
691
55.8k
  font.m_font.setFlags(flags);
692
55.8k
  font.m_extras = f.str();
693
694
55.8k
  return true;
695
55.8k
}
696
697
/* read the paragraph property */
698
bool MsWrd1Parser::readParagraph(long fPos, MsWrd1ParserInternal::Paragraph &para)
699
62.3k
{
700
62.3k
  para = MsWrd1ParserInternal::Paragraph();
701
62.3k
  libmwaw::DebugStream f;
702
62.3k
  MWAWInputStreamPtr input = getInput();
703
62.3k
  input->seek(fPos, librevenge::RVNG_SEEK_SET);
704
62.3k
  auto sz = static_cast<int>(input->readLong(1));
705
62.3k
  if (sz < 1 || sz > 0x7f || !input->checkPosition(fPos+1+sz)) {
706
9.93k
    MWAW_DEBUG_MSG(("MsWrd1Parser::readParagraph: the zone size seems bad\n"));
707
9.93k
    return false;
708
9.93k
  }
709
52.4k
  para.m_type = static_cast<int>(input->readULong(1));
710
52.4k
  int val;
711
52.4k
  if (sz >= 2) {
712
50.6k
    val = static_cast<int>(input->readULong(1));
713
50.6k
    switch (val>>6) {
714
32.8k
    case 0:
715
32.8k
      break; // left
716
8.08k
    case 1:
717
8.08k
      para.m_justify = MWAWParagraph::JustificationCenter;
718
8.08k
      break;
719
1.99k
    case 2:
720
1.99k
      para.m_justify = MWAWParagraph::JustificationRight;
721
1.99k
      break;
722
7.66k
    case 3:
723
7.66k
      para.m_justify = MWAWParagraph::JustificationFull;
724
7.66k
      break;
725
0
    default:
726
0
      break;
727
50.6k
    }
728
50.6k
    if (val & 0x10) f << "dontbreak[para],";
729
50.6k
    if (val & 0x20) f << "dontbreak[line],";
730
50.6k
    if (val & 0xf)
731
20.4k
      f << "#justify=" << std::hex << (val & 0xf) << std::dec << ",";
732
50.6k
  }
733
52.4k
  if (sz >= 4) { // find always 0 here
734
41.7k
    val = static_cast<int>(input->readLong(2));
735
41.7k
    if (val) f << "#f0=" << val << ",";
736
41.7k
  }
737
52.4k
  if (sz >= 6) {
738
39.8k
    val = static_cast<int>(input->readLong(2));
739
39.8k
    if (val)
740
34.8k
      para.m_margins[2] = double(val)/1440.0;
741
39.8k
  }
742
52.4k
  if (sz >= 8) {
743
37.6k
    val = static_cast<int>(input->readLong(2));
744
37.6k
    if (val)
745
32.5k
      para.m_margins[0] = double(val)/1440.0;
746
37.6k
  }
747
52.4k
  if (sz >= 10) {
748
34.2k
    val = static_cast<int>(input->readLong(2));
749
34.2k
    if (val && !para.m_margins[0].isSet())
750
599
      para.m_margins[1] = double(val)/1440.0;
751
33.6k
    else if (val)
752
24.1k
      para.m_margins[1] = para.m_margins[0].get()+double(val)/1440.0;
753
34.2k
  }
754
52.4k
  if (sz >= 12) {
755
34.1k
    val = static_cast<int>(input->readLong(2));
756
34.1k
    if (val)
757
25.3k
      para.setInterline(double(val)/1440.0, librevenge::RVNG_INCH);
758
34.1k
  }
759
52.4k
  if (sz >= 14) {
760
31.7k
    val = static_cast<int>(input->readLong(2));
761
31.7k
    if (val)
762
24.2k
      para.m_spacings[1] = double(val)/1440.0;
763
31.7k
  }
764
52.4k
  if (sz >= 16) {
765
30.7k
    val = static_cast<int>(input->readLong(2));
766
30.7k
    if (val)
767
23.9k
      para.m_spacings[2] = double(val)/1440.0;
768
30.7k
  }
769
52.4k
  if (sz >= 17)
770
16.6k
    para.m_type2 = static_cast<int>(input->readULong(1));
771
  // checkme: not sure what is the exact decomposition of the following
772
52.4k
  if (sz >= 22) { // find always 0 here
773
65.4k
    for (int i = 0; i < 5; i++) {
774
54.5k
      val = static_cast<int>(input->readLong(1));
775
54.5k
      if (val) f << "#f" << i+1 << "=" << val << ",";
776
54.5k
    }
777
10.9k
  }
778
52.4k
  if (sz >= 26) {
779
10.5k
    int numTabs = (sz-26)/4;
780
153k
    for (int i = 0; i < numTabs; i++) {
781
142k
      MWAWTabStop newTab;
782
142k
      newTab.m_position = double(input->readLong(2))/1440.;
783
142k
      auto flags = static_cast<int>(input->readULong(1));
784
142k
      switch ((flags>>5)&3) {
785
83.8k
      case 0:
786
83.8k
        break;
787
15.6k
      case 1:
788
15.6k
        newTab.m_alignment = MWAWTabStop::CENTER;
789
15.6k
        break;
790
8.17k
      case 2:
791
8.17k
        newTab.m_alignment = MWAWTabStop::RIGHT;
792
8.17k
        break;
793
35.0k
      case 3:
794
35.0k
        newTab.m_alignment = MWAWTabStop::DECIMAL;
795
35.0k
        break;
796
0
      default:
797
0
        break;
798
142k
      }
799
142k
      switch ((flags>>2)&3) {
800
71.8k
      case 0:
801
71.8k
        break;
802
20.6k
      case 1:
803
20.6k
        newTab.m_leaderCharacter = '.';
804
20.6k
        break;
805
16.6k
      case 2:
806
16.6k
        newTab.m_leaderCharacter = '-';
807
16.6k
        break;
808
33.6k
      case 3:
809
33.6k
        newTab.m_leaderCharacter = '_';
810
33.6k
        break;
811
0
      default:
812
0
        break;
813
142k
      }
814
142k
      if (flags & 0x93)
815
93.5k
        f << "#tabs" << i << "[fl1=" << std::hex << (flags & 0x93) << std::dec << ",";
816
142k
      val = static_cast<int>(input->readULong(1));
817
142k
      if (val)
818
104k
        f << "#tabs" << i << "[fl2=" << std::hex << val << std::dec << ",";
819
142k
      para.m_tabs->push_back(newTab);
820
142k
    }
821
10.5k
  }
822
52.4k
  if (input->tell() != fPos+1+sz)
823
17.2k
    ascii().addDelimiter(input->tell(), '|');
824
52.4k
  para.m_extra = f.str();
825
52.4k
  return true;
826
52.4k
}
827
828
/* read the page break separation */
829
bool MsWrd1Parser::readPageBreak(MWAWVec2i limits)
830
2.72k
{
831
2.72k
  MWAWInputStreamPtr input = getInput();
832
2.72k
  if (limits[1] <= limits[0] || !input->checkPosition(limits[1]*0x80)) {
833
0
    MWAW_DEBUG_MSG(("MsWrd1Parser::readPageBreak: the zone is not well defined\n"));
834
0
    return false;
835
0
  }
836
2.72k
  libmwaw::DebugStream f;
837
2.72k
  long pos = limits[0]*0x80;
838
2.72k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
839
2.72k
  f << "Entries(PageBreak):";
840
2.72k
  auto N = static_cast<int>(input->readULong(2));
841
2.72k
  f << "N=" << N << ",";
842
2.72k
  if (N==0 || 4+6*N > (limits[1]-limits[0])*0x80) {
843
906
    MWAW_DEBUG_MSG(("MsWrd1Parser::readPageBreak: the number of element seems odds\n"));
844
906
    f << "###";
845
906
    ascii().addPos(pos);
846
906
    ascii().addNote(f.str().c_str());
847
906
    return false;
848
906
  }
849
1.81k
  long val = static_cast<int>(input->readULong(2)); // 1|a
850
1.81k
  f << "unkn=" << val << ",";
851
1.81k
  MsWrd1ParserInternal::PLC plc(MsWrd1ParserInternal::PAGE);
852
663k
  for (int i = 0; i < N; i++) {
853
661k
    auto pg = static_cast<int>(input->readULong(2));
854
661k
    long textPos = long(input->readULong(4))+0x80;
855
661k
    f << "Page" << i << "=" << std::hex << textPos << std::dec;
856
661k
    if (pg != i+1) f << "[page=" << pg << "]";
857
661k
    if (textPos < m_state->m_eot) {
858
175k
      plc.m_id = pg;
859
175k
      m_state->m_plcMap.insert
860
175k
      (std::multimap<long, MsWrd1ParserInternal::PLC>::value_type(textPos, plc));
861
175k
    }
862
486k
    else if (i != N-1)
863
485k
      f << "###";
864
661k
    f << ",";
865
661k
  }
866
1.81k
  if (input->tell() != limits[1]*0x80)
867
1.81k
    ascii().addDelimiter(input->tell(),'|');
868
1.81k
  ascii().addPos(pos);
869
1.81k
  ascii().addNote(f.str().c_str());
870
1.81k
  return true;
871
2.72k
}
872
873
/* read the footnote zone */
874
bool MsWrd1Parser::readFootnoteCorrespondance(MWAWVec2i limits)
875
3.14k
{
876
3.14k
  MWAWInputStreamPtr input = getInput();
877
3.14k
  if (limits[1] <= limits[0] || !input->checkPosition(limits[1]*0x80)) {
878
0
    MWAW_DEBUG_MSG(("MsWrd1Parser::readFootnoteCorrespondance: the zone is not well defined\n"));
879
0
    return false;
880
0
  }
881
3.14k
  libmwaw::DebugStream f;
882
883
3.14k
  long textEnd = m_state->m_eot;
884
3.14k
  MsWrd1ParserInternal::PLC plc(MsWrd1ParserInternal::FOOTNOTE);
885
3.14k
  long pos = limits[0]*0x80;
886
3.14k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
887
3.14k
  f << "Entries(Footnote):";
888
3.14k
  auto N = static_cast<int>(input->readULong(2));
889
3.14k
  auto N1 = static_cast<int>(input->readULong(2));
890
3.14k
  f << "N=" << N << ",";
891
3.14k
  if (N!=N1) f << "N1=" << N1 << ",";
892
3.14k
  if (N!=N1 || N==0 || 4+8*N > (limits[1]-limits[0])*0x80) {
893
1.96k
    MWAW_DEBUG_MSG(("MsWrd1Parser::readFootnoteCorrespondance: the number of element seems odds\n"));
894
1.96k
    f << "###";
895
1.96k
    ascii().addPos(pos);
896
1.96k
    ascii().addNote(f.str().c_str());
897
1.96k
    return false;
898
1.96k
  }
899
1.18k
  std::map<long, int> footnoteMap;
900
207k
  for (int i = 0; i < N; i++) {
901
207k
    long textPos = long(input->readULong(4))+0x80;
902
207k
    long notePos = long(input->readULong(4))+0x80;
903
207k
    bool ok = textPos <= textEnd && notePos <= textEnd;
904
207k
    f << "Fn" << i << ":" << std::hex << textPos << "<->" << notePos << std::dec << ",";
905
207k
    if (!ok) {
906
143k
      if (i==N-1) break;
907
142k
      f << "###";
908
142k
      continue;
909
143k
    }
910
63.5k
    plc.m_id = int(footnoteMap.size());
911
63.5k
    footnoteMap[notePos]=plc.m_id;
912
63.5k
    m_state->m_plcMap.insert
913
63.5k
    (std::multimap<long, MsWrd1ParserInternal::PLC>::value_type(textPos, plc));
914
63.5k
    m_state->m_plcMap.insert
915
63.5k
    (std::multimap<long, MsWrd1ParserInternal::PLC>::value_type(notePos, plc));
916
63.5k
  }
917
1.18k
  m_state->m_footnotesList.resize(footnoteMap.size(),MWAWVec2l(0,0));
918
10.3k
  for (auto fIt=footnoteMap.begin(); fIt!=footnoteMap.end();) {
919
9.20k
    MWAWVec2l fPos;
920
9.20k
    fPos[0] = fIt->first;
921
9.20k
    int id = fIt++->second;
922
9.20k
    fPos[1] = fIt==footnoteMap.end() ? m_state->m_eot : fIt->first;
923
9.20k
    if (id >= int(m_state->m_footnotesList.size()))
924
510
      m_state->m_footnotesList.resize(size_t(id)+1,MWAWVec2l(0,0));
925
9.20k
    m_state->m_footnotesList[size_t(id)]=fPos;
926
9.20k
  }
927
1.18k
  ascii().addDelimiter(input->tell(),'|');
928
1.18k
  ascii().addPos(pos);
929
1.18k
  ascii().addNote(f.str().c_str());
930
931
1.18k
  return true;
932
3.14k
}
933
934
/* read the zone4: a list of main zone ( headers, footers ) ? */
935
bool MsWrd1Parser::readZones(MWAWVec2i limits)
936
2.69k
{
937
2.69k
  MWAWInputStreamPtr input = getInput();
938
2.69k
  if (limits[1] <= limits[0] || !input->checkPosition(limits[1]*0x80)) {
939
0
    MWAW_DEBUG_MSG(("MsWrd1Parser::readZones: the zone is not well defined\n"));
940
0
    return false;
941
0
  }
942
2.69k
  libmwaw::DebugStream f;
943
944
2.69k
  MsWrd1ParserInternal::PLC plc(MsWrd1ParserInternal::ZONE);
945
2.69k
  long pos = limits[0]*0x80;
946
2.69k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
947
2.69k
  f << "Entries(Zones):";
948
2.69k
  auto N = static_cast<int>(input->readULong(2));
949
2.69k
  auto N1 = static_cast<int>(input->readULong(2));
950
2.69k
  f << "N=" << N << ",";
951
2.69k
  if (N!=N1) f << "N1=" << N1 << ",";
952
2.69k
  if (N!=N1 || N==0 || 4+10*N > (limits[1]-limits[0])*0x80) {
953
2.08k
    MWAW_DEBUG_MSG(("MsWrd1Parser::readZones: the number of element seems odds\n"));
954
2.08k
    f << "###";
955
2.08k
    ascii().addPos(pos);
956
2.08k
    ascii().addNote(f.str().c_str());
957
2.08k
    return false;
958
2.08k
  }
959
51.6k
  for (int i = 0; i < N; i++) {
960
50.9k
    long textPos = long(input->readULong(4))+0x80;
961
50.9k
    f << std::hex << textPos << std::dec;
962
50.9k
    f << ":f0=" << input->readLong(2); // find 1|2|3
963
50.9k
    auto val = static_cast<int>(input->readLong(4)); // find -1, 0x900, 0xa00
964
50.9k
    if (val!=-1) f << ":f1=" << std::hex << val << std::dec;
965
50.9k
    if (textPos < m_state->m_eot) {
966
17.5k
      plc.m_id = i;
967
17.5k
      m_state->m_plcMap.insert
968
17.5k
      (std::multimap<long, MsWrd1ParserInternal::PLC>::value_type(textPos, plc));
969
17.5k
    }
970
33.4k
    else if (textPos != m_state->m_eot && i != N-1)
971
32.9k
      f << "###";
972
50.9k
    f << ",";
973
50.9k
  }
974
609
  ascii().addDelimiter(input->tell(),'|');
975
609
  ascii().addPos(pos);
976
609
  ascii().addNote(f.str().c_str());
977
978
609
  return true;
979
2.69k
}
980
981
/* read the document information */
982
bool MsWrd1Parser::readDocInfo(MWAWVec2i limits)
983
2.13k
{
984
2.13k
  MWAWInputStreamPtr input = getInput();
985
2.13k
  if (limits[1] != limits[0]+1 || !input->checkPosition(limits[1]*0x80)) {
986
60
    MWAW_DEBUG_MSG(("MsWrd1Parser::readDocInfo: the zone is not well defined\n"));
987
60
    return false;
988
60
  }
989
990
2.07k
  libmwaw::DebugStream f;
991
2.07k
  long pos = limits[0]*0x80;
992
2.07k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
993
2.07k
  f << "Entries(DocInfo):";
994
2.07k
  int val;
995
6.21k
  for (int i=0; i < 2; i++) { // find 66|0
996
4.14k
    val = static_cast<int>(input->readULong(1));
997
4.14k
    if (val)
998
2.74k
      f << "f" << i << "=" << std::hex << val << std::dec << ",";
999
4.14k
  }
1000
2.07k
  auto flags = static_cast<int>(input->readULong(1));
1001
2.07k
  switch (flags>>5) {
1002
1.09k
  case 0:
1003
1.09k
    f << "division=no,";
1004
1.09k
    break;
1005
90
  case 1:
1006
90
    f << "division=columns,";
1007
90
    break;
1008
361
  case 2:
1009
361
    f << "division=page,";
1010
361
    break; // default
1011
237
  case 3:
1012
237
    f << "division=evenpage,";
1013
237
    break;
1014
37
  case 4:
1015
37
    f << "division=oddpage,";
1016
37
    break;
1017
257
  default:
1018
257
    f << "#division=" << (flags>>5) << ",";
1019
257
    break;
1020
2.07k
  }
1021
2.07k
  switch ((flags>>2)&7) {
1022
919
  case 0: // default (numeric)
1023
919
    break;
1024
419
  case 1:
1025
419
    f << "numbering=roman[upper],";
1026
419
    break;
1027
62
  case 2:
1028
62
    f << "numbering=roman[lower],";
1029
62
    break;
1030
270
  case 3:
1031
270
    f << "numbering=alpha[upper],";
1032
270
    break;
1033
97
  case 4:
1034
97
    f << "numbering=alpha[lower],";
1035
97
    break;
1036
306
  default:
1037
306
    f << "#numbering[type]=" << ((flags>>2)&7) << ",";
1038
306
    break;
1039
2.07k
  }
1040
2.07k
  if (flags&3) f << "flags=" << (flags&3) << ",";
1041
1042
2.07k
  float pageDim[2];
1043
4.14k
  for (auto &d : pageDim) d = float(input->readULong(2))/1440.f;
1044
2.07k
  f << "dim=[" << pageDim[1] << "x" << pageDim[0] << "],";
1045
2.07k
  val = static_cast<int>(input->readLong(2));
1046
2.07k
  if (val != -1) f << "firstPage=" << val << ",";
1047
  // check me
1048
2.07k
  float pagePos[2][2]; // [Y|X][header|size]
1049
2.07k
  char const *wh[] = {"TopMargin", "Y[page]", "LeftMargin", "X[page]" };
1050
6.21k
  for (int i = 0; i < 2; i++) {
1051
12.4k
    for (int j = 0; j < 2; j++) {
1052
8.29k
      pagePos[i][j] = float(input->readULong(2))/1440.f;
1053
8.29k
      f << wh[i*2+j] << "=" << pagePos[i][j] << ",";
1054
8.29k
    }
1055
4.14k
  }
1056
2.07k
  flags = static_cast<int>(input->readULong(1));
1057
2.07k
  bool endNote = false;
1058
2.07k
  if (flags&1) {
1059
702
    f << "endnote,";
1060
702
    endNote = true;
1061
702
  }
1062
2.07k
  if (flags&2)
1063
537
    f << "autonumbering,";
1064
2.07k
  if (flags&0xFC)
1065
951
    f << "flags2=" << std::hex << (flags&0xFC) << std::dec << ",";
1066
2.07k
  ascii().addPos(pos);
1067
2.07k
  ascii().addNote(f.str().c_str());
1068
1069
2.07k
  pos = input->tell();
1070
2.07k
  f.str("");
1071
2.07k
  f << "DocInfo(II):";
1072
2.07k
  auto numCols = static_cast<int>(input->readULong(1));
1073
2.07k
  if (numCols != 1) {
1074
2.00k
    f << "nCols=" << numCols << ",";
1075
2.00k
    if (numCols < 1 || numCols > 6) {
1076
1.48k
      f << "###";
1077
1.48k
      numCols = 1;
1078
1.48k
    }
1079
2.00k
  }
1080
2.07k
  float hfLength[2];
1081
4.14k
  for (auto &hf : hfLength) hf = float(input->readULong(2))/1440.f;
1082
2.07k
  hfLength[1]=pageDim[0]-hfLength[1];
1083
1084
2.07k
  f << "headerLength=" << hfLength[0] << ",";
1085
2.07k
  f << "footerLength=" << hfLength[1] << ",";
1086
2.07k
  float colSep = float(input->readULong(2))/1440.f;
1087
2.07k
  f << "colSep=" << colSep << ",";
1088
2.07k
  val = static_cast<int>(input->readLong(2));
1089
2.07k
  if (val)
1090
1.47k
    f << "f3=" << val << ",";
1091
2.07k
  f << "distToHeader=" << float(input->readULong(2))/1440.f << ",";
1092
2.07k
  f << "distToNote=" << float(input->readULong(2))/1440.f << ",";
1093
  // probably follows by other distance
1094
1095
2.07k
  if (pageDim[0] > 0 && pageDim[1] > 0 &&
1096
1.58k
      pagePos[0][0]>=0 && pagePos[0][1]>=0 && pageDim[0] >= pagePos[0][0]+pagePos[0][1] &&
1097
743
      pagePos[1][0]>=0 && pagePos[1][1]>=0 && pageDim[1] >= pagePos[1][0]+pagePos[1][1] &&
1098
391
      pageDim[1] >= float(numCols)*pagePos[1][1]) {
1099
370
    getPageSpan().setMarginTop(double(pagePos[0][0]));
1100
370
    getPageSpan().setMarginLeft(double(pagePos[1][0]));
1101
370
    getPageSpan().setFormLength(double(pageDim[0]));
1102
370
    getPageSpan().setFormWidth(double(pageDim[1]));
1103
370
    m_state->m_endNote = endNote;
1104
370
    m_state->m_numColumns = numCols;
1105
370
    m_state->m_columnsSep = colSep;
1106
370
  }
1107
1.70k
  else {
1108
1.70k
    MWAW_DEBUG_MSG(("MsWrd1Parser::readDocInfo: some dimension do not look good\n"));
1109
1.70k
  }
1110
2.07k
  ascii().addDelimiter(input->tell(),'|');
1111
2.07k
  ascii().addPos(pos);
1112
2.07k
  ascii().addNote(f.str().c_str());
1113
2.07k
  ascii().addPos(pos+53);
1114
2.07k
  ascii().addNote("DocInfo(III)");
1115
2.07k
  return true;
1116
2.07k
}
1117
1118
// read a plc zone (char or paragraph properties )
1119
bool MsWrd1Parser::readPLC(MWAWVec2i limits, int wh)
1120
8.83k
{
1121
8.83k
  MWAWInputStreamPtr input = getInput();
1122
8.83k
  if (limits[1] <= limits[0] || !input->checkPosition(limits[1]*0x80)) {
1123
0
    MWAW_DEBUG_MSG(("MsWrd1Parser::readPLC: the zone is not well defined\n"));
1124
0
    return false;
1125
0
  }
1126
8.83k
  libmwaw::DebugStream f, f2;
1127
1128
8.83k
  std::map<long, int> posIdMap;
1129
8.83k
  MsWrd1ParserInternal::PLC plc(wh==0 ? MsWrd1ParserInternal::FONT :
1130
8.83k
                                MsWrd1ParserInternal::RULER);
1131
8.83k
  char const *what = wh==0 ? "Char" : "Para";
1132
1133
1.25M
  for (int z = limits[0], n=0; z < limits[1]; z++, n++) {
1134
1.24M
    f.str("");
1135
1.24M
    f << "Entries(" << what << ")[" << n << "]:";
1136
1.24M
    long pos = z*0x80;
1137
1.24M
    input->seek(pos+0x7f, librevenge::RVNG_SEEK_SET);
1138
1.24M
    auto N = static_cast<int>(input->readULong(1));
1139
1.24M
    f << "N=" << N << ",";
1140
1.24M
    if (4+N*6 > 0x7f) {
1141
389k
      f << "###";
1142
389k
      MWAW_DEBUG_MSG(("MsWrd1Parser::readPLC: the number of element seems to big\n"));
1143
389k
      ascii().addDelimiter(input->tell(),'|');
1144
389k
      ascii().addPos(pos);
1145
389k
      ascii().addNote(f.str().c_str());
1146
389k
      continue;
1147
389k
    }
1148
860k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1149
860k
    auto fPos = long(input->readULong(4));
1150
1151
2.48M
    for (int i = 0; i < N; i++) {
1152
1.62M
      f << "fPos=" << std::hex << fPos;
1153
1.62M
      auto newPos = long(input->readULong(4));
1154
1.62M
      f << "->" << newPos << std::dec;
1155
1156
1.62M
      auto depl = static_cast<int>(input->readLong(2));
1157
1.62M
      if (depl == -1)
1158
136k
        plc.m_id = -1;
1159
1.48M
      else if (depl < N*6 || 4+depl >= 0x7f) {
1160
1.34M
        f << "[###pos=" << std::hex << depl << std::dec << "]";
1161
1.34M
        plc.m_id = -1;
1162
1.34M
      }
1163
144k
      else {
1164
144k
        long dataPos = pos+depl+4;
1165
144k
        long actPos = input->tell();
1166
144k
        if (posIdMap.find(dataPos) == posIdMap.end()) {
1167
137k
          f2.str("");
1168
137k
          f2 << what << "-";
1169
137k
          if (wh == 0) {
1170
75.5k
            MsWrd1ParserInternal::Font font;
1171
75.5k
            if (readFont(dataPos, font)) {
1172
55.8k
              plc.m_id=int(m_state->m_fontsList.size());
1173
55.8k
              m_state->m_fontsList.push_back(font);
1174
55.8k
              f2 << plc.m_id << ":";
1175
#ifdef DEBUG
1176
              f2 << font.m_font.getDebugString(getFontConverter()) << font;
1177
#endif
1178
55.8k
            }
1179
19.6k
            else {
1180
19.6k
              plc.m_id = -1;
1181
19.6k
              f2 << "###";
1182
19.6k
            }
1183
75.5k
            ascii().addPos(dataPos);
1184
75.5k
            ascii().addNote(f2.str().c_str());
1185
75.5k
          }
1186
62.3k
          else {
1187
62.3k
            MsWrd1ParserInternal::Paragraph para;
1188
62.3k
            if (readParagraph(dataPos, para)) {
1189
52.4k
              plc.m_id=int(m_state->m_paragraphsList.size());
1190
52.4k
              m_state->m_paragraphsList.push_back(para);
1191
52.4k
              f2 << plc.m_id << ":" << para;
1192
52.4k
            }
1193
9.93k
            else {
1194
9.93k
              plc.m_id = -1;
1195
9.93k
              f2 << "###";
1196
9.93k
            }
1197
62.3k
            ascii().addPos(dataPos);
1198
62.3k
            ascii().addNote(f2.str().c_str());
1199
62.3k
          }
1200
137k
          posIdMap[dataPos] = plc.m_id;
1201
137k
        }
1202
6.14k
        else
1203
6.14k
          plc.m_id = posIdMap.find(dataPos)->second;
1204
144k
        input->seek(actPos, librevenge::RVNG_SEEK_SET);
1205
144k
      }
1206
1.62M
      m_state->m_plcMap.insert
1207
1.62M
      (std::multimap<long, MsWrd1ParserInternal::PLC>::value_type(fPos, plc));
1208
1.62M
      fPos = newPos;
1209
1.62M
      f << ":" << plc << ",";
1210
1.62M
    }
1211
860k
    ascii().addDelimiter(input->tell(),'|');
1212
860k
    ascii().addPos(pos);
1213
860k
    ascii().addNote(f.str().c_str());
1214
860k
  }
1215
1216
8.83k
  return true;
1217
8.83k
}
1218
1219
////////////////////////////////////////////////////////////
1220
// try to read a text entry
1221
////////////////////////////////////////////////////////////
1222
bool MsWrd1Parser::sendText(MWAWEntry const &textEntry, bool isMain)
1223
91.4k
{
1224
91.4k
  if (!textEntry.valid()) return false;
1225
90.7k
  if (!getTextListener()) {
1226
0
    MWAW_DEBUG_MSG(("MsWrd1Parser::sendText: can not find a listener!"));
1227
0
    return true;
1228
0
  }
1229
90.7k
  if (isMain) {
1230
13.5k
    int numCols = m_state->m_numColumns;
1231
13.5k
    if (numCols > 1 && !getTextListener()->isSectionOpened()) {
1232
248
      MWAWSection sec;
1233
248
      sec.setColumns(numCols, getPageWidth()/double(numCols), librevenge::RVNG_INCH, double(m_state->m_columnsSep));
1234
248
      getTextListener()->openSection(sec);
1235
248
    }
1236
13.5k
  }
1237
90.7k
  long pos = textEntry.begin();
1238
90.7k
  MWAWInputStreamPtr input = getInput();
1239
1240
90.7k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1241
90.7k
  libmwaw::DebugStream f;
1242
90.7k
  f << "TextContent:";
1243
90.7k
  int actFId=-1, actRId = -1, actPage=0;
1244
90.7k
  auto plcIt = m_state->m_plcMap.begin();
1245
26.3M
  while (plcIt != m_state->m_plcMap.end() && plcIt->first < pos) {
1246
26.2M
    MsWrd1ParserInternal::PLC const &plc = plcIt++->second;
1247
26.2M
    if (plc.m_type == MsWrd1ParserInternal::FONT)
1248
419k
      actFId = plc.m_id;
1249
25.8M
    else if (plc.m_type == MsWrd1ParserInternal::RULER)
1250
441k
      actRId = plc.m_id;
1251
25.3M
    else if (plc.m_type == MsWrd1ParserInternal::PAGE)
1252
391k
      actPage++;
1253
26.2M
  }
1254
  // new page can be in header, ..., so sometimes we must force a new page...
1255
90.7k
  if (isMain && actPage > m_state->m_actPage)
1256
855
    newPage(actPage);
1257
90.7k
  MsWrd1ParserInternal::Font actFont, defFont;
1258
90.7k
  defFont.m_font = MWAWFont(3,12);
1259
90.7k
  if (actFId>=0 && actFId < int(m_state->m_fontsList.size()))
1260
8.10k
    actFont = m_state->m_fontsList[size_t(actFId)];
1261
82.5k
  else
1262
82.5k
    actFont = defFont;
1263
90.7k
  bool rulerNotSent = actRId != -1, fontNotSent = true;
1264
13.4M
  while (!input->isEnd() && input->tell() < textEntry.end()) {
1265
13.3M
    long actPos = input->tell();
1266
13.3M
    bool firstPlc = true;
1267
14.0M
    while (plcIt != m_state->m_plcMap.end() && plcIt->first <= actPos) {
1268
637k
      if (firstPlc) {
1269
285k
        ascii().addPos(pos);
1270
285k
        ascii().addNote(f.str().c_str());
1271
285k
        pos = actPos;
1272
285k
        f.str("");
1273
285k
        f << "TextContent:";
1274
285k
        firstPlc = false;
1275
285k
      }
1276
1277
637k
      auto const &plc = plcIt++->second;
1278
637k
      switch (plc.m_type) {
1279
59.3k
      case MsWrd1ParserInternal::FONT:
1280
59.3k
        if (plc.m_id >= 0 && plc.m_id < int(m_state->m_fontsList.size()))
1281
34.9k
          getTextListener()->setFont(m_state->m_fontsList[size_t(plc.m_id)].m_font);
1282
24.4k
        else
1283
24.4k
          getTextListener()->setFont(defFont.m_font);
1284
59.3k
        actFont.m_font = getTextListener()->getFont();
1285
59.3k
        fontNotSent = false;
1286
59.3k
        break;
1287
145k
      case MsWrd1ParserInternal::RULER:
1288
145k
        actRId = plc.m_id;
1289
145k
        rulerNotSent = true;
1290
145k
        break;
1291
202k
      case MsWrd1ParserInternal::PAGE:
1292
202k
        if (isMain) newPage(++actPage);
1293
202k
        break;
1294
212k
      case MsWrd1ParserInternal::FOOTNOTE: {
1295
212k
        if (!isMain) break;
1296
72.4k
        if (plc.m_id < 0 || plc.m_id >= int(m_state->m_footnotesList.size())) {
1297
0
          MWAW_DEBUG_MSG(("MsWrd1Parser::sendText: oops, can not find a footnote!\n"));
1298
0
          break;
1299
0
        }
1300
72.4k
        MWAWEntry entry;
1301
72.4k
        entry.setBegin(m_state->m_footnotesList[size_t(plc.m_id)][0]);
1302
72.4k
        entry.setEnd(m_state->m_footnotesList[size_t(plc.m_id)][1]);
1303
72.4k
        removeLastCharIfEOL(entry);
1304
72.4k
        std::shared_ptr<MWAWSubDocument> subdoc
1305
72.4k
        (new MsWrd1ParserInternal::SubDocument(*this, getInput(), entry));
1306
72.4k
        getTextListener()->insertNote(MWAWNote(m_state->m_endNote ? MWAWNote::EndNote : MWAWNote::FootNote), subdoc);
1307
72.4k
        break;
1308
72.4k
      }
1309
17.5k
      case MsWrd1ParserInternal::ZONE:
1310
17.5k
      case MsWrd1ParserInternal::UNKNOWN:
1311
#if !defined(__clang__)
1312
      default:
1313
#endif
1314
17.5k
        break;
1315
637k
      }
1316
637k
      f << "[" << plc << "]";
1317
637k
    }
1318
13.3M
    if (rulerNotSent) {
1319
127k
      if (actRId >= 0 && actRId < int(m_state->m_paragraphsList.size()))
1320
36.7k
        setProperty(m_state->m_paragraphsList[size_t(actRId)]);
1321
90.8k
      else
1322
90.8k
        setProperty(MsWrd1ParserInternal::Paragraph());
1323
127k
      rulerNotSent = false;
1324
127k
    }
1325
13.3M
    if (fontNotSent) getTextListener()->setFont(actFont.m_font);
1326
13.3M
    auto c = static_cast<unsigned char>(input->readULong(1));
1327
13.3M
    f << char(c);
1328
13.3M
    switch (c) {
1329
772k
    case 1:
1330
772k
      getTextListener()->insertUnicodeString(librevenge::RVNGString("(picture)"));
1331
772k
      break;
1332
60.5k
    case 5: // footnote mark
1333
93.8k
    case 0xc: // end of file
1334
93.8k
      break;
1335
135k
    case 0x9:
1336
135k
      getTextListener()->insertTab();
1337
135k
      break;
1338
570k
    case 0xd:
1339
570k
      getTextListener()->insertEOL();
1340
570k
      break;
1341
11.8M
    default:
1342
11.8M
      getTextListener()->insertCharacter(static_cast<unsigned char>(c), input, textEntry.end());
1343
11.8M
      break;
1344
13.3M
    }
1345
13.3M
  }
1346
90.7k
  ascii().addPos(pos);
1347
90.7k
  ascii().addNote(f.str().c_str());
1348
90.7k
  return true;
1349
90.7k
}
1350
1351
// send the ruler properties
1352
void MsWrd1Parser::setProperty(MsWrd1ParserInternal::Paragraph const &para)
1353
127k
{
1354
127k
  if (!getTextListener()) return;
1355
127k
  getTextListener()->setParagraph(para);
1356
127k
}
1357
1358
////////////////////////////////////////////////////////////
1359
// Low level
1360
////////////////////////////////////////////////////////////
1361
1362
// read the header
1363
bool MsWrd1Parser::checkHeader(MWAWHeader *header, bool strict)
1364
47.0k
{
1365
47.0k
  *m_state = MsWrd1ParserInternal::State();
1366
47.0k
  MWAWInputStreamPtr input = getInput();
1367
47.0k
  if (!input || !input->hasDataFork())
1368
0
    return false;
1369
1370
47.0k
  libmwaw::DebugStream f;
1371
47.0k
  if (!input->checkPosition(0x80)) {
1372
341
    MWAW_DEBUG_MSG(("MsWrd1Parser::checkHeader: file is too short\n"));
1373
341
    return false;
1374
341
  }
1375
46.7k
  long pos = 0;
1376
46.7k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1377
46.7k
  auto val = static_cast<int>(input->readULong(2));
1378
46.7k
  switch (val) {
1379
46.5k
  case 0xfe32:
1380
46.5k
    switch (input->readULong(2)) {
1381
46.5k
    case 0x0:
1382
46.5k
      setVersion(1);
1383
46.5k
      break;
1384
0
    default:
1385
0
      return false;
1386
46.5k
    }
1387
46.5k
    break;
1388
46.5k
  default:
1389
156
    return false;
1390
46.7k
  }
1391
1392
46.5k
  f << "FileHeader:";
1393
46.5k
  val = static_cast<int>(input->readULong(1)); // v1: ab other 0 ?
1394
46.5k
  if (val) f << "f0=" << val << ",";
1395
139k
  for (int i = 1; i < 3; i++) { // always 0
1396
93.1k
    val = static_cast<int>(input->readLong(2));
1397
93.1k
    if (val) f << "f" << i << "=" << val << ",";
1398
93.1k
  }
1399
279k
  for (int i = 0; i < 5; i++) { // always 0 ?
1400
232k
    val = static_cast<int>(input->readLong(1));
1401
232k
    if (val) f << "g" << i << "=" << val << ",";
1402
232k
  }
1403
1404
46.5k
  m_state->m_eot = long(input->readULong(4));
1405
46.5k
  f << "text=" << std::hex << 0x80 << "<->" << m_state->m_eot << ",";
1406
46.5k
  if (0x80 > m_state->m_eot || !input->checkPosition(m_state->m_eot)) {
1407
1.55k
    MWAW_DEBUG_MSG(("MsWrd1Parser::checkHeader: problem with text position must stop\n"));
1408
1.55k
    return false;
1409
1.55k
  }
1410
1411
45.0k
  m_state->m_fileZonesLimit[0] = int((m_state->m_eot+0x7f)/0x80);
1412
45.0k
  f << "zonesPos=[" << std::hex;
1413
260k
  for (int i = 0; i < 6; i++) {
1414
226k
    m_state->m_fileZonesLimit[i+1] = static_cast<int>(input->readLong(2));
1415
226k
    if (m_state->m_fileZonesLimit[i]==m_state->m_fileZonesLimit[i+1]) {
1416
8.43k
      f << "_,";
1417
8.43k
      continue;
1418
8.43k
    }
1419
217k
    if (m_state->m_fileZonesLimit[i]<m_state->m_fileZonesLimit[i+1]) {
1420
86.5k
      f << m_state->m_fileZonesLimit[i]*0x80 << "<->"
1421
86.5k
        << m_state->m_fileZonesLimit[i+1]*0x80 << ",";
1422
86.5k
      continue;
1423
86.5k
    }
1424
131k
    MWAW_DEBUG_MSG(("MsWrd1Parser::checkHeader: problem reading the zones positions\n"));
1425
131k
    if (strict) return false;
1426
120k
    f << "###" << m_state->m_fileZonesLimit[i+1]*0x80 << ",";
1427
120k
    m_state->m_fileZonesLimit[i+1] = m_state->m_fileZonesLimit[i];
1428
120k
  }
1429
34.7k
  f << std::dec << "],";
1430
34.7k
  ascii().addPos(pos);
1431
34.7k
  ascii().addNote(f.str().c_str());
1432
34.7k
  pos = input->tell();
1433
34.7k
  f.str("");
1434
34.7k
  f << "FileHeader[A]:";
1435
625k
  for (int i = 0; i < 17; i++) {
1436
591k
    val = static_cast<int>(input->readLong(2));
1437
591k
    if (val) f << "f" << i << "=" << val << ",";
1438
591k
  }
1439
34.7k
  ascii().addPos(pos);
1440
34.7k
  ascii().addNote(f.str().c_str());
1441
1442
34.7k
  long textSize[2];
1443
69.5k
  for (auto &tSize : textSize) tSize = input->readLong(4);
1444
34.7k
  if (textSize[0] != textSize[1] || 0x80+textSize[0] != m_state->m_eot) {
1445
28.7k
    MWAW_DEBUG_MSG(("MsWrd1Parser::checkHeader: problem with text position length\n"));
1446
28.7k
    if (strict) return false;
1447
28.2k
    f << "##textSize=" << std::hex << textSize[0] << ":" << textSize[1] << std::dec << ",";
1448
28.2k
    if (textSize[1] > textSize[0]) textSize[0] = textSize[1];
1449
28.2k
    if (0x80+textSize[0] > m_state->m_eot && input->checkPosition(0x80+textSize[0]))
1450
3.11k
      m_state->m_eot = 0x80+textSize[0];
1451
28.2k
  }
1452
34.2k
  pos=input->tell();
1453
34.2k
  f.str("");
1454
34.2k
  f << "FileHeader[B]:";
1455
993k
  for (int i = 0; i < 28; i++) { // always 0
1456
959k
    val = static_cast<int>(input->readLong(2));
1457
959k
    if (val) f << "f" << i << "=" << val << ",";
1458
959k
  }
1459
34.2k
  ascii().addPos(pos);
1460
34.2k
  ascii().addNote(f.str().c_str());
1461
34.2k
  if (header)
1462
12.6k
    header->reset(MWAWDocument::MWAW_T_MICROSOFTWORD, 1);
1463
34.2k
  return true;
1464
34.7k
}
1465
1466
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: