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