Coverage Report

Created: 2026-10-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/NisusWrtText.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 <map>
38
#include <sstream>
39
40
#include <librevenge/librevenge.h>
41
42
#include "MWAWTextListener.hxx"
43
#include "MWAWDebug.hxx"
44
#include "MWAWFont.hxx"
45
#include "MWAWFontConverter.hxx"
46
#include "MWAWPageSpan.hxx"
47
#include "MWAWParagraph.hxx"
48
#include "MWAWPosition.hxx"
49
#include "MWAWRSRCParser.hxx"
50
#include "MWAWSection.hxx"
51
#include "MWAWSubDocument.hxx"
52
53
#include "NisusWrtParser.hxx"
54
#include "NisusWrtStruct.hxx"
55
56
#include "NisusWrtText.hxx"
57
58
/** Internal: the structures of a NisusWrtText */
59
namespace NisusWrtTextInternal
60
{
61
/** Internal: the fonts and many other data*/
62
struct Font {
63
  //! the constructor
64
  Font()
65
124k
    : m_font(-1, -1)
66
124k
    , m_pictureId(0)
67
124k
    , m_pictureWidth(0)
68
124k
    , m_markId(-1)
69
124k
    , m_variableId(0)
70
124k
    , m_format(0)
71
124k
    , m_format2(0)
72
124k
    , m_extra("")
73
124k
  {
74
124k
  }
75
  bool isVariable() const
76
178k
  {
77
178k
    return (m_format2&0x20)==0x20;
78
178k
  }
79
80
  //! operator<<
81
  friend std::ostream &operator<<(std::ostream &o, Font const &font);
82
83
  //! the font
84
  MWAWFont m_font;
85
  //! the picture id ( if this is for a picture )
86
  int m_pictureId;
87
  //! the picture width
88
  int m_pictureWidth;
89
  //! a mark id
90
  int m_markId;
91
  //! the variable id : in fact cst[unkn] + v_id
92
  int m_variableId;
93
  //! the main format ...
94
  int m_format;
95
  //! a series of flags
96
  int m_format2;
97
  //! two picture dim ( orig && file ?)
98
  MWAWBox2i m_pictureDim[2];
99
  //! extra data
100
  std::string m_extra;
101
};
102
103
104
std::ostream &operator<<(std::ostream &o, Font const &font)
105
0
{
106
0
  if (font.m_pictureId) o << "pictId=" << font.m_pictureId << ",";
107
0
  if (font.m_pictureWidth) o << "pictW=" << font.m_pictureWidth << ",";
108
0
  if (font.m_markId >= 0) o << "markId=" << font.m_markId << ",";
109
0
  if (font.m_variableId > 0) o << "variableId=" << font.m_variableId << ",";
110
0
  if (font.m_format2&0x4) o << "index,";
111
0
  if (font.m_format2&0x8) o << "TOC,";
112
0
  if (font.m_format2&0x10) o << "samePage,";
113
0
  if (font.m_format2&0x20) o << "variable,";
114
0
  if (font.m_format2&0x40) o << "hyphenate,";
115
0
  if (font.m_format2&0x83)
116
0
    o << "#format2=" << std::hex << (font.m_format2 &0x83) << std::dec << ",";
117
118
0
  if (font.m_format & 1) o << "noSpell,";
119
0
  if (font.m_format & 0x10) o << "sameLine,";
120
0
  if (font.m_format & 0x40) o << "endOfPage,"; // checkme
121
0
  if (font.m_format & 0xA6)
122
0
    o << "#fl=" << std::hex << (font.m_format & 0xA6) << std::dec << ",";
123
0
  if (font.m_pictureDim[0].size()[0] || font.m_pictureDim[0].size()[1])
124
0
    o << "pictDim=" << font.m_pictureDim[0] << ",";
125
0
  if (font.m_pictureDim[0] != font.m_pictureDim[1] &&
126
0
      (font.m_pictureDim[1].size()[0] || font.m_pictureDim[1].size()[1]))
127
0
    o << "pictDim[crop]=" << font.m_pictureDim[1] << ",";
128
0
  if (font.m_extra.length())
129
0
    o << font.m_extra << ",";
130
0
  return o;
131
0
}
132
133
/** Internal: class to store the paragraph properties */
134
struct Paragraph final : public MWAWParagraph {
135
  //! Constructor
136
  Paragraph()
137
60.3k
    : MWAWParagraph()
138
60.3k
    , m_name("")
139
60.3k
  {
140
60.3k
  }
141
146k
  Paragraph(Paragraph const &) = default;
142
  //! destructor
143
  ~Paragraph() final;
144
  //! operator<<
145
  friend std::ostream &operator<<(std::ostream &o, Paragraph const &ind)
146
0
  {
147
0
    o << static_cast<MWAWParagraph const &>(ind);
148
0
    if (ind.m_name.length()) o << "name=" << ind.m_name << ",";
149
0
    return o;
150
0
  }
151
  //! the paragraph name
152
  std::string m_name;
153
};
154
155
Paragraph::~Paragraph()
156
206k
{
157
206k
}
158
159
/** Internal structure: use to store a header */
160
struct HeaderFooter {
161
  //! Constructor
162
  HeaderFooter()
163
0
    : m_type(MWAWHeaderFooter::HEADER)
164
0
    , m_occurrence(MWAWHeaderFooter::NEVER)
165
0
    , m_page(0)
166
0
    , m_textParagraph(-1)
167
0
    , m_unknown(0)
168
0
    , m_parsed(false)
169
0
    , m_extra("")
170
0
  {
171
0
    for (auto &id : m_paragraph) id = -1;
172
0
  }
173
  //! operator<<
174
  friend std::ostream &operator<<(std::ostream &o, HeaderFooter const &hf);
175
  //! the header type
176
  MWAWHeaderFooter::Type m_type;
177
  //! the header occurrence
178
  MWAWHeaderFooter::Occurrence m_occurrence;
179
  //! the page
180
  int m_page;
181
  //! the paragraph position in the header zone (first and last)
182
  long m_paragraph[2];
183
  //! the text position
184
  long m_textParagraph;
185
  //! a unknown value
186
  int m_unknown;
187
  //! a flag to know if the footnote is parsed
188
  mutable bool m_parsed;
189
  //! some extra debuging information
190
  std::string m_extra;
191
};
192
193
std::ostream &operator<<(std::ostream &o, HeaderFooter const &hf)
194
0
{
195
0
  if (hf.m_type==MWAWHeaderFooter::HEADER) o << "header,";
196
0
  else o << "footer,";
197
0
  switch (hf.m_occurrence) {
198
0
  case MWAWHeaderFooter::NEVER:
199
0
    o << "never,";
200
0
    break;
201
0
  case MWAWHeaderFooter::ODD:
202
0
    o << "odd,";
203
0
    break;
204
0
  case MWAWHeaderFooter::EVEN:
205
0
    o << "even,";
206
0
    break;
207
0
  case MWAWHeaderFooter::ALL:
208
0
    o << "all,";
209
0
    break;
210
#if !defined(__clang__)
211
  default:
212
    o << "#occurrence=" << int(hf.m_occurrence) << ",";
213
    break;
214
#endif
215
0
  }
216
0
  o << "pos=" << hf.m_paragraph[0] << "<->" << hf.m_paragraph[1] << ",";
217
0
  o << "pos[def]=" << hf.m_textParagraph << ",";
218
0
  if (hf.m_unknown) o << "unkn=" << std::hex << hf.m_unknown << std::dec << ",";
219
0
  o << hf.m_extra;
220
0
  return o;
221
0
}
222
223
/** Internal structure: use to store a footnote */
224
struct Footnote {
225
  //! Constructor
226
  Footnote()
227
0
    : m_number(0)
228
0
    , m_textPosition()
229
0
    , m_textLabel("")
230
0
    , m_noteLabel("")
231
0
    , m_parsed(false)
232
0
    , m_extra("")
233
0
  {
234
0
    for (auto &id : m_paragraph) id = -1;
235
0
  }
236
237
  //! returns a label corresponding to a note ( or nothing if we can use numbering note)
238
  std::string getTextLabel(int actId) const
239
0
  {
240
0
    if (m_textLabel.length() == 0 || m_textLabel=="1")
241
0
      return std::string("");
242
0
    std::stringstream s;
243
0
    for (char c : m_textLabel) {
244
0
      if (c=='1')
245
0
        s << actId;
246
0
      else
247
0
        s << c;
248
0
    }
249
0
    return s.str();
250
0
  }
251
252
  //! operator<<
253
  friend std::ostream &operator<<(std::ostream &o, Footnote const &ft);
254
  //! the note number
255
  int m_number;
256
  //! the paragraph position in the footnote zone (first and last)
257
  int m_paragraph[2];
258
  //! the text position
259
  NisusWrtStruct::Position m_textPosition;
260
  //! the label in the text
261
  std::string m_textLabel;
262
  //! the label in the note
263
  std::string m_noteLabel;
264
  //! a flag to know if the footnote is parsed
265
  mutable bool m_parsed;
266
  //! some extra debuging information
267
  std::string m_extra;
268
};
269
270
std::ostream &operator<<(std::ostream &o, Footnote const &ft)
271
0
{
272
0
  o << "pos=" << ft.m_textPosition << ",";
273
0
  if (ft.m_paragraph[1] > ft.m_paragraph[0])
274
0
    o << "paragraph[inNote]=" << ft.m_paragraph[0] << "<->" << ft.m_paragraph[1] << ",";
275
0
  if (ft.m_number) o << "number=" << ft.m_number << ",";
276
0
  if (ft.m_textLabel.length())
277
0
    o << "textLabel=\"" << ft.m_textLabel << "\",";
278
0
  if (ft.m_noteLabel.length())
279
0
    o << "noteLabel=\"" << ft.m_noteLabel << "\",";
280
0
  if (ft.m_extra.length())
281
0
    o << ft.m_extra;
282
0
  return o;
283
0
}
284
285
//! Internal: the picture data ( PICD )
286
struct PicturePara {
287
  //! constructor
288
  PicturePara()
289
153k
    : m_id(-1)
290
153k
    , m_paragraph(-1)
291
153k
    , m_position()
292
153k
  {
293
153k
  }
294
  //! operator<<
295
  friend std::ostream &operator<<(std::ostream &o, PicturePara const &pict);
296
  //! the picture id
297
  int m_id;
298
  //! the paragraph position
299
  int m_paragraph;
300
  //! the position
301
  MWAWBox2i m_position;
302
};
303
304
std::ostream &operator<<(std::ostream &o, PicturePara const &pict)
305
0
{
306
0
  if (pict.m_id > 0) o << "pictId=" << pict.m_id << ",";
307
0
  if (pict.m_paragraph >= 0) o << "paragraph=" << pict.m_paragraph << ",";
308
0
  if (pict.m_position.size()[0] || pict.m_position.size()[1])
309
0
    o << "pos=" << pict.m_position << ",";
310
0
  return o;
311
0
}
312
313
/** different types
314
 *
315
 * - Format: font properties
316
 * - Ruler: new ruler
317
 */
318
enum PLCType { P_Format=0, P_Ruler, P_Footnote, P_HeaderFooter, P_PicturePara, P_Unknown};
319
320
/** Internal: class to store the PLC: Pointer List Content ? */
321
struct DataPLC {
322
  DataPLC()
323
15.0k
    : m_type(P_Format)
324
15.0k
    , m_id(-1)
325
15.0k
    , m_extra("")
326
15.0k
  {
327
15.0k
  }
328
  //! operator<<
329
  friend std::ostream &operator<<(std::ostream &o, DataPLC const &plc);
330
  //! PLC type
331
  PLCType m_type;
332
  //! the id
333
  int m_id;
334
  //! an extra data to store message ( if needed )
335
  std::string m_extra;
336
};
337
//! operator<< for DataPLC
338
std::ostream &operator<<(std::ostream &o, DataPLC const &plc)
339
0
{
340
0
  switch (plc.m_type) {
341
0
  case P_Format:
342
0
    o << "F";
343
0
    break;
344
0
  case P_Ruler:
345
0
    o << "R";
346
0
    break;
347
0
  case P_Footnote:
348
0
    o << "Fn";
349
0
    break;
350
0
  case P_HeaderFooter:
351
0
    o << "HF";
352
0
    break;
353
0
  case P_PicturePara:
354
0
    o << "Pict";
355
0
    break;
356
0
  case P_Unknown:
357
#if !defined(__clang__)
358
  default:
359
#endif
360
0
    o << "#type=" << int(plc.m_type) << ",";
361
0
  }
362
0
  if (plc.m_id >= 0) o << plc.m_id << ",";
363
0
  else o << "_";
364
0
  if (plc.m_extra.length()) o << plc.m_extra;
365
0
  return o;
366
0
}
367
368
//! internal structure used to store zone data
369
struct Zone {
370
  typedef std::multimap<NisusWrtStruct::Position, DataPLC, NisusWrtStruct::Position::Compare> PLCMap;
371
372
  //! constructor
373
  Zone()
374
47.6k
    : m_entry()
375
47.6k
    , m_paragraphList()
376
47.6k
    , m_pictureParaList()
377
47.6k
    , m_plcMap()
378
47.6k
  {
379
47.6k
  }
380
  //! the position of text in the rsrc file
381
  MWAWEntry m_entry;
382
  //! the list of paragraph
383
  std::vector<Paragraph> m_paragraphList;
384
  //! the list of paragraph
385
  std::vector<PicturePara> m_pictureParaList;
386
387
  //! the map pos -> format id
388
  PLCMap m_plcMap;
389
};
390
391
////////////////////////////////////////
392
//! Internal: the state of a NisusWrtText
393
struct State {
394
  //! constructor
395
  State()
396
15.8k
    : m_version(-1)
397
15.8k
    , m_fontList()
398
15.8k
    , m_footnoteList()
399
15.8k
    , m_numPages(-1)
400
15.8k
    , m_actualPage(0)
401
15.8k
    , m_hfList()
402
15.8k
    , m_headersId()
403
15.8k
    , m_footersId()
404
15.8k
  {
405
15.8k
  }
406
407
  //! the file version
408
  mutable int m_version;
409
410
  /** the font list */
411
  std::vector<Font> m_fontList;
412
  /** the list of footnote */
413
  std::vector<Footnote> m_footnoteList;
414
  /** the main zones : Main, Footnote, HeaderFooter */
415
  Zone m_zones[3];
416
417
  int m_numPages /* the number of pages */, m_actualPage /* the actual page */;
418
  /** the list of header footer */
419
  std::vector<HeaderFooter> m_hfList;
420
  /** the list of header id which corresponds to each page */
421
  std::vector<int> m_headersId;
422
  /** the list of footer id which corresponds to each page */
423
  std::vector<int> m_footersId;
424
};
425
426
////////////////////////////////////////
427
//! Internal: the subdocument of a NisusWrtText
428
class SubDocument final : public MWAWSubDocument
429
{
430
public:
431
  SubDocument(NisusWrtText &pars, MWAWInputStreamPtr const &input, int id, libmwaw::SubDocumentType type)
432
0
    : MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
433
0
    , m_textParser(&pars)
434
0
    , m_id(id)
435
0
    , m_type(type)
436
0
  {
437
0
  }
438
439
  //! destructor
440
0
  ~SubDocument() final {}
441
442
  //! operator!=
443
  bool operator!=(MWAWSubDocument const &doc) const final;
444
445
  //! the parser function
446
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
447
448
protected:
449
  /** the text parser */
450
  NisusWrtText *m_textParser;
451
  //! the subdocument id
452
  int m_id;
453
  //! the subdocument type
454
  libmwaw::SubDocumentType m_type;
455
private:
456
  SubDocument(SubDocument const &orig) = delete;
457
  SubDocument &operator=(SubDocument const &orig) = delete;
458
};
459
460
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
461
0
{
462
0
  if (!listener.get()) {
463
0
    MWAW_DEBUG_MSG(("NisusWrtTextInternal::SubDocument::parse: no listener\n"));
464
0
    return;
465
0
  }
466
0
  if (!m_textParser) {
467
0
    MWAW_DEBUG_MSG(("NisusWrtTextInternal::SubDocument::parse: no parser\n"));
468
0
    return;
469
0
  }
470
0
  long pos = m_input->tell();
471
0
  if (m_type == libmwaw::DOC_NOTE)
472
0
    m_textParser->sendFootnote(m_id);
473
0
  else if (m_type == libmwaw::DOC_HEADER_FOOTER)
474
0
    m_textParser->sendHeaderFooter(m_id);
475
0
  else {
476
0
    MWAW_DEBUG_MSG(("NisusWrtTextInternal::SubDocument::parse: oops do not know how to send this kind of document\n"));
477
0
    return;
478
0
  }
479
0
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
480
0
}
481
482
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
483
0
{
484
0
  if (MWAWSubDocument::operator!=(doc)) return true;
485
0
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
486
0
  if (!sDoc) return true;
487
0
  if (m_textParser != sDoc->m_textParser) return true;
488
0
  if (m_id != sDoc->m_id) return true;
489
0
  if (m_type != sDoc->m_type) return true;
490
0
  return false;
491
0
}
492
}
493
494
////////////////////////////////////////////////////////////
495
// constructor/destructor, ...
496
////////////////////////////////////////////////////////////
497
NisusWrtText::NisusWrtText(NisusWrtParser &parser)
498
15.8k
  : m_parserState(parser.getParserState())
499
15.8k
  , m_state(new NisusWrtTextInternal::State)
500
15.8k
  , m_mainParser(&parser)
501
15.8k
{
502
15.8k
}
503
504
NisusWrtText::~NisusWrtText()
505
15.8k
{
506
15.8k
}
507
508
int NisusWrtText::version() const
509
0
{
510
0
  if (m_state->m_version < 0)
511
0
    m_state->m_version = m_parserState->m_version;
512
0
  return m_state->m_version;
513
0
}
514
515
int NisusWrtText::numPages() const
516
7.50k
{
517
7.50k
  if (m_state->m_numPages >= 0)
518
7.50k
    return m_state->m_numPages;
519
0
  const_cast<NisusWrtText *>(this)->computePositions();
520
0
  return m_state->m_numPages;
521
7.50k
}
522
523
std::shared_ptr<MWAWSubDocument> NisusWrtText::getHeader(int page, int &numSimilar)
524
1.73M
{
525
1.73M
  numSimilar=1;
526
1.73M
  std::shared_ptr<MWAWSubDocument> res;
527
1.73M
  auto numHeaders = int(m_state->m_headersId.size());
528
1.73M
  if (page < 1 || page-1 >= numHeaders) {
529
1.72M
    if (m_state->m_numPages>page)
530
0
      numSimilar=m_state->m_numPages-page+1;
531
1.72M
    return res;
532
1.72M
  }
533
7.13k
  int hId = m_state->m_headersId[size_t(page-1)];
534
7.13k
  if (hId >= 0)
535
0
    res.reset(new NisusWrtTextInternal::SubDocument(*this, m_mainParser->rsrcInput(), hId, libmwaw::DOC_HEADER_FOOTER));
536
8.48k
  while (page < numHeaders && m_state->m_headersId[size_t(page)]==hId) {
537
1.34k
    page++;
538
1.34k
    numSimilar++;
539
1.34k
  }
540
7.13k
  return res;
541
1.73M
}
542
543
std::shared_ptr<MWAWSubDocument> NisusWrtText::getFooter(int page, int &numSimilar)
544
1.73M
{
545
1.73M
  numSimilar=1;
546
1.73M
  std::shared_ptr<MWAWSubDocument> res;
547
1.73M
  auto numFooters = int(m_state->m_footersId.size());
548
1.73M
  if (page < 1 || page-1 >= numFooters) {
549
1.72M
    if (m_state->m_numPages>page)
550
0
      numSimilar=m_state->m_numPages-page+1;
551
1.72M
    return res;
552
1.72M
  }
553
7.13k
  int fId = m_state->m_footersId[size_t(page-1)];
554
7.13k
  if (fId >= 0)
555
0
    res.reset(new NisusWrtTextInternal::SubDocument(*this, m_mainParser->rsrcInput(), fId, libmwaw::DOC_HEADER_FOOTER));
556
8.48k
  while (page < numFooters && m_state->m_footersId[size_t(page)]==fId) {
557
1.34k
    page++;
558
1.34k
    numSimilar++;
559
1.34k
  }
560
561
7.13k
  return res;
562
1.73M
}
563
564
void NisusWrtText::computePositions()
565
7.13k
{
566
  // first compute the number of page and the number of paragraph by pages
567
7.13k
  int nPages = 1;
568
7.13k
  MWAWInputStreamPtr input = m_mainParser->getInput();
569
7.13k
  input->seek(0, librevenge::RVNG_SEEK_SET);
570
7.13k
  int paragraph=0;
571
7.13k
  std::vector<int> firstParagraphInPage;
572
7.13k
  firstParagraphInPage.push_back(0);
573
5.02M
  while (!input->isEnd()) {
574
5.01M
    auto c = char(input->readULong(1));
575
5.01M
    if (c==0xd)
576
202k
      paragraph++;
577
4.81M
    else if (c==0xc) {
578
1.34k
      nPages++;
579
1.34k
      firstParagraphInPage.push_back(paragraph);
580
1.34k
    }
581
5.01M
  }
582
7.13k
  m_state->m_actualPage = 1;
583
7.13k
  m_state->m_numPages = nPages;
584
585
  // update the main page zone
586
7.13k
  m_state->m_zones[NisusWrtStruct::Z_Main].m_entry.setBegin(0);
587
7.13k
  m_state->m_zones[NisusWrtStruct::Z_Main].m_entry.setEnd(input->tell());
588
7.13k
  m_state->m_zones[NisusWrtStruct::Z_Main].m_entry.setId(NisusWrtStruct::Z_Main);
589
  // compute the header/footer pages
590
7.13k
  int actPage = 1;
591
7.13k
  MWAWVec2i headerId(-1, -1), footerId(-1, -1);
592
7.13k
  m_state->m_headersId.resize(size_t(nPages), -1);
593
7.13k
  m_state->m_footersId.resize(size_t(nPages), -1);
594
7.13k
  for (size_t i = 0; i < m_state->m_hfList.size(); i++) {
595
0
    auto &hf = m_state->m_hfList[i];
596
0
    int page = 1;
597
0
    long textPos = hf.m_textParagraph;
598
0
    if (hf.m_type == MWAWHeaderFooter::FOOTER && textPos) textPos--;
599
0
    for (size_t j = 0; j < firstParagraphInPage.size(); j++) {
600
0
      if (textPos < firstParagraphInPage[j])
601
0
        break;
602
0
      page = int(j)+1;
603
0
    }
604
    //std::cerr << "find : page=" << page << " for hf" << int(i) << "\n";
605
0
    for (int p = actPage;  p < page; p++) {
606
0
      m_state->m_headersId[size_t(p)-1] = (p%2) ? headerId[0] : headerId[1];
607
0
      m_state->m_footersId[size_t(p)-1] = (p%2) ? footerId[0] : footerId[1];
608
0
    }
609
0
    actPage = hf.m_page = page;
610
0
    MWAWVec2i &wh = hf.m_type == MWAWHeaderFooter::HEADER ? headerId : footerId;
611
0
    switch (hf.m_occurrence) {
612
0
    case MWAWHeaderFooter::ODD:
613
0
      wh[0] = int(i);
614
0
      break;
615
0
    case MWAWHeaderFooter::EVEN:
616
0
      wh[1] = int(i);
617
0
      break;
618
0
    case MWAWHeaderFooter::ALL:
619
0
      wh[0] = wh[1] = int(i);
620
0
      break;
621
0
    case MWAWHeaderFooter::NEVER:
622
0
      wh[0] = wh[1] = -1;
623
0
      break;
624
#if !defined(__clang__)
625
    default:
626
      break;
627
#endif
628
0
    }
629
0
  }
630
15.6k
  for (int p = actPage;  p <= nPages; p++) {
631
8.48k
    m_state->m_headersId[size_t(p)-1] = (p%2) ? headerId[0] : headerId[1];
632
8.48k
    m_state->m_footersId[size_t(p)-1] = (p%2) ? footerId[0] : footerId[1];
633
8.48k
  }
634
7.13k
}
635
636
////////////////////////////////////////////////////////////
637
// Intermediate level
638
////////////////////////////////////////////////////////////
639
640
// find the different zones
641
bool NisusWrtText::createZones()
642
7.13k
{
643
7.13k
  if (!m_mainParser->getRSRCParser()) {
644
0
    MWAW_DEBUG_MSG(("NisusWrtText::createZones: can not find the entry map\n"));
645
0
    return false;
646
0
  }
647
7.13k
  auto const &entryMap = m_mainParser->getRSRCParser()->getEntriesMap();
648
649
  // footnote and headerFooter main zone
650
7.13k
  auto it = entryMap.lower_bound("HF  ");
651
7.13k
  while (it != entryMap.end()) {
652
7.13k
    if (it->first != "HF  ")
653
7.13k
      break;
654
0
    MWAWEntry const &entry = it++->second;
655
0
    readHeaderFooter(entry);
656
0
  }
657
7.13k
  it = entryMap.lower_bound("FOOT");
658
7.13k
  while (it != entryMap.end()) {
659
7.13k
    if (it->first != "FOOT")
660
7.13k
      break;
661
0
    MWAWEntry const &entry = it++->second;
662
0
    readFootnotes(entry);
663
0
  }
664
665
  // fonts
666
7.13k
  it = entryMap.lower_bound("FLST");
667
18.5k
  while (it != entryMap.end()) {
668
18.5k
    if (it->first != "FLST")
669
7.13k
      break;
670
11.4k
    MWAWEntry const &entry = it++->second;
671
11.4k
    readFontsList(entry);
672
11.4k
  }
673
7.13k
  it = entryMap.lower_bound("STYL");
674
16.1k
  while (it != entryMap.end()) {
675
16.1k
    if (it->first != "STYL")
676
7.13k
      break;
677
9.00k
    MWAWEntry const &entry = it++->second;
678
9.00k
    readFonts(entry);
679
9.00k
  }
680
7.13k
  it = entryMap.lower_bound("FTAB");
681
14.0k
  while (it != entryMap.end()) {
682
14.0k
    if (it->first != "FTAB")
683
7.13k
      break;
684
6.90k
    MWAWEntry const &entry = it++->second;
685
6.90k
    readFonts(entry);
686
6.90k
  }
687
688
  // the font change
689
7.13k
  char const *posFontNames[] = { "FRMT", "FFRM", "HFRM" };
690
28.5k
  for (int z = 0; z < 3; z++) {
691
21.4k
    it = entryMap.lower_bound(posFontNames[z]);
692
28.4k
    while (it != entryMap.end()) {
693
28.4k
      if (it->first != posFontNames[z])
694
21.4k
        break;
695
7.03k
      MWAWEntry const &entry = it++->second;
696
7.03k
      readPosToFont(entry, NisusWrtStruct::ZoneType(z));
697
7.03k
    }
698
21.4k
  }
699
700
  // the paragraph: in mainZone 1004 means style paragraph
701
7.13k
  char const *paragNames[] = { "RULE", "FRUL", "HRUL" };
702
28.5k
  for (int z = 0; z < 3; z++) {
703
21.4k
    it = entryMap.lower_bound(paragNames[z]);
704
31.3k
    while (it != entryMap.end()) {
705
31.3k
      if (it->first != paragNames[z])
706
21.4k
        break;
707
9.89k
      MWAWEntry const &entry = it++->second;
708
9.89k
      readParagraphs(entry, NisusWrtStruct::ZoneType(z));
709
9.89k
    }
710
21.4k
  }
711
  // the picture associated to the paragraph
712
7.13k
  char const *pictDNames[] = { "PICD", "FPIC", "HPIC" };
713
28.5k
  for (int z = 0; z < 3; z++) {
714
21.4k
    it = entryMap.lower_bound(pictDNames[z]);
715
28.2k
    while (it != entryMap.end()) {
716
28.2k
      if (it->first != pictDNames[z])
717
21.4k
        break;
718
6.85k
      MWAWEntry const &entry = it++->second;
719
6.85k
      readPICD(entry, NisusWrtStruct::ZoneType(z));
720
6.85k
    }
721
21.4k
  }
722
723
  // End of the style zone
724
725
  // style name ( can also contains some flags... )
726
7.13k
  it = entryMap.lower_bound("STNM");
727
13.1k
  while (it != entryMap.end()) {
728
13.1k
    if (it->first != "STNM")
729
7.13k
      break;
730
5.99k
    MWAWEntry const &entry = it++->second;
731
5.99k
    std::vector<std::string> list;
732
5.99k
    m_mainParser->readStringsList(entry, list, false);
733
5.99k
  }
734
  // style link to paragraph name
735
7.13k
  it = entryMap.lower_bound("STRL");
736
14.0k
  while (it != entryMap.end()) {
737
14.0k
    if (it->first != "STRL")
738
7.13k
      break;
739
6.89k
    MWAWEntry const &entry = it++->second;
740
6.89k
    std::vector<std::string> list;
741
6.89k
    m_mainParser->readStringsList(entry, list, false);
742
6.89k
  }
743
  // style next name
744
7.13k
  it = entryMap.lower_bound("STNX");
745
18.0k
  while (it != entryMap.end()) {
746
18.0k
    if (it->first != "STNX")
747
7.13k
      break;
748
10.8k
    MWAWEntry const &entry = it++->second;
749
10.8k
    std::vector<std::string> list;
750
10.8k
    m_mainParser->readStringsList(entry, list, false);
751
10.8k
  }
752
753
  // the text zone
754
7.13k
  char const *textNames[] = { "", "FNTX", "HFTX" };
755
21.4k
  for (int z = 1; z < 3; z++) {
756
14.2k
    it = entryMap.lower_bound(textNames[z]);
757
14.2k
    while (it != entryMap.end()) {
758
14.2k
      if (it->first != textNames[z])
759
14.2k
        break;
760
0
      m_state->m_zones[z].m_entry = it++->second;
761
0
      m_state->m_zones[z].m_entry.setId(z);
762
0
    }
763
14.2k
  }
764
  // now update the different data
765
7.13k
  computePositions();
766
7.13k
  return true;
767
7.13k
}
768
769
////////////////////////////////////////////////////////////
770
//     Fonts
771
////////////////////////////////////////////////////////////
772
773
// read a list of fonts
774
bool NisusWrtText::readFontsList(MWAWEntry const &entry)
775
11.4k
{
776
11.4k
  if (!entry.valid() && entry.length()!=0) {
777
4.19k
    MWAW_DEBUG_MSG(("NisusWrtText::readFontsList: the entry is bad\n"));
778
4.19k
    return false;
779
4.19k
  }
780
7.25k
  entry.setParsed(true);
781
7.25k
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
782
7.25k
  if (!input || !input->checkPosition(entry.end())) {
783
0
    MWAW_DEBUG_MSG(("NisusWrtText::readFontsList: the zone is too short\n"));
784
0
    return false;
785
0
  }
786
7.25k
  libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
787
7.25k
  long pos = entry.begin();
788
7.25k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
789
790
7.25k
  libmwaw::DebugStream f;
791
7.25k
  f << "Entries(FontNames)[" << entry.id() << "]:";
792
7.25k
  asciiFile.addPos(pos-4);
793
7.25k
  asciiFile.addNote(f.str().c_str());
794
795
7.25k
  int num=0;
796
320k
  while (!input->isEnd()) {
797
320k
    pos = input->tell();
798
320k
    if (pos == entry.end()) break;
799
317k
    if (pos+4 > entry.end()) {
800
468
      asciiFile.addPos(pos);
801
468
      asciiFile.addNote("FontNames###");
802
803
468
      MWAW_DEBUG_MSG(("NisusWrtText::readFontsList: can not read flst\n"));
804
468
      return false;
805
468
    }
806
317k
    auto fId = static_cast<int>(input->readULong(2));
807
317k
    f.str("");
808
317k
    f << "FontNames" << num++ << ":fId=" << std::hex << fId << std::dec << ",";
809
317k
    auto pSz = static_cast<int>(input->readULong(1));
810
811
317k
    if (pSz+1+pos+2 > entry.end()) {
812
4.61k
      f << "###";
813
4.61k
      asciiFile.addPos(pos);
814
4.61k
      asciiFile.addNote(f.str().c_str());
815
816
4.61k
      MWAW_DEBUG_MSG(("NisusWrtText::readFontsList: can not read pSize\n"));
817
4.61k
      return false;
818
4.61k
    }
819
312k
    std::string name("");
820
2.32M
    for (int c=0; c < pSz; c++)
821
2.01M
      name += char(input->readULong(1));
822
312k
    m_parserState->m_fontConverter->setCorrespondance(fId, name);
823
312k
    f << name;
824
312k
    asciiFile.addPos(pos);
825
312k
    asciiFile.addNote(f.str().c_str());
826
312k
    if ((pSz%2)==0) input->seek(1,librevenge::RVNG_SEEK_CUR);
827
312k
  }
828
2.17k
  return true;
829
7.25k
}
830
831
// read the FTAB/STYL resource: a font format ?
832
bool NisusWrtText::readFonts(MWAWEntry const &entry)
833
15.9k
{
834
15.9k
  bool isStyle = entry.type()=="STYL";
835
15.9k
  int const fSize = isStyle ? 58 : 98;
836
15.9k
  std::string name(isStyle ? "Style" : "Fonts");
837
15.9k
  if ((!entry.valid()&&entry.length()) || (entry.length()%fSize)) {
838
9.78k
    MWAW_DEBUG_MSG(("NisusWrtText::readFonts: the entry is bad\n"));
839
9.78k
    return false;
840
9.78k
  }
841
6.12k
  entry.setParsed(true);
842
6.12k
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
843
6.12k
  libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
844
6.12k
  long pos = entry.begin();
845
6.12k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
846
847
6.12k
  auto numElt = int(entry.length()/fSize);
848
6.12k
  libmwaw::DebugStream f;
849
6.12k
  f << "Entries(" << name << ")[" << entry.id() << "]:N=" << numElt;
850
6.12k
  asciiFile.addPos(pos-4);
851
6.12k
  asciiFile.addNote(f.str().c_str());
852
853
6.12k
  long val;
854
123k
  for (int i = 0; i < numElt; i++) {
855
117k
    NisusWrtTextInternal::Font font;
856
117k
    pos = input->tell();
857
117k
    f.str("");
858
117k
    if (!isStyle)
859
111k
      font.m_pictureId = static_cast<int>(input->readLong(2));
860
861
117k
    if (font.m_pictureId) {
862
      // the two value seems to differ slightly for a picture
863
4.09k
      val = long(input->readULong(2));
864
4.09k
      if (val != 0xFF01) f << "#pictFlags0=" << std::hex << val << ",";
865
4.09k
      font.m_pictureWidth = static_cast<int>(input->readLong(2));
866
4.09k
    }
867
113k
    else {
868
113k
      val = long(input->readULong(2));
869
113k
      if (val != 0xFF00)
870
113k
        font.m_font.setId(int(val));
871
113k
      val = long(input->readULong(2));
872
113k
      if (val != 0xFF00)
873
113k
        font.m_font.setSize(float(val));
874
113k
    }
875
876
117k
    uint32_t flags=0;
877
117k
    auto flag = static_cast<int>(input->readULong(2));
878
879
117k
    if (flag&0x1) flags |= MWAWFont::boldBit;
880
117k
    if (flag&0x2) flags |= MWAWFont::italicBit;
881
117k
    if (flag&0x4) font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
882
117k
    if (flag&0x8) flags |= MWAWFont::embossBit;
883
117k
    if (flag&0x10) flags |= MWAWFont::shadowBit;
884
117k
    if (flag&0x20) font.m_font.setDeltaLetterSpacing(-1);
885
117k
    if (flag&0x40) font.m_font.setDeltaLetterSpacing(1);
886
117k
    if (flag &0xFF80)
887
1.41k
      f << "#flags0=" << std::hex << (flag &0xFF80) << std::dec << ",";
888
117k
    flag = static_cast<int>(input->readULong(2));
889
117k
    if (flag & 1) {
890
4.23k
      font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
891
4.23k
      f << "underline[lower],";
892
4.23k
    }
893
117k
    if (flag & 2)  font.m_font.setUnderlineStyle(MWAWFont::Line::Dot);
894
117k
    if (flag & 4)  font.m_font.setUnderlineWordFlag(true);
895
117k
    if (flag & 0x8) font.m_font.set(MWAWFont::Script::super());
896
117k
    if (flag & 0x10) font.m_font.set(MWAWFont::Script::sub());
897
117k
    if (flag & 0x20) font.m_font.setStrikeOutStyle(MWAWFont::Line::Simple);
898
117k
    if (flag & 0x40) font.m_font.setOverlineStyle(MWAWFont::Line::Simple);
899
117k
    if (flag & 0x80) flags |= MWAWFont::smallCapsBit;
900
117k
    if (flag & 0x100) flags |= MWAWFont::uppercaseBit;
901
117k
    if (flag & 0x200) flags |= MWAWFont::boxedBit;
902
117k
    if (flag & 0x400) flags |= MWAWFont::hiddenBit;
903
117k
    if (flag & 0x1000) font.m_font.set(MWAWFont::Script(40,librevenge::RVNG_PERCENT,58));
904
117k
    if (flag & 0x2000) font.m_font.set(MWAWFont::Script(-40,librevenge::RVNG_PERCENT,58));
905
117k
    if (flag & 0x4000) flags |= MWAWFont::reverseVideoBit;
906
117k
    if (flag & 0x8800)
907
285
      f << "#flags1=" << std::hex << (flag & 0x8800) << std::dec << ",";
908
117k
    val = input->readLong(2);
909
117k
    if (val) f << "#f0=" << std::hex << val << ",";
910
117k
    font.m_format = static_cast<int>(input->readULong(1));
911
117k
    if (font.m_format & 8) {
912
3.42k
      font.m_format &= 0xF7;
913
3.42k
      flags |= MWAWFont::reverseWritingBit;
914
3.42k
    }
915
117k
    font.m_format2 = static_cast<int>(input->readULong(1));
916
117k
    font.m_font.setFlags(flags);
917
918
117k
    int color = 0;
919
    // now data differs
920
117k
    if (isStyle) {
921
6.39k
      val = static_cast<int>(input->readULong(2)); // find [0-3] here
922
6.39k
      if (val) f << "unkn0=" << val << ",";
923
44.7k
      for (int j = 0; j < 6; j++) { // find s0=67, s1=a728
924
38.3k
        val = static_cast<int>(input->readULong(2));
925
38.3k
        if (val) f << "f" << j << "=" << std::hex << val << std::dec << ",";
926
38.3k
      }
927
6.39k
      color = static_cast<int>(input->readULong(2));
928
6.39k
    }
929
111k
    else {
930
111k
      color = static_cast<int>(input->readULong(2));
931
667k
      for (int j = 1; j < 6; j++) { // find always 0 here...
932
556k
        val = static_cast<int>(input->readULong(2));
933
556k
        if (val) f << "#f" << j << "=" << val << ",";
934
556k
      }
935
111k
      bool hasMark = false;
936
111k
      val = static_cast<int>(input->readULong(2));
937
111k
      if (val == 1) hasMark = true;
938
104k
      else if (val) f << "#hasMark=" << val << ",";
939
111k
      val = static_cast<int>(input->readULong(2));
940
111k
      if (hasMark) font.m_markId = int(val);
941
104k
      else if (val) f << "#markId=" << val << ",";
942
      // f0=0|1 and if f0=1 then f1 is a small number between 1 and 20
943
2.11M
      for (int j = 0; j < 18; j++) {
944
2.00M
        val = static_cast<int>(input->readULong(2));
945
2.00M
        if (val) f << "g" << j << "=" << val << ",";
946
2.00M
      }
947
111k
      float expand=float(input->readLong(4))/65536.f;
948
111k
      if (expand < 0 || expand > 0)
949
1.90k
        font.m_font.setDeltaLetterSpacing(expand);
950
      // 0, -1 or a small number related to the variable id : probably unknowncst+vId
951
111k
      font.m_variableId = static_cast<int>(input->readLong(4));
952
      // the remaining seems to be 0 excepted for picture
953
556k
      for (int j = 0; j < 4; j++) { // find 0,0,[0|d5|f5|f8|ba], big number
954
445k
        val = static_cast<int>(input->readULong(2));
955
445k
        if (val) f << "h" << j << "=" << std::hex << val << std::dec << ",";
956
445k
      }
957
      // two dim ?
958
222k
      for (auto &pictDim : font.m_pictureDim) {
959
222k
        int dim[4];
960
890k
        for (auto &d : dim) d = static_cast<int>(input->readLong(2));
961
222k
        pictDim=MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2]));
962
222k
      }
963
111k
    }
964
965
117k
    static const uint32_t colors[] =
966
117k
    { 0, 0xFF0000, 0x00FF00, 0x0000FF, 0x00FFFF, 0xFF00FF, 0xFFFF00, 0xFFFFFF };
967
117k
    if (color >= 0 && color < 8)
968
115k
      font.m_font.setColor(MWAWColor(colors[color]));
969
2.49k
    else if (color != 0xFF00)
970
2.47k
      f << "#color=" << color << ",";
971
117k
    font.m_extra = f.str();
972
117k
    if (!isStyle)
973
111k
      m_state->m_fontList.push_back(font);
974
975
117k
    f.str("");
976
117k
    f << name << i << ":" << font.m_font.getDebugString(m_parserState->m_fontConverter)
977
117k
      << font;
978
117k
    if (input->tell() != pos+fSize)
979
6.39k
      asciiFile.addDelimiter(input->tell(),'|');
980
117k
    asciiFile.addPos(pos);
981
117k
    asciiFile.addNote(f.str().c_str());
982
117k
    input->seek(pos+fSize, librevenge::RVNG_SEEK_SET);
983
117k
  }
984
6.12k
  return true;
985
15.9k
}
986
987
// read the FRMT resource: filepos -> fonts
988
bool NisusWrtText::readPosToFont(MWAWEntry const &entry, NisusWrtStruct::ZoneType zoneId)
989
7.03k
{
990
7.03k
  if (!entry.valid() || (entry.length()%10)) {
991
3.18k
    MWAW_DEBUG_MSG(("NisusWrtText::readPosToFont: the entry is bad\n"));
992
3.18k
    return false;
993
3.18k
  }
994
3.84k
  if (zoneId >= 3) {
995
0
    MWAW_DEBUG_MSG(("NisusWrtText::readPosToFont: find unexpected zoneId: %d\n", zoneId));
996
0
    return false;
997
0
  }
998
3.84k
  NisusWrtTextInternal::Zone &zone = m_state->m_zones[zoneId];
999
3.84k
  entry.setParsed(true);
1000
3.84k
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
1001
3.84k
  libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
1002
3.84k
  long pos = entry.begin();
1003
3.84k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1004
1005
3.84k
  auto numElt = int(entry.length()/10);
1006
3.84k
  libmwaw::DebugStream f;
1007
3.84k
  f << "Entries(PosToFont)[" << zoneId << "]:N=" << numElt;
1008
3.84k
  asciiFile.addPos(pos-4);
1009
3.84k
  asciiFile.addNote(f.str().c_str());
1010
1011
3.84k
  NisusWrtStruct::Position position;
1012
3.84k
  NisusWrtTextInternal::DataPLC plc;
1013
3.84k
  plc.m_type = NisusWrtTextInternal::P_Format;
1014
253k
  for (int i = 0; i < numElt; i++) {
1015
249k
    pos = input->tell();
1016
249k
    f.str("");
1017
249k
    f << "PosToFont" << i << "[" << zoneId << "]:";
1018
249k
    position.m_paragraph = static_cast<int>(input->readULong(4)); // checkme or ??? m_paragraph
1019
249k
    position.m_word = static_cast<int>(input->readULong(2));
1020
249k
    position.m_char = static_cast<int>(input->readULong(2));
1021
249k
    f << "pos=" << position << ",";
1022
249k
    auto id = static_cast<int>(input->readLong(2));
1023
249k
    f << "F" << id << ",";
1024
249k
    plc.m_id = id;
1025
249k
    zone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(position, plc));
1026
249k
    asciiFile.addPos(pos);
1027
249k
    asciiFile.addNote(f.str().c_str());
1028
249k
    input->seek(pos+10, librevenge::RVNG_SEEK_SET);
1029
249k
  }
1030
3.84k
  return true;
1031
3.84k
}
1032
1033
////////////////////////////////////////////////////////////
1034
// the paragraphs
1035
////////////////////////////////////////////////////////////
1036
1037
// send the paragraph to the listener
1038
void NisusWrtText::setProperty(NisusWrtTextInternal::Paragraph const &para, int width)
1039
55.0k
{
1040
55.0k
  if (!m_parserState->m_textListener) return;
1041
55.0k
  double origRMargin = para.m_margins[2].get();
1042
55.0k
  double rMargin=double(width)/72.-origRMargin;
1043
55.0k
  if (rMargin < 0.0) rMargin = 0;
1044
55.0k
  const_cast<NisusWrtTextInternal::Paragraph &>(para).m_margins[2] = rMargin;
1045
55.0k
  m_parserState->m_textListener->setParagraph(para);
1046
55.0k
  const_cast<NisusWrtTextInternal::Paragraph &>(para).m_margins[2] = origRMargin;
1047
55.0k
}
1048
1049
// read the RULE resource: a list of rulers
1050
bool NisusWrtText::readParagraphs(MWAWEntry const &entry, NisusWrtStruct::ZoneType zoneId)
1051
9.89k
{
1052
9.89k
  if (!entry.valid() && entry.length() != 0) {
1053
2.52k
    MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: the entry is bad\n"));
1054
2.52k
    return false;
1055
2.52k
  }
1056
7.37k
  if (zoneId >= 3) {
1057
0
    MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: find unexpected zoneId: %d\n", zoneId));
1058
0
    return false;
1059
0
  }
1060
7.37k
  auto &zone = m_state->m_zones[zoneId];
1061
1062
7.37k
  entry.setParsed(true);
1063
7.37k
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
1064
7.37k
  libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
1065
7.37k
  long pos = entry.begin();
1066
7.37k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1067
1068
7.37k
  auto numElt = int(entry.length()/98);
1069
7.37k
  libmwaw::DebugStream f, f2;
1070
7.37k
  f << "Entries(RULE)[" << entry.type() << entry.id() << "]";
1071
7.37k
  if (entry.id()==1004) f << "[Styl]";
1072
7.36k
  else if (entry.id() != 1003) {
1073
1.36k
    MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: find unexpected entryId: %d\n", entry.id()));
1074
1.36k
    f << "###";
1075
1.36k
  }
1076
7.37k
  f << ":N=" << numElt;
1077
7.37k
  asciiFile.addPos(pos-4);
1078
7.37k
  asciiFile.addNote(f.str().c_str());
1079
1080
7.37k
  NisusWrtTextInternal::DataPLC plc;
1081
7.37k
  plc.m_type = NisusWrtTextInternal::P_Ruler;
1082
1083
7.37k
  long val;
1084
67.7k
  while (input->tell() != entry.end()) {
1085
66.9k
    int num = (entry.id() == 1003) ? static_cast<int>(zone.m_paragraphList.size()) : -1;
1086
66.9k
    pos = input->tell();
1087
66.9k
    f.str("");
1088
66.9k
    if (pos+8 > entry.end() || input->isEnd()) {
1089
195
      f << "RULE" << num << "[" << zoneId << "]:###";
1090
195
      asciiFile.addPos(pos);
1091
195
      asciiFile.addNote(f.str().c_str());
1092
1093
195
      MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: can not read end of zone\n"));
1094
195
      return false;
1095
195
    }
1096
1097
66.7k
    auto nPara = long(input->readULong(4));
1098
66.7k
    if (nPara == 0x7FFFFFFF) {
1099
2.56k
      input->seek(-4, librevenge::RVNG_SEEK_CUR);
1100
2.56k
      break;
1101
2.56k
    }
1102
64.1k
    NisusWrtStruct::Position textPosition;
1103
64.1k
    textPosition.m_paragraph = static_cast<int>(nPara);  // checkme or ???? + para
1104
1105
64.1k
    auto sz = long(input->readULong(4));
1106
64.1k
    if (sz < 0x42 || pos+sz > entry.end()) {
1107
3.78k
      f << "RULE" << num << "[" << zoneId << "]:###";
1108
3.78k
      asciiFile.addPos(pos);
1109
3.78k
      asciiFile.addNote(f.str().c_str());
1110
1111
3.78k
      MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: can not read the size zone\n"));
1112
3.78k
      return false;
1113
3.78k
    }
1114
60.3k
    NisusWrtTextInternal::Paragraph para;
1115
1116
60.3k
    para.setInterline(double(input->readLong(4))/65536., librevenge::RVNG_POINT, MWAWParagraph::AtLeast);
1117
60.3k
    para.m_spacings[1] = double(input->readLong(4))/65536./72.;
1118
60.3k
    auto wh = int(input->readLong(2));
1119
60.3k
    switch (wh) {
1120
51.0k
    case 0:
1121
51.0k
      break; // left
1122
2.82k
    case 1:
1123
2.82k
      para.m_justify = MWAWParagraph::JustificationCenter;
1124
2.82k
      break;
1125
3.47k
    case 2:
1126
3.47k
      para.m_justify = MWAWParagraph::JustificationRight;
1127
3.47k
      break;
1128
2.78k
    case 3:
1129
2.78k
      para.m_justify = MWAWParagraph::JustificationFull;
1130
2.78k
      break;
1131
271
    default:
1132
271
      f << "#align=" << wh << ",";
1133
271
      break;
1134
60.3k
    }
1135
60.3k
    val = input->readLong(2);
1136
60.3k
    if (val) f << "#f0=" << val << ",";
1137
1138
60.3k
    para.m_marginsUnit = librevenge::RVNG_INCH;
1139
60.3k
    para.m_margins[0] = double(input->readLong(4))/65536./72.;
1140
60.3k
    para.m_margins[1] = double(input->readLong(4))/65536./72.;
1141
60.3k
    para.m_margins[2] = double(input->readLong(4))/65536./72.;
1142
60.3k
    para.m_margins[0] =para.m_margins[0].get()-para.m_margins[1].get();
1143
60.3k
    wh = int(input->readULong(1));
1144
60.3k
    switch (wh) {
1145
51.6k
    case 0: // at least
1146
51.6k
      break;
1147
2.67k
    case 1:
1148
2.67k
      para.m_spacingsInterlineType = MWAWParagraph::Fixed;
1149
2.67k
      break;
1150
5.09k
    case 2:
1151
5.09k
      para.m_spacingsInterlineUnit = librevenge::RVNG_PERCENT;
1152
5.09k
      para.m_spacingsInterlineType = MWAWParagraph::Fixed;
1153
      // before spacing is also in %, try to correct it
1154
5.09k
      para.m_spacings[1] = para.m_spacings[1].get()*12.0;
1155
5.09k
      break;
1156
1.00k
    default: // unknown, so...
1157
1.00k
      f << "#interline=" << (val&0xFC) << ",";
1158
1.00k
      para.setInterline(1.0, librevenge::RVNG_PERCENT);
1159
1.00k
      break;
1160
60.3k
    }
1161
60.3k
    val = input->readLong(1);
1162
60.3k
    if (val) f << "#f1=" << val << ",";
1163
905k
    for (int i = 0; i < 14; i++) {
1164
845k
      val = input->readLong(2);
1165
845k
      if (val) f << "g" << i << "=" << val << ",";
1166
845k
    }
1167
60.3k
    input->seek(pos+0x3E, librevenge::RVNG_SEEK_SET);
1168
60.3k
    long numTabs = input->readLong(2);
1169
60.3k
    bool ok = true;
1170
60.3k
    if (0x40+8*numTabs+2 > sz) {
1171
901
      f << "###";
1172
901
      MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: can not read the string\n"));
1173
901
      ok = false;
1174
901
      numTabs = 0;
1175
901
    }
1176
115k
    for (long i = 0; i < numTabs; i++) {
1177
54.8k
      long tabPos = input->tell();
1178
54.8k
      MWAWTabStop tab;
1179
1180
54.8k
      f2.str("");
1181
54.8k
      tab.m_position = double(input->readLong(4))/72./65536.; // from left pos
1182
54.8k
      val = long(input->readULong(1));
1183
54.8k
      switch (val) {
1184
24.7k
      case 1:
1185
24.7k
        break;
1186
7.36k
      case 2:
1187
7.36k
        tab.m_alignment = MWAWTabStop::CENTER;
1188
7.36k
        break;
1189
7.34k
      case 3:
1190
7.34k
        tab.m_alignment = MWAWTabStop::RIGHT;
1191
7.34k
        break;
1192
7.29k
      case 4:
1193
7.29k
        tab.m_alignment = MWAWTabStop::DECIMAL;
1194
7.29k
        break;
1195
7.25k
      case 6: // a little old, look simillar to full justification
1196
7.25k
        f2 << "justify,";
1197
7.25k
        break;
1198
820
      default:
1199
820
        f2 << "#type=" << val << ",";
1200
820
        break;
1201
54.8k
      }
1202
54.8k
      auto leader=static_cast<unsigned char>(input->readULong(1));
1203
54.8k
      if (leader) {
1204
15.1k
        int unicode= m_parserState->m_fontConverter->unicode(3, leader);
1205
15.1k
        if (unicode==-1)
1206
324
          tab.m_leaderCharacter =static_cast<unsigned short>(leader);
1207
14.8k
        else
1208
14.8k
          tab.m_leaderCharacter =static_cast<unsigned short>(unicode);
1209
15.1k
      }
1210
54.8k
      val = long(input->readLong(2)); // unused ?
1211
54.8k
      if (val) f2 << "#unkn0=" << val << ",";
1212
54.8k
      para.m_tabs->push_back(tab);
1213
54.8k
      if (f2.str().length())
1214
0
        f << "tab" << i << "=[" << f2.str() << "],";
1215
54.8k
      input->seek(tabPos+8, librevenge::RVNG_SEEK_SET);
1216
54.8k
    }
1217
1218
    // ruler name
1219
60.3k
    long pSz = ok ? long(input->readULong(1)) : 0;
1220
60.3k
    if (pSz) {
1221
379
      if (input->tell()+pSz != pos+sz && input->tell()+pSz+1 != pos+sz) {
1222
361
        f << "name###";
1223
361
        MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: can not read the ruler name\n"));
1224
361
        asciiFile.addDelimiter(input->tell()-1,'#');
1225
361
      }
1226
18
      else {
1227
18
        std::string str("");
1228
223
        for (long i = 0; i < pSz; i++)
1229
205
          str += char(input->readULong(1));
1230
18
        para.m_name = str;
1231
18
      }
1232
379
    }
1233
60.3k
    plc.m_id = num;
1234
60.3k
    para.m_extra=f.str();
1235
60.3k
    if (entry.id() == 1003) {
1236
59.1k
      zone.m_paragraphList.push_back(para);
1237
59.1k
      zone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(textPosition, plc));
1238
59.1k
    }
1239
1240
60.3k
    f.str("");
1241
60.3k
    f << "RULE" << num << "[" << zoneId << "]:";
1242
60.3k
    f << "paragraph=" << nPara << "," << para;
1243
1244
60.3k
    asciiFile.addPos(pos);
1245
60.3k
    asciiFile.addNote(f.str().c_str());
1246
60.3k
    input->seek(pos+sz, librevenge::RVNG_SEEK_SET);
1247
60.3k
  }
1248
3.38k
  pos = input->tell();
1249
3.38k
  f.str("");
1250
3.38k
  f << "RULE[" << zoneId << "](II):";
1251
3.38k
  if (pos+66 != entry.end() || input->readULong(4) != 0x7FFFFFFF) {
1252
819
    f << "###";
1253
819
    asciiFile.addPos(pos);
1254
819
    asciiFile.addNote(f.str().c_str());
1255
1256
819
    MWAW_DEBUG_MSG(("NisusWrtText::readParagraphs: find odd end\n"));
1257
819
    return true;
1258
819
  }
1259
82.1k
  for (int i = 0; i < 31; i++) { // only find 0 expected f12=0|100
1260
79.6k
    val = long(input->readLong(2));
1261
79.6k
    if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1262
79.6k
  }
1263
2.56k
  asciiFile.addPos(pos);
1264
2.56k
  asciiFile.addNote(f.str().c_str());
1265
1266
2.56k
  return true;
1267
3.38k
}
1268
1269
////////////////////////////////////////////////////////////
1270
//     the zones header
1271
////////////////////////////////////////////////////////////
1272
1273
// read the header/footer main entry
1274
bool NisusWrtText::readHeaderFooter(MWAWEntry const &entry)
1275
0
{
1276
0
  if (!entry.valid() || (entry.length()%32)) {
1277
0
    MWAW_DEBUG_MSG(("NisusWrtText::readHeaderFooter: the entry is bad\n"));
1278
0
    return false;
1279
0
  }
1280
0
  auto &zone = m_state->m_zones[NisusWrtStruct::Z_HeaderFooter];
1281
0
  entry.setParsed(true);
1282
0
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
1283
0
  libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
1284
0
  long pos = entry.begin();
1285
0
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1286
1287
0
  auto numElt = int(entry.length()/32);
1288
0
  libmwaw::DebugStream f;
1289
0
  f << "Entries(HeaderFooter)[" << entry.id() << "]:N=" << numElt;
1290
0
  asciiFile.addPos(pos-4);
1291
0
  asciiFile.addNote(f.str().c_str());
1292
1293
0
  NisusWrtTextInternal::DataPLC plc;
1294
0
  plc.m_type = NisusWrtTextInternal::P_HeaderFooter;
1295
0
  long val;
1296
0
  long lastPara = 0;
1297
0
  for (int i = 0; i < numElt; i++) {
1298
0
    pos = input->tell();
1299
0
    f.str("");
1300
0
    NisusWrtTextInternal::HeaderFooter hf;
1301
0
    hf.m_textParagraph = input->readLong(4);
1302
0
    hf.m_paragraph[0] = lastPara;
1303
0
    hf.m_paragraph[1] = lastPara = input->readLong(4);
1304
0
    auto what = static_cast<int>(input->readULong(2));
1305
0
    switch ((what>>2)&0x3) {
1306
0
    case 1:
1307
0
      hf.m_type = MWAWHeaderFooter::HEADER;
1308
0
      break;
1309
0
    case 2:
1310
0
      hf.m_type = MWAWHeaderFooter::FOOTER;
1311
0
      break;
1312
0
    default:
1313
0
      f << "#what=" << ((what>>2)&0x3);
1314
0
      break;
1315
0
    }
1316
0
    switch (what&0x3) {
1317
0
    case 1:
1318
0
      hf.m_occurrence = MWAWHeaderFooter::ODD;
1319
0
      break;
1320
0
    case 2:
1321
0
      hf.m_occurrence = MWAWHeaderFooter::EVEN;
1322
0
      break;
1323
0
    case 3:
1324
0
      hf.m_occurrence = MWAWHeaderFooter::ALL;
1325
0
      break;
1326
0
    default:
1327
0
      f << "[#page],";
1328
0
      break;
1329
0
    }
1330
0
    if (what&0xFFF0) f << "#flags=" << std::hex << (what&0xFFF0) << ",";
1331
    // find 0|0x10|0x18|0x20|0x36|0x3a|0x40|0x4c|0x66 here
1332
0
    hf.m_unknown = static_cast<int>(input->readLong(2));
1333
0
    for (int j = 0; j < 10; j++) { // always 0 ?
1334
0
      val = long(input->readLong(2));
1335
0
      if (val) f << "g" << j << "=" << val << ",";
1336
0
    }
1337
0
    hf.m_extra = f.str();
1338
0
    f.str("");
1339
0
    f << "HeaderFooter" << i << ":" << hf;
1340
1341
0
    m_state->m_hfList.push_back(hf);
1342
0
    plc.m_id = i+1;
1343
0
    NisusWrtStruct::Position hfPosition;
1344
0
    hfPosition.m_paragraph=int(lastPara);
1345
0
    zone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(hfPosition, plc));
1346
0
    asciiFile.addPos(pos);
1347
0
    asciiFile.addNote(f.str().c_str());
1348
0
    input->seek(pos+32, librevenge::RVNG_SEEK_SET);
1349
0
  }
1350
0
  return true;
1351
0
}
1352
1353
// read the footnote main entry
1354
bool NisusWrtText::readFootnotes(MWAWEntry const &entry)
1355
0
{
1356
0
  if (!entry.valid() || (entry.length()%36)) {
1357
0
    MWAW_DEBUG_MSG(("NisusWrtText::readFootnotes: the entry is bad\n"));
1358
0
    return false;
1359
0
  }
1360
0
  auto &mainZone = m_state->m_zones[NisusWrtStruct::Z_Main];
1361
0
  auto &zone = m_state->m_zones[NisusWrtStruct::Z_Footnote];
1362
0
  entry.setParsed(true);
1363
0
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
1364
0
  libmwaw::DebugFile &asciiFile = m_mainParser->rsrcAscii();
1365
0
  long pos = entry.begin();
1366
0
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1367
1368
0
  auto numElt = int(entry.length()/36);
1369
0
  libmwaw::DebugStream f;
1370
0
  f << "Entries(Footnotes)[" << entry.id() << "]:N=" << numElt;
1371
0
  asciiFile.addPos(pos-4);
1372
0
  asciiFile.addNote(f.str().c_str());
1373
1374
0
  NisusWrtTextInternal::DataPLC plc;
1375
0
  plc.m_type = NisusWrtTextInternal::P_Footnote;
1376
0
  int lastParagraph = 0;
1377
0
  long val;
1378
0
  for (int i = 0; i < numElt; i++) {
1379
0
    pos = input->tell();
1380
0
    f.str("");
1381
0
    NisusWrtTextInternal::Footnote footnote;
1382
0
    footnote.m_textPosition.m_paragraph = static_cast<int>(input->readULong(4)); // checkme or ??? m_paragraph
1383
0
    footnote.m_textPosition.m_word = static_cast<int>(input->readULong(2));
1384
0
    footnote.m_textPosition.m_char = static_cast<int>(input->readULong(2));
1385
0
    footnote.m_paragraph[0] = lastParagraph;
1386
0
    lastParagraph = static_cast<int>(input->readULong(4)); // checkme or ??? m_paragraph
1387
0
    footnote.m_paragraph[1] = lastParagraph;
1388
    // find f0=0|55|6f, f1=15|16|26|27|39|3a
1389
0
    for (int j = 0; j < 2; j++) {
1390
0
      val = input->readLong(2);
1391
0
      if (val) f << "f" << j << "=" << std::hex << val << std::dec << ",";
1392
0
    }
1393
0
    footnote.m_number = static_cast<int>(input->readLong(2));
1394
0
    for (int j = 0; j < 3; j++) { // always 0 ?
1395
0
      val = input->readLong(2);
1396
0
      if (val) f << "g" << j << "=" << val << ",";
1397
0
    }
1398
0
    for (int wh = 0; wh < 2; wh++) {
1399
0
      input->seek(pos+24+wh*6, librevenge::RVNG_SEEK_SET);
1400
0
      std::string label("");
1401
0
      for (int c = 0; c < 6; c++) {
1402
0
        auto ch = char(input->readULong(1));
1403
0
        if (ch == 0)
1404
0
          break;
1405
0
        label += ch;
1406
0
      }
1407
0
      if (wh==0) footnote.m_noteLabel = label;
1408
0
      else footnote.m_textLabel = label;
1409
0
    }
1410
0
    footnote.m_extra = f.str();
1411
0
    f.str("");
1412
0
    f << "Footnotes" << i << ":" << footnote;
1413
1414
0
    m_state->m_footnoteList.push_back(footnote);
1415
0
    plc.m_id = i;
1416
0
    mainZone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(footnote.m_textPosition, plc));
1417
0
    NisusWrtStruct::Position notePosition;
1418
0
    notePosition.m_paragraph= footnote.m_paragraph[0];
1419
0
    zone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(notePosition, plc));
1420
1421
0
    asciiFile.addPos(pos);
1422
0
    asciiFile.addNote(f.str().c_str());
1423
0
    input->seek(pos+36, librevenge::RVNG_SEEK_SET);
1424
0
  }
1425
0
  return true;
1426
0
}
1427
1428
// read the PICD zone ( a list of picture ? )
1429
bool NisusWrtText::readPICD(MWAWEntry const &entry, NisusWrtStruct::ZoneType zoneId)
1430
6.85k
{
1431
6.85k
  if ((!entry.valid()&&entry.length()) || (entry.length()%14)) {
1432
2.98k
    MWAW_DEBUG_MSG(("NisusWrtText::readPICD: the entry is bad\n"));
1433
2.98k
    return false;
1434
2.98k
  }
1435
3.87k
  if (zoneId >= 3) {
1436
0
    MWAW_DEBUG_MSG(("NisusWrtText::readPICD: find unexpected zoneId: %d\n", zoneId));
1437
0
    return false;
1438
0
  }
1439
3.87k
  auto &zone = m_state->m_zones[zoneId];
1440
3.87k
  entry.setParsed(true);
1441
3.87k
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
1442
3.87k
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
1443
3.87k
  long pos = entry.begin();
1444
3.87k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1445
1446
3.87k
  auto numElt = int(entry.length()/14);
1447
3.87k
  libmwaw::DebugStream f;
1448
3.87k
  f << "Entries(PICD)[" << zoneId << "]:N=" << numElt;
1449
3.87k
  ascFile.addPos(pos-4);
1450
3.87k
  ascFile.addNote(f.str().c_str());
1451
1452
3.87k
  NisusWrtTextInternal::DataPLC plc;
1453
3.87k
  plc.m_type = NisusWrtTextInternal::P_PicturePara;
1454
157k
  for (int i = 0; i < numElt; i++) {
1455
153k
    pos = input->tell();
1456
153k
    f.str("");
1457
153k
    NisusWrtTextInternal::PicturePara pict;
1458
153k
    pict.m_paragraph = static_cast<int>(input->readLong(4));
1459
153k
    int dim[4];
1460
153k
    for (int &j : dim)
1461
615k
      j = static_cast<int>(input->readLong(2));
1462
153k
    pict.m_position = MWAWBox2i(MWAWVec2i(dim[1],dim[0]), MWAWVec2i(dim[3],dim[2]));
1463
153k
    pict.m_id = static_cast<int>(input->readULong(2));
1464
153k
    zone.m_pictureParaList.push_back(pict);
1465
1466
153k
    NisusWrtStruct::Position pictPosition;
1467
153k
    pictPosition.m_paragraph= pict.m_paragraph;
1468
153k
    plc.m_id = i;
1469
153k
    zone.m_plcMap.insert(NisusWrtTextInternal::Zone::PLCMap::value_type(pictPosition, plc));
1470
1471
153k
    f << "PICD" << i << ":" << pict;
1472
153k
    ascFile.addPos(pos);
1473
153k
    ascFile.addNote(f.str().c_str());
1474
153k
    input->seek(pos+14, librevenge::RVNG_SEEK_SET);
1475
153k
  }
1476
3.87k
  return true;
1477
3.87k
}
1478
1479
////////////////////////////////////////////////////////////
1480
//
1481
// Low level
1482
//
1483
////////////////////////////////////////////////////////////
1484
1485
////////////////////////////////////////////////////////////
1486
long NisusWrtText::findFilePos(NisusWrtStruct::ZoneType zoneId, NisusWrtStruct::Position const &pos)
1487
0
{
1488
0
  if (zoneId >= 3) {
1489
0
    MWAW_DEBUG_MSG(("NisusWrtText::findFilePos: find unexpected zoneId: %d\n", zoneId));
1490
0
    return -1;
1491
0
  }
1492
0
  auto &zone = m_state->m_zones[zoneId];
1493
0
  MWAWEntry entry = zone.m_entry;
1494
0
  if (!entry.valid()) {
1495
0
    MWAW_DEBUG_MSG(("NisusWrtText::findFilePos: the entry is bad\n"));
1496
0
    return -1;
1497
0
  }
1498
1499
0
  bool isMain = zoneId == NisusWrtStruct::Z_Main;
1500
0
  MWAWInputStreamPtr input = isMain ?
1501
0
                             m_mainParser->getInput() : m_mainParser->rsrcInput();
1502
0
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1503
1504
0
  NisusWrtStruct::Position actPos;
1505
0
  for (long i = 0; i < entry.length(); i++) {
1506
0
    if (input->isEnd())
1507
0
      break;
1508
0
    if (pos == actPos)
1509
0
      return input->tell();
1510
0
    auto c = static_cast<unsigned char>(input->readULong(1));
1511
    // update the position
1512
0
    switch (c) {
1513
0
    case 0xd:
1514
0
      actPos.m_paragraph++;
1515
0
      actPos.m_word = actPos.m_char = 0;
1516
0
      break;
1517
0
    case '\t':
1518
0
    case ' ':
1519
0
      actPos.m_word++;
1520
0
      actPos.m_char = 0;
1521
0
      break;
1522
0
    default:
1523
0
      actPos.m_char++;
1524
0
      break;
1525
0
    }
1526
0
  }
1527
0
  if (pos == actPos)
1528
0
    return input->tell();
1529
0
  MWAW_DEBUG_MSG(("NisusWrtText::findFilePos: can not find the position\n"));
1530
0
  return -1;
1531
0
}
1532
1533
////////////////////////////////////////////////////////////
1534
bool NisusWrtText::sendHeaderFooter(int hfId)
1535
0
{
1536
0
  if (!m_parserState->m_textListener) {
1537
0
    MWAW_DEBUG_MSG(("NisusWrtText::sendHeaderFooter: can not find the listener\n"));
1538
0
    return false;
1539
0
  }
1540
0
  if (hfId >= int(m_state->m_hfList.size())) {
1541
0
    MWAW_DEBUG_MSG(("NisusWrtText::sendHeaderFooter: can not find the headerFooter list\n"));
1542
0
    return false;
1543
0
  }
1544
0
  if (hfId < 0)
1545
0
    return true;
1546
0
  auto const &hf = m_state->m_hfList[size_t(hfId)];
1547
0
  hf.m_parsed = true;
1548
1549
0
  MWAWEntry entry;
1550
0
  entry.setId(NisusWrtStruct::Z_HeaderFooter);
1551
0
  NisusWrtStruct::Position pos;
1552
0
  pos.m_paragraph = static_cast<int>(hf.m_paragraph[0]);
1553
0
  entry.setBegin(findFilePos(NisusWrtStruct::Z_HeaderFooter, pos));
1554
0
  pos.m_paragraph = static_cast<int>(hf.m_paragraph[1]);
1555
0
  entry.setEnd(findFilePos(NisusWrtStruct::Z_HeaderFooter, pos));
1556
0
  if (entry.begin() < 0 || entry.length() < 0) {
1557
0
    MWAW_DEBUG_MSG(("NisusWrtText::sendHeaderFooter: can not compute the headerFooter entry\n"));
1558
0
    return false;
1559
0
  }
1560
0
  pos.m_paragraph = static_cast<int>(hf.m_paragraph[0]);
1561
0
  sendText(entry, pos);
1562
0
  return true;
1563
0
}
1564
1565
////////////////////////////////////////////////////////////
1566
bool NisusWrtText::sendFootnote(int footnoteId)
1567
0
{
1568
0
  if (!m_parserState->m_textListener) {
1569
0
    MWAW_DEBUG_MSG(("NisusWrtText::sendFootnote: can not find the listener\n"));
1570
0
    return false;
1571
0
  }
1572
0
  if (footnoteId >= int(m_state->m_footnoteList.size())) {
1573
0
    MWAW_DEBUG_MSG(("NisusWrtText::sendFootnote: can not find the footnote list\n"));
1574
0
    return false;
1575
0
  }
1576
0
  if (footnoteId < 0)
1577
0
    return true;
1578
0
  auto const &fnote = m_state->m_footnoteList[size_t(footnoteId)];
1579
0
  fnote.m_parsed = true;
1580
1581
0
  MWAWEntry entry;
1582
0
  entry.setId(NisusWrtStruct::Z_Footnote);
1583
0
  NisusWrtStruct::Position pos;
1584
0
  pos.m_paragraph = fnote.m_paragraph[0];
1585
0
  entry.setBegin(findFilePos(NisusWrtStruct::Z_Footnote, pos));
1586
0
  pos.m_paragraph = fnote.m_paragraph[1];
1587
0
  entry.setEnd(findFilePos(NisusWrtStruct::Z_Footnote, pos));
1588
0
  if (entry.begin() < 0 || entry.length() < 0) {
1589
0
    MWAW_DEBUG_MSG(("NisusWrtText::sendFootnote: can not compute the footnote entry\n"));
1590
0
    return false;
1591
0
  }
1592
0
  pos.m_paragraph = fnote.m_paragraph[0];
1593
0
  sendText(entry, pos);
1594
0
  return true;
1595
0
}
1596
1597
////////////////////////////////////////////////////////////
1598
bool NisusWrtText::sendText(MWAWEntry const &entry, NisusWrtStruct::Position firstPos)
1599
7.13k
{
1600
7.13k
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
1601
7.13k
  if (!listener) {
1602
0
    MWAW_DEBUG_MSG(("NisusWrtText::sendText: can not find the listener\n"));
1603
0
    return false;
1604
0
  }
1605
7.13k
  if (!entry.valid()) {
1606
0
    MWAW_DEBUG_MSG(("NisusWrtText::sendText: the entry is bad\n"));
1607
0
    return false;
1608
0
  }
1609
1610
7.13k
  auto zoneId = static_cast<NisusWrtStruct::ZoneType>(entry.id());
1611
7.13k
  if (zoneId >= 3) {
1612
0
    MWAW_DEBUG_MSG(("NisusWrtText::sendText: find unexpected zoneId: %d\n", zoneId));
1613
0
    return false;
1614
0
  }
1615
7.13k
  auto &zone = m_state->m_zones[zoneId];
1616
7.13k
  bool isMain = zoneId == NisusWrtStruct::Z_Main;
1617
7.13k
  auto width = int(72.0*m_mainParser->getPageWidth());
1618
7.13k
  if (isMain || zoneId == NisusWrtStruct::Z_Footnote) {
1619
7.13k
    float colSep = 0.5f;
1620
7.13k
    int nCol = 1;
1621
7.13k
    m_mainParser->getColumnInfo(nCol, colSep);
1622
7.13k
    if (nCol > 1)
1623
14
      width /= nCol;
1624
7.13k
    if (isMain && nCol > 1) {
1625
14
      if (listener->isSectionOpened())
1626
0
        listener->closeSection();
1627
1628
14
      MWAWSection sec;
1629
14
      sec.setColumns(nCol, double(width), librevenge::RVNG_POINT);
1630
14
      listener->openSection(sec);
1631
14
    }
1632
7.13k
  }
1633
7.13k
  MWAWInputStreamPtr input
1634
7.13k
    = isMain ? m_mainParser->getInput() : m_mainParser->rsrcInput();
1635
7.13k
  libmwaw::DebugFile &ascFile =
1636
7.13k
    isMain ? m_mainParser->ascii() : m_mainParser->rsrcAscii();
1637
7.13k
  long pos = entry.begin();
1638
7.13k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1639
1640
7.13k
  libmwaw::DebugStream f;
1641
7.13k
  f << "Entries(TEXT)[" << zoneId << "]:";
1642
7.13k
  std::string str("");
1643
7.13k
  NisusWrtStruct::Position actPos(firstPos);
1644
7.13k
  auto it = zone.m_plcMap.begin();
1645
26.8k
  while (it != zone.m_plcMap.end() && it->first.cmp(actPos) < 0)
1646
19.7k
    ++it;
1647
1648
7.13k
  NisusWrtTextInternal::Font actFont;
1649
7.13k
  actFont.m_font = MWAWFont(3,12);
1650
7.13k
  listener->setFont(actFont.m_font);
1651
7.13k
  NisusWrtStruct::FootnoteInfo ftInfo;
1652
7.13k
  m_mainParser->getFootnoteInfo(ftInfo);
1653
7.13k
  bool lastCharFootnote = false;
1654
7.13k
  int noteId = 0, numVar=0, actVar=-1;
1655
7.13k
  std::vector<int> varValues;
1656
7.13k
  std::map<int, int> fontIdToVarIdMap;
1657
5.02M
  for (long i = 0; i <= entry.length(); i++) {
1658
5.02M
    if (i==entry.length() && !lastCharFootnote)
1659
7.13k
      break;
1660
5.41M
    while (it != zone.m_plcMap.end() && it->first.cmp(actPos) <= 0) {
1661
402k
      auto const &plcPos = it->first;
1662
402k
      auto const &plc = it++->second;
1663
402k
      f << str;
1664
402k
      str="";
1665
402k
      if (plcPos.cmp(actPos) < 0) {
1666
20.9k
        MWAW_DEBUG_MSG(("NisusWrtText::sendText: oops find unexpected position\n"));
1667
20.9k
        f << "###[" << plc << "]";
1668
20.9k
        continue;
1669
20.9k
      }
1670
381k
      f << "[" << plc << "]";
1671
381k
      switch (plc.m_type) {
1672
215k
      case NisusWrtTextInternal::P_Format: {
1673
215k
        if (plc.m_id < 0 || plc.m_id >= int(m_state->m_fontList.size())) {
1674
37.1k
          MWAW_DEBUG_MSG(("NisusWrtText::sendText: oops can not find the actual font\n"));
1675
37.1k
          f << "###";
1676
37.1k
          break;
1677
37.1k
        }
1678
178k
        auto const &font = m_state->m_fontList[size_t(plc.m_id)];
1679
178k
        actFont = font;
1680
178k
        if (font.m_pictureId <= 0)
1681
174k
          listener->setFont(font.m_font);
1682
178k
        if (!font.isVariable())
1683
172k
          break;
1684
5.63k
        if (fontIdToVarIdMap.find(plc.m_id) != fontIdToVarIdMap.end())
1685
2.28k
          actVar = fontIdToVarIdMap.find(plc.m_id)->second;
1686
3.35k
        else {
1687
3.35k
          actVar = numVar++;
1688
3.35k
          fontIdToVarIdMap[plc.m_id]=actVar;
1689
3.35k
        }
1690
5.63k
        break;
1691
178k
      }
1692
55.0k
      case NisusWrtTextInternal::P_Ruler: {
1693
55.0k
        if (plc.m_id < 0 || plc.m_id >= int(zone.m_paragraphList.size())) {
1694
0
          MWAW_DEBUG_MSG(("NisusWrtText::sendText: oops can not find the actual ruler\n"));
1695
0
          f << "###";
1696
0
          break;
1697
0
        }
1698
55.0k
        auto const &para = zone.m_paragraphList[size_t(plc.m_id)];
1699
55.0k
        setProperty(para, width);
1700
55.0k
        break;
1701
55.0k
      }
1702
0
      case NisusWrtTextInternal::P_Footnote: {
1703
0
        if (!isMain) break;
1704
0
        if (!lastCharFootnote) {
1705
0
          MWAW_DEBUG_MSG(("NisusWrtText::sendText: oops do not find the footnote symbol\n"));
1706
0
          break;
1707
0
        }
1708
0
        if (plc.m_id < 0 || plc.m_id >= int(m_state->m_footnoteList.size())) {
1709
0
          MWAW_DEBUG_MSG(("NisusWrtText::sendText: can not find the footnote\n"));
1710
0
          MWAWSubDocumentPtr subdoc(new NisusWrtTextInternal::SubDocument(*this, input, -1, libmwaw::DOC_NOTE));
1711
0
          listener->insertNote(MWAWNote(MWAWNote::FootNote), subdoc);
1712
0
          break;
1713
0
        }
1714
0
        MWAWSubDocumentPtr subdoc(new NisusWrtTextInternal::SubDocument(*this, input, plc.m_id, libmwaw::DOC_NOTE));
1715
0
        auto const &fnote = m_state->m_footnoteList[size_t(plc.m_id)];
1716
0
        noteId++;
1717
0
        if (fnote.m_number && noteId != fnote.m_number)
1718
0
          noteId = fnote.m_number;
1719
0
        MWAWNote note(ftInfo.endNotes() ? MWAWNote::EndNote : MWAWNote::FootNote);
1720
0
        note.m_number=noteId;
1721
0
        note.m_label=fnote.getTextLabel(noteId).c_str();
1722
0
        listener->insertNote(note, subdoc);
1723
0
        break;
1724
0
      }
1725
110k
      case NisusWrtTextInternal::P_PicturePara: {
1726
110k
        if (plc.m_id < 0 || plc.m_id >= int(zone.m_pictureParaList.size())) {
1727
0
          MWAW_DEBUG_MSG(("NisusWrtText::sendText: can not find the paragraph picture\n"));
1728
0
          break;
1729
0
        }
1730
110k
        auto &pict = zone.m_pictureParaList[size_t(plc.m_id)];
1731
110k
        MWAWPosition pictPos(MWAWVec2f(pict.m_position.min()), MWAWVec2f(pict.m_position.size()), librevenge::RVNG_POINT);
1732
110k
        pictPos.setRelativePosition(MWAWPosition::Paragraph);
1733
110k
        pictPos.m_wrapping = MWAWPosition::WBackground;
1734
110k
        m_mainParser->sendPicture(pict.m_id, pictPos);
1735
110k
        break;
1736
110k
      }
1737
0
      case NisusWrtTextInternal::P_HeaderFooter:
1738
0
        break;
1739
0
      case NisusWrtTextInternal::P_Unknown:
1740
#if !defined(__clang__)
1741
      default:
1742
#endif
1743
0
        MWAW_DEBUG_MSG(("NisusWrtText::sendText: oops can not find unknown plc type\n"));
1744
0
        f << "###";
1745
0
        break;
1746
381k
      }
1747
381k
    }
1748
1749
5.01M
    if (input->isEnd())
1750
0
      break;
1751
5.01M
    if (i==entry.length())
1752
0
      break;
1753
5.01M
    auto c = static_cast<unsigned char>(input->readULong(1));
1754
5.01M
    str+=char(c);
1755
5.01M
    if (c==0xd) {
1756
202k
      f << str;
1757
202k
      ascFile.addPos(pos);
1758
202k
      ascFile.addNote(f.str().c_str());
1759
1760
202k
      str = "";
1761
202k
      pos = input->tell();
1762
202k
      f.str("");
1763
202k
      f << "TEXT" << zoneId << ":";
1764
202k
    }
1765
1766
    // update the position
1767
5.01M
    switch (c) {
1768
202k
    case 0xd:
1769
202k
      actPos.m_paragraph++;
1770
202k
      actPos.m_word = actPos.m_char = 0;
1771
202k
      break;
1772
98.4k
    case '\t':
1773
734k
    case ' ':
1774
734k
      actPos.m_word++;
1775
734k
      actPos.m_char = 0;
1776
734k
      break;
1777
4.07M
    default:
1778
4.07M
      actPos.m_char++;
1779
4.07M
      break;
1780
5.01M
    }
1781
1782
    // send char
1783
5.01M
    lastCharFootnote = false;
1784
5.01M
    switch (c) {
1785
7.82k
    case 0x1: {
1786
7.82k
      if (actFont.m_pictureId <= 0) {
1787
5.77k
        MWAW_DEBUG_MSG(("NisusWrtText::sendText: can not find pictureId for char 1\n"));
1788
5.77k
        f << "#";
1789
5.77k
        break;
1790
5.77k
      }
1791
2.05k
      MWAWPosition pictPos(MWAWVec2f(actFont.m_pictureDim[0].min()), MWAWVec2f(actFont.m_pictureDim[0].size()), librevenge::RVNG_POINT);
1792
2.05k
      pictPos.setRelativePosition(MWAWPosition::CharBaseLine);
1793
2.05k
      pictPos.setClippingPosition
1794
2.05k
      (MWAWVec2f(actFont.m_pictureDim[1].min()-actFont.m_pictureDim[0].min()),
1795
2.05k
       MWAWVec2f(actFont.m_pictureDim[0].max()-actFont.m_pictureDim[1].max()));
1796
2.05k
      m_mainParser->sendPicture(actFont.m_pictureId, pictPos);
1797
2.05k
      break;
1798
7.82k
    }
1799
388
    case 0x3: // checkme: find in some file ( but seems to do nothing )
1800
388
      break;
1801
98.4k
    case 0x9:
1802
98.4k
      listener->insertTab();
1803
98.4k
      break;
1804
164
    case 0xb:
1805
164
      listener->insertEOL(true);
1806
164
      break;
1807
1.34k
    case 0xc:
1808
1.34k
      if (!isMain) break;
1809
1.34k
      m_mainParser->newPage(++m_state->m_actualPage);
1810
1.34k
      if (ftInfo.resetNumberOnNewPage()) noteId = 0;
1811
1.34k
      break;
1812
202k
    case 0xd:
1813
202k
      listener->insertEOL();
1814
202k
      break;
1815
715
    case 0xf: {
1816
715
      std::string format(m_mainParser->getDateFormat(zoneId, actVar));
1817
715
      MWAWField field(MWAWField::Date);
1818
715
      if (format.length())
1819
468
        field.m_DTFormat = format;
1820
247
      else
1821
247
        f << "#";
1822
715
      listener->insertField(field);
1823
715
      break;
1824
1.34k
    }
1825
1.89k
    case 0x10: {
1826
1.89k
      MWAWField field(MWAWField::Time);
1827
1.89k
      field.m_DTFormat = "%H:%M";
1828
1.89k
      listener->insertField(field);
1829
1.89k
      break;
1830
1.34k
    }
1831
464
    case 0x11:
1832
464
      listener->insertField(MWAWField(MWAWField::Title));
1833
464
      break;
1834
7.27k
    case 0x14: // mark separator ( ok to ignore )
1835
7.27k
      break;
1836
318
    case 0x1c:
1837
318
      if (isMain)
1838
318
        lastCharFootnote = true;
1839
318
      break;
1840
1.50k
    case 0x1d: {
1841
1.50k
      MWAWField::Type fType;
1842
1.50k
      std::string content;
1843
1.50k
      if (!m_mainParser->getReferenceData(zoneId, actVar, fType, content, varValues)) {
1844
1.50k
        listener->insertChar(' ');
1845
1.50k
        f << "#[ref]";
1846
1.50k
        break;
1847
1.50k
      }
1848
0
      if (fType != MWAWField::None)
1849
0
        listener->insertField(MWAWField(fType));
1850
0
      else if (content.length())
1851
0
        listener->insertUnicodeString(librevenge::RVNGString(content.c_str()));
1852
0
      else
1853
0
        f << "#[ref]";
1854
0
      break;
1855
1.50k
    }
1856
    // checkme: find also 0x8, 0x13, 0x15, 0x1e, 0x1f in glossary
1857
4.69M
    default:
1858
4.69M
      i+=listener->insertCharacter(static_cast<unsigned char>(c), input, entry.end());
1859
4.69M
      break;
1860
5.01M
    }
1861
5.01M
  }
1862
7.13k
  f << str;
1863
7.13k
  ascFile.addPos(pos);
1864
7.13k
  ascFile.addNote(f.str().c_str());
1865
7.13k
  return true;
1866
7.13k
}
1867
1868
//! send data to the listener
1869
bool NisusWrtText::sendMainText()
1870
7.13k
{
1871
7.13k
  if (!m_parserState->m_textListener) return true;
1872
1873
7.13k
  if (!m_state->m_zones[0].m_entry.valid()) {
1874
0
    MWAW_DEBUG_MSG(("NisusWrtText::sendMainText: can not find the main text\n"));
1875
0
    return false;
1876
0
  }
1877
7.13k
  m_state->m_zones[0].m_entry.setParsed(true);
1878
7.13k
  sendText(m_state->m_zones[0].m_entry);
1879
7.13k
  return true;
1880
7.13k
}
1881
1882
1883
void NisusWrtText::flushExtra()
1884
0
{
1885
0
  if (!m_parserState->m_textListener) return;
1886
0
  for (size_t f = 0; f < m_state->m_footnoteList.size(); f++) {
1887
0
    if (m_state->m_footnoteList[f].m_parsed)
1888
0
      continue;
1889
0
    sendFootnote(int(f));
1890
0
  }
1891
0
  m_parserState->m_textListener->insertChar(' ');
1892
0
  for (size_t hf = 0; hf < m_state->m_hfList.size(); hf++) {
1893
0
    if (m_state->m_hfList[hf].m_parsed)
1894
0
      continue;
1895
0
    sendHeaderFooter(int(hf));
1896
0
  }
1897
0
}
1898
1899
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: