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 <set>
39 #include <sstream>
40 
41 #include <librevenge/librevenge.h>
42 
43 #include "MWAWCell.hxx"
44 #include "MWAWTextListener.hxx"
45 #include "MWAWFont.hxx"
46 #include "MWAWFontConverter.hxx"
47 #include "MWAWHeader.hxx"
48 #include "MWAWPosition.hxx"
49 #include "MWAWPictMac.hxx"
50 #include "MWAWPrinter.hxx"
51 #include "MWAWRSRCParser.hxx"
52 #include "MWAWSubDocument.hxx"
53 
54 #include "FullWrtGraph.hxx"
55 #include "FullWrtStruct.hxx"
56 #include "FullWrtText.hxx"
57 
58 #include "FullWrtParser.hxx"
59 
60 /** Internal: the structures of a FullWrtParser */
61 namespace FullWrtParserInternal
62 {
63 //! Internal and low level: a structure used to define the list of zone in Zone 0 data of a FullWrite file
64 struct DocZoneStruct {
65   //! constructor
DocZoneStructFullWrtParserInternal::DocZoneStruct66   DocZoneStruct()
67     : m_pos(-1)
68     , m_structType(0)
69     , m_type(-1)
70     , m_nextId(0)
71     , m_fatherId(-1)
72     , m_childList() {}
73   //! the operator<<
operator <<(std::ostream & o,DocZoneStruct const & dt)74   friend std::ostream &operator<<(std::ostream &o, DocZoneStruct const &dt)
75   {
76     switch (dt.m_structType) {
77     case 0:
78       o << "empty,";
79       break;
80     case 1: // normal
81       break;
82     case 4: // hidden?
83       o << "hidden,";
84       break;
85     default:
86       o << "#structType=" << dt.m_structType << ",";
87       break;
88     }
89     if (dt.m_type!=-1)
90       o << FullWrtStruct::getTypeName(dt.m_type);
91     if (dt.m_nextId) o << "nId=" << dt.m_nextId << ",";
92     if (dt.m_fatherId>=0) o << "fId=" << dt.m_fatherId << ",";
93     if (!dt.m_childList.empty()) {
94       o << "childs=[";
95       for (auto id : dt.m_childList)
96         o << id << ",";
97       o << "],";
98     }
99     return o;
100   }
101   //! the file position
102   long m_pos;
103   //! the struct type
104   int m_structType;
105   //! the type
106   int m_type;
107   //! the next id
108   int m_nextId;
109   //! the father id
110   int m_fatherId;
111   //! the list of child id
112   std::vector<int> m_childList;
113 };
114 
115 ////////////////////////////////////////
116 //! Internal: the sidebar of a FullWrtParser
117 struct SideBar final : public FullWrtStruct::ZoneHeader {
118   //! constructor
SideBarFullWrtParserInternal::SideBar119   explicit SideBar(FullWrtStruct::ZoneHeader &)
120     : m_box()
121     , m_page(0)
122   {
123   }
124   //! destructor
125   ~SideBar() final;
126   //! the position (in point)
127   MWAWBox2f m_box;
128   //! the page
129   int m_page;
130 };
131 
~SideBar()132 SideBar::~SideBar()
133 {
134 }
135 
136 ////////////////////////////////////////
137 //! Internal: the reference data call of a FullWrtParser
138 struct ReferenceCalledData {
139   // constructor
ReferenceCalledDataFullWrtParserInternal::ReferenceCalledData140   ReferenceCalledData()
141     : m_id(-1)
142   {
143     for (auto &val : m_values) val=0;
144   }
145   //! the operator<<
operator <<(std::ostream & o,ReferenceCalledData const & dt)146   friend std::ostream &operator<<(std::ostream &o, ReferenceCalledData const &dt)
147   {
148     if (dt.m_id >= 0) o << "refId=" << dt.m_id << ",";
149     for (int i = 0; i < 5; i++) {
150       if (dt.m_values[i])
151         o << "f" << i << "=" << dt.m_values[i] << ",";
152     }
153     return o;
154   }
155   //! the reference id
156   int m_id;
157   //! some unknown values
158   int m_values[5];
159 };
160 
161 ////////////////////////////////////////
162 //! Internal: the state of a FullWrtParser
163 struct State {
164   //! constructor
StateFullWrtParserInternal::State165   State()
166     : m_pageSpanSet(false)
167     , m_fileZoneList()
168     , m_fileZoneFlagsList()
169     , m_docZoneList()
170     , m_docFileIdMap()
171     , m_fileDocIdMap()
172     , m_biblioId(-1)
173     , m_entryMap()
174     , m_variableRedirectMap()
175     , m_referenceRedirectMap()
176     , m_actPage(0)
177     , m_numPages(0)
178     , m_headerHeight(0)
179     , m_footerHeight(0)
180   {
181     for (auto &fl : m_zoneFlagsId) fl = -1;
182   }
183 
184   //! insert a docId fileId in the correspondance map
addCorrespondanceFullWrtParserInternal::State185   bool addCorrespondance(int docId, int fileId)
186   {
187     if (m_docFileIdMap.find(docId) != m_docFileIdMap.end() ||
188         m_fileDocIdMap.find(fileId) != m_fileDocIdMap.end()) {
189       MWAW_DEBUG_MSG(("FullWrtParserInternal::State::addCorrespondance can not insert %d<->%d\n", docId, fileId));
190       return false;
191     }
192     m_fileDocIdMap[fileId]=docId;
193     m_docFileIdMap[docId]=fileId;
194     // update the zone type ( if possible )
195     if (docId >= 0 && docId < int(m_docZoneList.size()) &&
196         m_entryMap.find(fileId) != m_entryMap.end() &&
197         m_entryMap.find(fileId)->second)
198       m_entryMap.find(fileId)->second->m_fileType = m_docZoneList[size_t(docId)].m_type;
199     else {
200       MWAW_DEBUG_MSG(("FullWrtParserInternal::State::addCorrespondance can not update the zone type for %d<->%d\n", docId, fileId));
201     }
202     return true;
203   }
204   //! return the file zone id ( if found or -1)
getFileZoneIdFullWrtParserInternal::State205   int getFileZoneId(int docId) const
206   {
207     auto it = m_docFileIdMap.find(docId);
208     if (it == m_docFileIdMap.end()) {
209       MWAW_DEBUG_MSG(("FullWrtParserInternal::State::getFileZoneId can not find %d\n", docId));
210       return -1;
211     }
212     return it->second;
213   }
214   //! a flag to know if the page span has been filled
215   bool m_pageSpanSet;
216   //! the list of main zone flags id
217   int m_zoneFlagsId[3];
218 
219   //! the list of file zone position
220   FullWrtStruct::EntryPtr m_fileZoneList;
221 
222   //! the list of file zone flags
223   FullWrtStruct::EntryPtr m_fileZoneFlagsList;
224 
225   //! the list of the documents zone list
226   std::vector<DocZoneStruct> m_docZoneList;
227 
228   //! the correspondance doc id -> file id
229   std::map<int,int> m_docFileIdMap;
230 
231   //! the correspondance file id -> doc id
232   std::map<int,int> m_fileDocIdMap;
233 
234   //! the bibliography id
235   int m_biblioId;
236 
237   //! zoneId -> entry
238   std::multimap<int, FullWrtStruct::EntryPtr > m_entryMap;
239 
240   //! redirection docId -> variable docId
241   std::map<int,int> m_variableRedirectMap;
242 
243   //! redirection docId -> reference docId
244   std::map<int,ReferenceCalledData> m_referenceRedirectMap;
245 
246   int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
247 
248   int m_headerHeight /** the header height if known */,
249       m_footerHeight /** the footer height if known */;
250 };
251 
252 ////////////////////////////////////////
253 //! Internal: the subdocument of a FullWrtParser
254 class SubDocument final : public MWAWSubDocument
255 {
256 public:
SubDocument(FullWrtParser & pars,MWAWInputStreamPtr const & input,int zoneId)257   SubDocument(FullWrtParser &pars, MWAWInputStreamPtr const &input, int zoneId)
258     : MWAWSubDocument(&pars, input, MWAWEntry())
259     , m_id(zoneId) {}
260 
261   //! destructor
~SubDocument()262   ~SubDocument() final {}
263 
264   //! operator!=
operator !=(MWAWSubDocument const & doc) const265   bool operator!=(MWAWSubDocument const &doc) const final
266   {
267     if (MWAWSubDocument::operator!=(doc)) return true;
268     auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
269     if (!sDoc) return true;
270     if (m_id != sDoc->m_id) return true;
271     return false;
272   }
273 
274   //! the parser function
275   void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
276 
277 protected:
278   //! the subdocument id
279   int m_id;
280 };
281 
parse(MWAWListenerPtr & listener,libmwaw::SubDocumentType)282 void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
283 {
284   if (!listener.get()) {
285     MWAW_DEBUG_MSG(("FullWrtParserInternall::SubDocument::parse: no listener\n"));
286     return;
287   }
288   auto *parser=dynamic_cast<FullWrtParser *>(m_parser);
289   if (!parser) {
290     MWAW_DEBUG_MSG(("FullWrtParserInternall::SubDocument::parse: no parser\n"));
291     return;
292   }
293 
294   long pos = m_input->tell();
295   parser->send(m_id);
296   m_input->seek(pos, librevenge::RVNG_SEEK_SET);
297 }
298 }
299 
300 ////////////////////////////////////////////////////////////
301 // constructor/destructor, ...
302 ////////////////////////////////////////////////////////////
FullWrtParser(MWAWInputStreamPtr const & input,MWAWRSRCParserPtr const & rsrcParser,MWAWHeader * header)303 FullWrtParser::FullWrtParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
304   : MWAWTextParser(input, rsrcParser, header)
305   , m_state()
306   , m_graphParser()
307   , m_textParser()
308 {
309   init();
310 }
311 
~FullWrtParser()312 FullWrtParser::~FullWrtParser()
313 {
314   for (auto it : m_state->m_entryMap) {
315     FullWrtStruct::EntryPtr zone = it.second;
316     if (zone) zone->closeDebugFile();
317   }
318 }
319 
init()320 void FullWrtParser::init()
321 {
322   resetTextListener();
323   setAsciiName("main-1");
324 
325   m_state.reset(new FullWrtParserInternal::State);
326 
327   // reduce the margin (in case, the page is not defined)
328   getPageSpan().setMargins(0.1);
329 
330   m_graphParser.reset(new FullWrtGraph(*this));
331   m_textParser.reset(new FullWrtText(*this));
332 }
333 
334 ////////////////////////////////////////////////////////////
335 // new page, interface function
336 ////////////////////////////////////////////////////////////
newPage(int number)337 void FullWrtParser::newPage(int number)
338 {
339   if (number <= m_state->m_actPage || number > m_state->m_numPages)
340     return;
341 
342   while (m_state->m_actPage < number) {
343     m_state->m_actPage++;
344     if (!getTextListener() || m_state->m_actPage == 1)
345       continue;
346     getTextListener()->insertBreak(MWAWTextListener::PageBreak);
347   }
348 }
349 
getPageLeftTop() const350 MWAWVec2f FullWrtParser::getPageLeftTop() const
351 {
352   return MWAWVec2f(float(getPageSpan().getMarginLeft()),
353                    float(getPageSpan().getMarginTop()+m_state->m_headerHeight/72.0));
354 }
355 
getBorder(int bId,FullWrtStruct::Border & border) const356 bool FullWrtParser::getBorder(int bId, FullWrtStruct::Border &border) const
357 {
358   return m_graphParser->getBorder(bId, border);
359 }
360 
getNumDocZoneStruct() const361 int FullWrtParser::getNumDocZoneStruct() const
362 {
363   return int(m_state->m_docZoneList.size());
364 }
365 
getDocumentTypeName(int id) const366 std::string FullWrtParser::getDocumentTypeName(int id) const
367 {
368   if (id<0||id >= int(m_state->m_docZoneList.size()))
369     return "";
370   return FullWrtStruct::getTypeName(m_state->m_docZoneList[size_t(id)].m_type);
371 }
372 
sendGraphic(int id)373 void FullWrtParser::sendGraphic(int id)
374 {
375   if (id < 0 || id >= int(m_state->m_docZoneList.size())) {
376     MWAW_DEBUG_MSG(("FullWrtParser::sendGraphic: can not find graphic data for zone %d\n", id));
377   }
378   else {
379     auto const &data = m_state->m_docZoneList[size_t(id)];
380     if (data.m_type != 0x15) {
381       MWAW_DEBUG_MSG(("FullWrtParser::sendGraphic: call for zone[%x]\n", static_cast<unsigned int>(data.m_type)));
382     }
383   }
384   m_graphParser->sendGraphic(m_state->getFileZoneId(id));
385 }
386 
send(int fileId,MWAWColor fontColor)387 bool FullWrtParser::send(int fileId, MWAWColor fontColor)
388 {
389   if (fileId < 0) {
390     if (getTextListener()) getTextListener()->insertChar(' ');
391     return true;
392   }
393   return m_textParser->send(fileId, fontColor);
394 }
395 
396 ////////////////////////////////////////////////////////////
397 // the parser
398 ////////////////////////////////////////////////////////////
parse(librevenge::RVNGTextInterface * docInterface)399 void FullWrtParser::parse(librevenge::RVNGTextInterface *docInterface)
400 {
401   if (!getInput().get() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
402   bool ok = true;
403   try {
404 #ifdef DEBUG
405     // just test if a rsrc exists
406     if (getRSRCParser()) {
407       MWAW_DEBUG_MSG(("FullWrtParser::parse: find a ressource fork\n"));
408       getRSRCParser()->getEntry("STR ", 700);
409     }
410 #endif
411     // create the asciiFile
412     ascii().setStream(getInput());
413     ascii().open(asciiName());
414 
415     checkHeader(nullptr);
416     ok = createZones();
417     if (ok) {
418       createDocument(docInterface);
419       m_graphParser->sendPageGraphics();
420       m_textParser->sendMainText();
421       m_graphParser->flushExtra();
422 #ifdef DEBUG
423       m_textParser->flushExtra();
424 #endif
425     }
426     bool first = true;
427     libmwaw::DebugStream f;
428     for (auto it : m_state->m_entryMap) {
429       FullWrtStruct::EntryPtr zone = it.second;
430       if (!zone || !zone->valid() || zone->isParsed()) continue;
431       f.str("");
432       if (zone->hasType("UnknownZone"))
433         f << "Entries(NotParsed)";
434       else
435         f << "Entries(" << zone->type() << ")";
436       if (zone->hasType("Biblio")) {
437         MWAW_DEBUG_MSG(("FullWrtParser::parse: find some biblio zone unparsed!!!\n"));
438       }
439       else if (first) {
440         f << "###";
441         first = false;
442         MWAW_DEBUG_MSG(("FullWrtParser::parse: find some unparsed zone!!!\n"));
443       }
444       if (zone->m_nextId != -2) f << "[" << zone->m_nextId << "]";
445       f << "|" << *zone << ":";
446       libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
447 
448       asciiFile.addPos(zone->begin());
449       asciiFile.addNote(f.str().c_str());
450       asciiFile.addPos(zone->end());
451       asciiFile.addNote("_");
452 
453       zone->closeDebugFile();
454     }
455 
456     ascii().reset();
457   }
458   catch (...) {
459     MWAW_DEBUG_MSG(("FullWrtParser::parse: exception catched when parsing\n"));
460     ok = false;
461   }
462 
463   resetTextListener();
464   if (!ok) throw(libmwaw::ParseException());
465 }
466 
467 ////////////////////////////////////////////////////////////
468 // create the document
469 ////////////////////////////////////////////////////////////
createDocument(librevenge::RVNGTextInterface * documentInterface)470 void FullWrtParser::createDocument(librevenge::RVNGTextInterface *documentInterface)
471 {
472   if (!documentInterface) return;
473   if (getTextListener()) {
474     MWAW_DEBUG_MSG(("FullWrtParser::createDocument: listener already exist\n"));
475     return;
476   }
477 
478   // update the page
479   m_state->m_actPage = 0;
480   int numPage = m_textParser->numPages();
481   if (m_graphParser->numPages()>numPage)
482     numPage=m_graphParser->numPages();
483   m_state->m_numPages = numPage;
484 
485   // create the page list
486   int actHeaderId = -1, actFooterId = -1;
487   std::shared_ptr<FullWrtParserInternal::SubDocument> headerSubdoc, footerSubdoc;
488   std::vector<MWAWPageSpan> pageList;
489   for (int i = 0; i < m_state->m_numPages;) {
490     int numSim[2]= {1,1};
491     int headerId =  m_textParser->getHeaderFooterId(true,i+1, numSim[0]);
492     if (headerId != actHeaderId) {
493       actHeaderId = headerId;
494       if (actHeaderId == -1)
495         headerSubdoc.reset();
496       else
497         headerSubdoc.reset
498         (new FullWrtParserInternal::SubDocument(*this, getInput(), headerId));
499     }
500     int footerId =  m_textParser->getHeaderFooterId(false, i+1, numSim[1]);
501     if (footerId != actFooterId) {
502       actFooterId = footerId;
503       if (actFooterId == -1)
504         footerSubdoc.reset();
505       else
506         footerSubdoc.reset
507         (new FullWrtParserInternal::SubDocument(*this, getInput(), footerId));
508     }
509 
510     MWAWPageSpan ps(getPageSpan());
511     if (headerSubdoc) {
512       MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
513       header.m_subDocument=headerSubdoc;
514       ps.setHeaderFooter(header);
515     }
516     if (footerSubdoc) {
517       MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
518       footer.m_subDocument=footerSubdoc;
519       ps.setHeaderFooter(footer);
520     }
521     if (numSim[1] < numSim[0]) numSim[0]=numSim[1];
522     if (numSim[0] < 1) numSim[0]=1;
523     ps.setPageSpan(numSim[0]);
524     i+=numSim[0];
525     pageList.push_back(ps);
526   }
527 
528   // retrieve the header/footer
529   MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
530   setTextListener(listen);
531   listen->startDocument();
532 }
533 
534 
535 ////////////////////////////////////////////////////////////
536 //
537 // Intermediate level
538 //
539 ////////////////////////////////////////////////////////////
createFileZones()540 bool FullWrtParser::createFileZones()
541 {
542   /* FileZonePos: define a list of zone in the file and a link between these zone
543      FileZoneFlags: define the list of contents zone and link each to the first file zone
544    */
545 
546   // first creates the zone (entry are mapped by number of the zones in the file)
547   if (m_state->m_fileZoneList)
548     readFileZonePos(m_state->m_fileZoneList);
549   // update each main filezone, ie. move appart the 3 main zone and sets their final ids
550   if (m_state->m_fileZoneFlagsList)
551     readFileZoneFlags(m_state->m_fileZoneFlagsList);
552 
553   // finally, remapped the enry by fId
554   std::vector<FullWrtStruct::EntryPtr > listZones;
555   for (auto it : m_state->m_entryMap)
556     listZones.push_back(it.second);
557   m_state->m_entryMap.clear();
558   for (auto &entry : listZones) {
559     if (!entry->valid() || entry->isParsed()) continue;
560     int fId = entry->id();
561     if (entry->m_typeId == -1) fId=-fId-1;
562     if (m_state->m_entryMap.find(fId) != m_state->m_entryMap.end()) {
563       MWAW_DEBUG_MSG(("FullWrtParser::createFileZones: can not find generic zone id %d\n",fId));
564     }
565     else
566       m_state->m_entryMap.insert
567       (std::multimap<int, FullWrtStruct::EntryPtr >::value_type(fId, entry));
568   }
569   return true;
570 }
571 
createZones()572 bool FullWrtParser::createZones()
573 {
574   createFileZones();
575 
576   // first treat the main zones
577   std::vector<FullWrtStruct::EntryPtr > mainZones;
578   mainZones.resize(3);
579   for (auto it : m_state->m_entryMap) {
580     FullWrtStruct::EntryPtr &zone = it.second;
581     if (!zone || !zone->valid() || zone->isParsed()) continue;
582     if (zone->m_typeId != -1 || zone->id() < 0 || zone->id() >= 3)
583       continue;
584     auto zId = size_t(zone->id());
585     if (mainZones[size_t(zId)]) {
586       MWAW_DEBUG_MSG(("FullWrtParser::createZones: Oops main zone %d already founded\n", int(zId)));
587       continue;
588     }
589     mainZones[zId] = zone;
590   }
591   if (!mainZones[1] || !readDocZoneStruct(mainZones[1])) {
592     MWAW_DEBUG_MSG(("FullWrtParser::createZones: can not read the docZoneStruct zone\n"));
593   }
594   if (!mainZones[0] || !readDocZoneData(mainZones[0])) {
595     MWAW_DEBUG_MSG(("FullWrtParser::createZones: can not read the docZoneData zone\n"));
596   }
597   if (!mainZones[2] || !readDocInfo(mainZones[2])) {
598     MWAW_DEBUG_MSG(("FullWrtParser::createZones: can not read the document information zone\n"));
599   }
600 
601   // now treat the other zones
602   for (auto it : m_state->m_entryMap) {
603     FullWrtStruct::EntryPtr &zone = it.second;
604     if (!zone || !zone->valid() || zone->isParsed()) continue;
605     if (zone->m_typeId >= 0) {
606       // first use the zone type
607       bool done = false;
608       switch (zone->m_fileType) {
609       case 0x15:
610         done = m_graphParser->readGraphic(zone);
611         break;
612       case 0xa:
613       case 0xb:
614       case 0xc:
615       case 0xd:
616       case 0xe:
617       case 0xf:
618       case 0x10:
619       case 0x11:
620       case 0x12:
621       case 0x13:
622       case 0x14:
623       case 0x18:
624         done = m_textParser->readTextData(zone);
625         break;
626       default:
627         break;
628       }
629       if (done) continue;
630 
631       // unknown, so try all possibilities
632       if (m_graphParser->readGraphic(zone)) continue;
633       if (m_textParser->readTextData(zone)) continue;
634     }
635     else if (zone->m_typeId == -1) {
636       if (zone->id()>=0 && zone->id()< 3) {
637         MWAW_DEBUG_MSG(("FullWrtParser::createZones: Oops find an unparsed main zone %d\n", zone->id()));
638       }
639       else if (zone->hasType("Biblio")) {
640         MWAW_DEBUG_MSG(("FullWrtParser::createZones: find a bibliography zone: unparsed\n"));
641       }
642       else {
643         MWAW_DEBUG_MSG(("FullWrtParser::createZones: find unexpected general zone\n"));
644       }
645     }
646   }
647   m_textParser->prepareData();
648   return true;
649 }
650 
651 ////////////////////////////////////////////////////////////
652 //
653 // Low level
654 //
655 ////////////////////////////////////////////////////////////
656 
657 ////////////////////////////////////////////////////////////
658 // read the header
659 ////////////////////////////////////////////////////////////
checkHeader(MWAWHeader * header,bool)660 bool FullWrtParser::checkHeader(MWAWHeader *header, bool /*strict*/)
661 {
662   *m_state = FullWrtParserInternal::State();
663 
664   MWAWInputStreamPtr input = getInput();
665   if (!input || !input->hasDataFork())
666     return false;
667 
668   int const minSize=50;
669   input->seek(minSize,librevenge::RVNG_SEEK_SET);
670   if (int(input->tell()) != minSize) {
671     MWAW_DEBUG_MSG(("FullWrtParser::checkHeader: file is too short\n"));
672     return false;
673   }
674 
675   if (!readDocPosition())
676     return false;
677 
678   input->seek(0,librevenge::RVNG_SEEK_SET);
679 
680   if (header)
681     header->reset(MWAWDocument::MWAW_T_FULLWRITE, 1);
682 
683   return true;
684 }
685 
readDocInfo(FullWrtStruct::EntryPtr zone)686 bool FullWrtParser::readDocInfo(FullWrtStruct::EntryPtr zone)
687 {
688   if (zone->length() < 0x4b2)
689     return false;
690   MWAWInputStreamPtr input = zone->m_input;
691   libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
692   libmwaw::DebugStream f;
693 
694   long pos = zone->begin();
695   input->seek(pos, librevenge::RVNG_SEEK_SET);
696   auto N = static_cast<int>(input->readLong(2));
697   if (!N) return false;
698   input->seek(4, librevenge::RVNG_SEEK_CUR);
699   auto val = static_cast<int>(input->readLong(2));
700   if (N < val-2 || N > val+2) return false;
701   input->seek(2, librevenge::RVNG_SEEK_CUR);
702   auto nC = static_cast<int>(input->readULong(1));
703   if (!nC || nC > 70) return false;
704   for (int i = 0; i < nC; i++) {
705     auto c = static_cast<int>(input->readULong(1));
706     if (c < 0x20) return false;
707   }
708 
709   zone->setParsed(true);
710   input->seek(pos+2, librevenge::RVNG_SEEK_SET);
711   f << "Entries(DocInfo)|" << *zone << ":";
712   f << "N0=" << N << ",";
713   f << "unkn0=" << std::hex << input->readULong(2) << std::dec << ","; // big number
714   for (int i = 0; i < 2; i++) { // 0|1|a7|ff followed by a small number
715     val = static_cast<int>(input->readULong(1));
716     if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
717   }
718   val = static_cast<int>(input->readLong(2)); // almost always N
719   if (val != N) f << "N1=" << val << ",";
720   f << "unkn1=" << std::hex << input->readULong(2) << std::dec << ","; // big number
721   std::string title("");
722   nC = static_cast<int>(input->readULong(1));
723   for (int i = 0; i < nC; i++)
724     title += char(input->readLong(1));
725   f << "title=" << title << ",";
726   asciiFile.addDelimiter(input->tell(),'|');
727   asciiFile.addPos(pos);
728   asciiFile.addNote(f.str().c_str());
729 
730   pos += 10+70; // checkme
731   input->seek(pos, librevenge::RVNG_SEEK_SET);
732   f.str("");
733   f << "DocInfo-A:";
734   for (int i = 0; i < 4; i++) { // 0, 1, 2, 80
735     val = static_cast<int>(input->readULong(1));
736     if (val)
737       f << "fl" << i << "=" << std::hex << val << std::dec << ",";
738   }
739   val = static_cast<int>(input->readLong(2)); // almost always 0, but find also 53, 64, ba, f4
740   if (val) f << "f0=" << val << ",";
741   f << "unkn=["; // 2 big number which seems realted
742   for (int i = 0; i < 2; i++)
743     f << std::hex << input->readULong(4) << std::dec << ",";
744   f << "],";
745   for (int i = 4; i < 6; i++) { // always 1, 0 ?
746     val = static_cast<int>(input->readULong(1));
747     if (val!=5-i) f << "fl" << i << "=" << std::hex << val << std::dec << ",";
748   }
749   // 5 relative big number : multiple of 4
750   auto ptr = long(input->readULong(4));
751   f << "unkn2=[" << std::hex << ptr << "," << std::dec;
752   for (int i = 0; i < 4; i++)
753     f << long(input->readULong(4))-ptr << ",";
754   f << "],";
755   val = static_cast<int>(input->readLong(2)); // almost always -2, but find also 6
756   if (val != -2)
757     f << "f1=" << val << ",";
758   for (int i = 0; i < 2; i++) { // always 0,0
759     val = static_cast<int>(input->readULong(2));
760     if (val) f << "f" << i+2 << "=" << std::hex << val << std::dec << ",";
761   }
762   for (int st=0; st < 2; st++) {
763     long actPos = input->tell();
764     val = static_cast<int>(input->readULong(2)); // big number
765     if (val) f << "g" << st << "=" << std::hex << val << std::dec << ",";
766 
767     nC = static_cast<int>(input->readULong(1));
768     if (nC > 32) {
769       MWAW_DEBUG_MSG(("FullWrtParser::readDocInfo: can not read user name\n"));
770       nC = 0;
771     }
772     std::string s("");
773     for (int i = 0; i < nC; i++) s+=char(input->readLong(1));
774     if (nC)
775       f << "Username" << st << "=" << s << ",";
776     input->seek(actPos+36, librevenge::RVNG_SEEK_SET);
777   }
778 
779   asciiFile.addPos(pos);
780   asciiFile.addNote(f.str().c_str());
781   pos = input->tell();
782   asciiFile.addPos(pos);
783   asciiFile.addNote("DocInfo-B");
784   pos+=196;
785   input->seek(pos, librevenge::RVNG_SEEK_SET);
786   for (int i = 0; i < 6; i++) {
787     pos = input->tell();
788     f.str("");
789     f << "DocInfo-C" << i << ":" << input->readLong(2);
790 
791     asciiFile.addPos(pos);
792     asciiFile.addNote(f.str().c_str());
793     input->seek(pos+48, librevenge::RVNG_SEEK_SET);
794   }
795   for (int i = 0; i < 9; i++) {
796     pos = input->tell();
797     f.str("");
798     f << "DocInfo-D" << i << ":";
799 
800     asciiFile.addPos(pos);
801     asciiFile.addNote(f.str().c_str());
802     input->seek(pos+38, librevenge::RVNG_SEEK_SET);
803   }
804   pos = input->tell();
805   asciiFile.addPos(pos);
806   asciiFile.addNote("DocInfo-E");
807   input->seek(pos+182, librevenge::RVNG_SEEK_SET);
808 
809   // this part in v1 and v2
810   pos = input->tell();
811   f.str("");
812   f << "DocInfo-F:";
813   bool ok = true;
814   if (version()==2) {
815     asciiFile.addPos(pos);
816     asciiFile.addNote(f.str().c_str());
817     input->seek(pos+436, librevenge::RVNG_SEEK_SET);
818     pos = input->tell();
819     if (!readEndDocInfo(zone)) {
820       asciiFile.addPos(pos);
821       asciiFile.addNote("DocInfoII#");
822       ok = false;
823     }
824   }
825   else {
826     if (pos+18 < zone->end()) {
827       val = static_cast<int>(input->readLong(2)); // always 0
828       if (val) f << "f0=" << val << ",";
829       val = static_cast<int>(input->readLong(4)); // can be a big number (often neg )
830       if (val) f << "f1=" << val << ",";
831       // always 0 except one time g0=a,g1=1d,g5=50fe
832       for (int i = 0; i < 6; i++) {
833         val = static_cast<int>(input->readULong(2));
834         if (val) f << "g" << i << "=" << std::hex << val << std::dec << ",";
835       }
836     }
837     else {
838       ok = false;
839       f << "#";
840     }
841     asciiFile.addPos(pos);
842     asciiFile.addNote(f.str().c_str());
843   }
844 
845   if (ok) {
846     pos=input->tell();
847     f.str("");
848     f << "DocInfo-G:";
849     auto type=static_cast<int>(input->readULong(2));
850     f << "f0=" << std::hex << type << std::dec << ",";
851     int dim[4];
852     for (auto &d : dim) d=static_cast<int>(input->readLong(2));
853     f << "dim=" << dim[1] << "x" << dim[0] << "<->"
854       << dim[3] << "x" << dim[2] << ",";
855     // checkme: 3: followed by 2 int, 6-b: followed by 3 int, e-f: by 4, other?
856     int num=(type&0xF000)>=0xE000 ? 4:(type&0xF000)>0x3000 ? 3:2;
857     for (int i=0; i< num; ++i) { // f1=0x100, f3=3
858       val=static_cast<int>(input->readLong(2));
859       if (val) f << "f" << i+1 << "=" << val << ",";
860     }
861     for (int st=0; st<2; ++st) { // probably left/right page
862       int margins[4];
863       for (auto &margin : margins) margin=static_cast<int>(input->readLong(2));
864       f << "pageDim" << st << "=" << margins[1] << "x" << margins[0] << "<->"
865         << margins[3] << "x" << margins[2] << ",";
866       if (st==1)
867         break;
868       if (margins[0]<margins[2] && margins[2]<dim[2] && 2*(margins[2]-margins[0]) > dim[2] &&
869           margins[1]<margins[3] && margins[3]<dim[3] && 2*(margins[3]-margins[1]) > dim[3] &&
870           dim[2]>100 && dim[2]<2000 && dim[3]>100 && dim[3]<2000) {
871         getPageSpan().setMarginTop(double(margins[0])/72.0);
872         getPageSpan().setMarginBottom(double(dim[2]-margins[2])/72.0);
873         getPageSpan().setMarginLeft(double(margins[1])/72.0);
874         getPageSpan().setMarginRight(double(dim[3]-margins[3])/72.0);
875         getPageSpan().setFormLength(double(dim[2])/72.);
876         getPageSpan().setFormWidth(double(dim[3])/72.);
877         m_state->m_pageSpanSet=true;
878       }
879       else {
880         MWAW_DEBUG_MSG(("FullWrtParser::readDocInfo:can not read document margins!\n"));
881       }
882     }
883     asciiFile.addDelimiter(input->tell(),'|');
884 
885     asciiFile.addPos(pos);
886     asciiFile.addNote(f.str().c_str());
887     /* seems to end with
888        00000009000043686f6f73657200
889        000000100000436f6e74726f6c2050616e656c7300
890        0000000b000046696e642046696c6500
891        0000001500004772617068696e672043616c63756c61746f7200
892        0000000f00004a69677361772050757a7a6c6500
893        0000000a00004b6579204361707300
894        0000000a00004e6f74652050616400
895        0000001500000000000000000000000000006361000000000000
896     */
897   }
898   // try to retrieve the last part: printInfo+3 int
899   input->seek(zone->end()-130, librevenge::RVNG_SEEK_SET);
900   if (readPrintInfo(zone)) {
901     pos = input->tell();
902     f.str("");
903     f << "DocInfo-End:";
904     if (pos == zone->end()-6) {
905       // f0=0, f1=0|1(in v2) but can also be a big number in v1, f2=0|1a
906       for (int i = 0; i < 3; i++) {
907         val = static_cast<int>(input->readLong(2));
908         if (val) f << "f" << i << "=" << val << ",";
909       }
910     }
911     else
912       f << "#";
913     asciiFile.addPos(pos);
914     asciiFile.addNote(f.str().c_str());
915   }
916   else {
917     MWAW_DEBUG_MSG(("FullWrtParser::readDocInfo: can not find print info\n"));
918     asciiFile.addPos(zone->end()-130);
919     asciiFile.addNote("DocInfo-G#");
920   }
921 
922   zone->closeDebugFile();
923   return true;
924 }
925 
926 ////////////////////////////////////////////////////////////
927 // read the end of the zone data
readEndDocInfo(FullWrtStruct::EntryPtr zone)928 bool FullWrtParser::readEndDocInfo(FullWrtStruct::EntryPtr zone)
929 {
930   if (version() < 2)
931     return false;
932 
933   MWAWInputStreamPtr input = zone->m_input;
934   libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
935   libmwaw::DebugStream f;
936 
937   // at least 4 possible zones, maybe more...
938   for (int i = 0; i < 5; i++) {
939     long pos = input->tell();
940     bool ok = true;
941     std::string name("");
942     for (int j = 0; j < 4; j++) {
943       auto val=int(input->readULong(1));
944       if (val < 9) {
945         ok = false;
946         break;
947       }
948       name+=char(val);
949     }
950     if (!ok || input->readULong(1)) {
951       input->seek(pos, librevenge::RVNG_SEEK_SET);
952       break;
953     }
954     input->seek(pos, librevenge::RVNG_SEEK_SET);
955     ok = false;
956     if (name=="font") // block0 : unseen
957       ;
958     else if (name=="bord")
959       ok = m_graphParser->readBorderDocInfo(zone);
960     else if (name=="extr")
961       ok = m_textParser->readParaModDocInfo(zone);
962     else if (name=="cite") // block3
963       ok = readCitationDocInfo(zone);
964 
965     if (ok)
966       continue;
967 
968     MWAW_DEBUG_MSG(("FullWrtParser::readEndDocInfo: can not read block %s\n", name.c_str()));
969     input->seek(pos+5, librevenge::RVNG_SEEK_SET);
970     long blckSz = input->readLong(4);
971     if (blckSz < 2 || pos+8+blckSz > zone->end()) {
972       input->seek(pos, librevenge::RVNG_SEEK_SET);
973       break;
974     }
975     auto num=int(input->readULong(2));
976     f.str("");
977     f << "Entries(Doc" << name << "):N=" << num << ",###";
978     asciiFile.addPos(pos);
979     asciiFile.addNote(f.str().c_str());
980     input->seek(pos+9+blckSz, librevenge::RVNG_SEEK_SET);
981   }
982   return true;
983 }
984 
readCitationDocInfo(FullWrtStruct::EntryPtr zone)985 bool FullWrtParser::readCitationDocInfo(FullWrtStruct::EntryPtr zone)
986 {
987   MWAWInputStreamPtr input = zone->m_input;
988   libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
989   libmwaw::DebugStream f;
990   long pos = input->tell();
991   if (input->readULong(4)!=0x63697465 || input->readULong(1)) {
992     input->seek(pos, librevenge::RVNG_SEEK_SET);
993     return false;
994   }
995 
996   long blckSz = input->readLong(4);
997   long endData = pos+9+blckSz;
998   int num = static_cast<int>(input->readULong(2)), val;
999   f << "Entries(RefValues):N=" << num << ",";
1000   if (blckSz <= 2 || endData > zone->end() || pos+num > endData) {
1001     MWAW_DEBUG_MSG(("FullWrtParser::readCitationDocInfo: problem reading the data block or the number of data\n"));
1002     f << "###";
1003     asciiFile.addPos(pos);
1004     asciiFile.addNote(f.str().c_str());
1005     if (endData <= zone->end()) {
1006       input->seek(endData, librevenge::RVNG_SEEK_SET);
1007       return true;
1008     }
1009     input->seek(pos, librevenge::RVNG_SEEK_SET);
1010     return false;
1011   }
1012 
1013   asciiFile.addPos(pos);
1014   asciiFile.addNote(f.str().c_str());
1015   for (int i = 0; i < num; i++) {
1016     f.str("");
1017     f << "RefValues-" << i << ",";
1018     pos = input->tell();
1019     auto sz = int(input->readULong(1));
1020     if (input->tell()+sz > endData)
1021       break;
1022     std::string name("");
1023     bool ok = true;
1024     for (int j = 0; j < sz; j++) {
1025       val = int(input->readULong(1));
1026       if (val < 0x9) {
1027         ok = false;
1028         break;
1029       }
1030       name+=char(val);
1031     }
1032     if (!ok) break;
1033     f << "\"" << name << "\",";
1034     asciiFile.addPos(pos);
1035     asciiFile.addNote(f.str().c_str());
1036   }
1037   if (input->tell() != endData) {
1038     f.str("");
1039     f << "RefValues-##";
1040     asciiFile.addPos(pos);
1041     asciiFile.addNote(f.str().c_str());
1042     input->seek(endData, librevenge::RVNG_SEEK_SET);
1043   }
1044   return true;
1045 }
1046 
1047 ////////////////////////////////////////////////////////////
1048 // read the print info
1049 ////////////////////////////////////////////////////////////
readPrintInfo(FullWrtStruct::EntryPtr zone)1050 bool FullWrtParser::readPrintInfo(FullWrtStruct::EntryPtr zone)
1051 {
1052   MWAWInputStreamPtr input = zone->m_input;
1053   libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
1054 
1055   long pos = input->tell();
1056   if (input->readULong(2) != 0) return false;
1057   auto sz = long(input->readULong(2));
1058   if (sz != 0x78)
1059     return false;
1060   long endPos = pos+4+sz;
1061   input->seek(endPos, librevenge::RVNG_SEEK_SET);
1062   if (long(input->tell()) != endPos) {
1063     MWAW_DEBUG_MSG(("FullWrtParser::readPrintInfo: file is too short\n"));
1064     return false;
1065   }
1066   input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1067 
1068   libmwaw::DebugStream f;
1069   // print info
1070   libmwaw::PrinterInfo info;
1071   if (!info.read(input)) {
1072     // we know that the size is ok, so let try to continue
1073     asciiFile.addPos(pos);
1074     asciiFile.addNote("Entries(PrintInfo):##");
1075     input->seek(endPos, librevenge::RVNG_SEEK_SET);
1076     MWAW_DEBUG_MSG(("FullWrtParser::readPrintInfo: can not read print info, continue\n"));
1077     return true;
1078   }
1079   f << "Entries(PrintInfo):"<< info;
1080 
1081   MWAWVec2i paperSize = info.paper().size();
1082   MWAWVec2i pageSize = info.page().size();
1083   if (pageSize.x() <= 0 || pageSize.y() <= 0 ||
1084       paperSize.x() <= 0 || paperSize.y() <= 0) return false;
1085 
1086   if (!m_state->m_pageSpanSet) {
1087     // define margin from print info
1088     MWAWVec2i lTopMargin= -1 * info.paper().pos(0);
1089     MWAWVec2i rBotMargin=info.paper().size() - info.page().size();
1090 
1091     // move margin left | top
1092     int decalX = lTopMargin.x() > 14 ? lTopMargin.x()-14 : 0;
1093     int decalY = lTopMargin.y() > 14 ? lTopMargin.y()-14 : 0;
1094     lTopMargin -= MWAWVec2i(decalX, decalY);
1095     rBotMargin += MWAWVec2i(decalX, decalY);
1096 
1097     // decrease right | bottom
1098     int rightMarg = rBotMargin.x() -50;
1099     if (rightMarg < 0) rightMarg=0;
1100     int botMarg = rBotMargin.y() -50;
1101     if (botMarg < 0) botMarg=0;
1102 
1103     getPageSpan().setMarginTop(lTopMargin.y()/72.0);
1104     getPageSpan().setMarginBottom(botMarg/72.0);
1105     getPageSpan().setMarginLeft(lTopMargin.x()/72.0);
1106     getPageSpan().setMarginRight(rightMarg/72.0);
1107     getPageSpan().setFormLength(paperSize.y()/72.);
1108     getPageSpan().setFormWidth(paperSize.x()/72.);
1109   }
1110   if (long(input->tell()) !=endPos) {
1111     input->seek(endPos, librevenge::RVNG_SEEK_SET);
1112     f << ", #endPos";
1113     asciiFile.addDelimiter(input->tell(), '|');
1114   }
1115 
1116   asciiFile.addPos(pos);
1117   asciiFile.addNote(f.str().c_str());
1118 
1119   return true;
1120 }
1121 
1122 ////////////////////////////////////////////////////////////
1123 // read the document zone data
readDocZoneData(FullWrtStruct::EntryPtr zone)1124 bool FullWrtParser::readDocZoneData(FullWrtStruct::EntryPtr zone)
1125 {
1126   MWAWInputStreamPtr input = zone->m_input;
1127   libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
1128   libmwaw::DebugStream f;
1129   //  int vers = version();
1130 
1131   long pos = zone->begin();
1132   input->seek(pos, librevenge::RVNG_SEEK_SET);
1133   zone->setParsed(true);
1134   f << "Entries(DZoneData)|" << *zone << ":";
1135   asciiFile.addPos(pos);
1136   asciiFile.addNote(f.str().c_str());
1137 
1138   int val, prevTypeOk=-1;
1139   // first try to read normally the zone
1140   size_t numDocTypes = m_state->m_docZoneList.size();
1141   for (size_t z = 1; z < m_state->m_docZoneList.size(); z++) {
1142     FullWrtParserInternal::DocZoneStruct const &doc = m_state->m_docZoneList[z];
1143     if (doc.m_type < 0) continue;
1144 
1145     pos = input->tell();
1146     if (pos+2 > zone->end()) break;
1147 
1148     bool done = false;
1149     for (int st = 0; st < 4; st++) {
1150       input->seek(pos+st, librevenge::RVNG_SEEK_SET);
1151       FullWrtStruct::ZoneHeader docData;
1152       std::shared_ptr<FullWrtStruct::ZoneHeader> res;
1153       docData.m_type = doc.m_type;
1154       switch (doc.m_type) {
1155       case 0:
1156         done = m_textParser->readColumns(zone);
1157         break;
1158       case 1:
1159         done = m_textParser->readParagraphTabs(zone, int(z));
1160         break;
1161       case 2:
1162         done = m_textParser->readItem(zone, int(z), doc.m_structType==4);
1163         break;
1164       case 3:
1165         done = m_textParser->readStyle(zone);
1166         break;
1167       case 4: {
1168         if (pos+st+4> zone->end()) break;
1169         f.str("");
1170         f << "Entries(DZone4):";
1171         int numOk = 0;
1172         for (int j = 0; j < 2; j++) { // always 0|0
1173           val = int(input->readLong(2));
1174           if (val >= -2 && val <= 0) numOk++;
1175           if (val) f << "f" << j << "=" << val << ",";
1176         }
1177         if (!numOk) break;
1178         asciiFile.addPos(pos+st);
1179         asciiFile.addNote(f.str().c_str());
1180         done = true;
1181         break;
1182       }
1183       case 6:
1184         if (pos+st+2> zone->end()) break;
1185         val = int(input->readULong(2));
1186         if (!val || val > 0x200) break;
1187         f.str("");
1188         f << "Entries(DZone6):" << doc << ",v=" << val;
1189         asciiFile.addPos(pos+st);
1190         asciiFile.addNote(f.str().c_str());
1191         done = true;
1192         break;
1193       case 0x13:
1194       case 0x14:
1195         res=m_graphParser->readSideBar(zone, docData);
1196         break;
1197       case 0x15:
1198         res=m_graphParser->readGraphicData(zone, docData);
1199         break;
1200       case 0x19: // reference data?
1201         done=readReferenceData(zone);
1202         break;
1203       case 0x1a: {
1204         if (pos+st+12> zone->end()) break;
1205         FullWrtParserInternal::ReferenceCalledData refData;
1206         refData.m_id = int(input->readULong(2));
1207         if (refData.m_id < 0 || refData.m_id >= int(numDocTypes) ||
1208             m_state->m_docZoneList[size_t(refData.m_id)].m_type != 0x19)
1209           break;
1210         // two small number and then f0=first id in refData, f1=RefValueId?
1211         // after 0 except one time f2=0x71
1212         for (auto &data : refData.m_values) data = int(input->readULong(2));
1213 
1214         f.str("");
1215         f << "Entries(RefCalled):docId=" << refData;
1216         if (m_state->m_referenceRedirectMap.find(int(z)) == m_state->m_referenceRedirectMap.end())
1217           m_state->m_referenceRedirectMap[int(z)]=refData;
1218         else {
1219           MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneData: oops, reference redirection already exists for docId=%d\n", int(z)));
1220         }
1221         asciiFile.addPos(pos+st);
1222         asciiFile.addNote(f.str().c_str());
1223         input->seek(pos+st+12, librevenge::RVNG_SEEK_SET);
1224         done = true;
1225         break;
1226       }
1227       case 0x1e:
1228         if (pos+st+2> zone->end()) break;
1229         val = int(input->readULong(2));
1230         if (val<=0 || val >= int(numDocTypes)) break;
1231 
1232         f.str("");
1233         // normally a type 15 or 18 zone
1234         f << "Entries(VariableData):docId=" << val
1235           << "[" << std::hex << m_state->m_docZoneList[size_t(val)].m_type
1236           << std::dec << "],";
1237         if (m_state->m_variableRedirectMap.find(int(z)) == m_state->m_variableRedirectMap.end())
1238           m_state->m_variableRedirectMap[int(z)]=val;
1239         else {
1240           MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneData: oops, variable redirection already exists for docId=%d\n", int(z)));
1241         }
1242         asciiFile.addPos(pos+st);
1243         asciiFile.addNote(f.str().c_str());
1244         done = true;
1245         break;
1246       case 0x1f:
1247         done = m_textParser->readDataMod(zone, int(z));
1248         break;
1249       default:
1250         done=doc.m_type<=0x18 && readGenericDocData(zone, docData);
1251         break;
1252       }
1253       if (res)
1254         done=true;
1255       if (done) {
1256         int docId=res ? res->m_docId : docData.m_docId;
1257         if (docId >= 0 && docId != int(z)) {
1258           MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneData: unexpected id %d != %d\n", docId, int(z)));
1259           done = false;
1260         }
1261         else {
1262           if (res)
1263             m_state->addCorrespondance(res->m_docId, res->m_fileId);
1264           break;
1265         }
1266       }
1267       input->seek(pos+st, librevenge::RVNG_SEEK_SET);
1268       if (input->readLong(1)) break;
1269     }
1270     if (done) {
1271       prevTypeOk = doc.m_type;
1272       continue;
1273     }
1274 
1275     f.str("");
1276     f << "Entries(DZoneData)##:" << doc;
1277     asciiFile.addPos(pos);
1278     asciiFile.addNote(f.str().c_str());
1279 
1280     MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneData: loose reading at zone %d[%d:%d]\n", int(z), doc.m_type, prevTypeOk));
1281     if (prevTypeOk != -1) prevTypeOk = -1;
1282     //
1283     input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1284     bool lastOk = false;
1285     while (input->tell()+4 < zone->end()) {
1286       bool prevLastOk = lastOk;
1287       lastOk = true;
1288       pos = input->tell();
1289       done=m_textParser->readParagraphTabs(zone)||m_textParser->readColumns(zone);
1290       if (done) continue;
1291       FullWrtStruct::ZoneHeader docData;
1292       done=docData.read(zone);
1293       if (done) {
1294         if (docData.m_docId >int(z) && docData.m_docId < int(m_state->m_docZoneList.size())) {
1295           z = size_t(docData.m_docId-1);
1296           input->seek(pos, librevenge::RVNG_SEEK_SET);
1297           MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneData: continue reading at zone %d\n", docData.m_docId));
1298           break;
1299         }
1300         continue;
1301       }
1302       if (prevLastOk) {
1303         asciiFile.addPos(pos);
1304         asciiFile.addNote("DZoneData##:");
1305       }
1306       lastOk = false;
1307       input->seek(pos+1, librevenge::RVNG_SEEK_SET);
1308     }
1309   }
1310 
1311   return true;
1312 }
1313 
readDocZoneStruct(FullWrtStruct::EntryPtr zone)1314 bool FullWrtParser::readDocZoneStruct(FullWrtStruct::EntryPtr zone)
1315 {
1316   MWAWInputStreamPtr input = zone->m_input;
1317   libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
1318   libmwaw::DebugStream f;
1319 
1320   long pos = zone->begin();
1321   input->seek(pos, librevenge::RVNG_SEEK_SET);
1322   auto N = static_cast<int>(input->readLong(2));
1323   if ((N & 0xF)||N<=0) return false;
1324   input->seek(pos+6, librevenge::RVNG_SEEK_SET);
1325   for (int i = 0; i < N-1; i++) {
1326     if (input->tell() >= zone->end())
1327       return false;
1328     long v = input->readLong(1);
1329     if (v==0) continue;
1330     if (v!=1 && v!=4) {
1331       if (2*i > N) {
1332         MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneStruct: find only %d/%d entries\n", i, N));
1333         break;
1334       }
1335       return false;
1336     }
1337     input->seek(4+v, librevenge::RVNG_SEEK_CUR);
1338   }
1339   if (input->tell() > zone->end())
1340     return false;
1341 
1342   zone->setParsed(true);
1343   f << "Entries(DZoneStruct)|" << *zone <<":";
1344   if (N%16) { // always a multiple of 16?
1345     MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneStruct: N(%d) seems odd\n", N));
1346     f << "###";
1347   }
1348   f << "N=" << N << ",";
1349   input->seek(pos+2, librevenge::RVNG_SEEK_SET);
1350   f << "unkn=" << std::hex << input->readULong(4) << std::dec << ",";
1351   asciiFile.addPos(pos);
1352   asciiFile.addNote(f.str().c_str());
1353   std::set<int> seenSet;
1354   auto &zoneList = m_state->m_docZoneList;
1355   zoneList.resize(size_t(N)+1);
1356   for (int i = 0; i < N-1; i++) {
1357     pos = input->tell();
1358     auto type = static_cast<int>(input->readULong(1));
1359     if (type > 1 && type!=4) {
1360       asciiFile.addPos(pos);
1361       asciiFile.addNote("DZoneStruct-###");
1362       break;
1363     }
1364 
1365     FullWrtParserInternal::DocZoneStruct dt;
1366     dt.m_structType = type;
1367     dt.m_pos = pos;
1368     f.str("");
1369     f << "DZoneStruct-" << i+1 << ":";
1370     if (type) {
1371       dt.m_type = static_cast<int>(input->readULong(1)); // small number between 0 and 1f
1372       dt.m_nextId = static_cast<int>(input->readLong(2));
1373       dt.m_fatherId = static_cast<int>(input->readLong(2));
1374       if (dt.m_nextId < 0 || dt.m_nextId > N) {
1375         f << "#nId=" << dt.m_nextId <<",";
1376         dt.m_nextId = 0;
1377       }
1378       if (dt.m_fatherId < 0 || dt.m_fatherId > N) {
1379         f << "#fId=" << dt.m_fatherId <<",";
1380         dt.m_fatherId = -1;
1381       }
1382       if (dt.m_nextId) {
1383         if (seenSet.find(dt.m_nextId) != seenSet.end()) {
1384           f << "##nId=" << dt.m_nextId << ",";
1385           dt.m_nextId = 0;
1386         }
1387         else
1388           seenSet.insert(dt.m_nextId);
1389       }
1390       if (type==4) {
1391         asciiFile.addDelimiter(input->tell(),'|');
1392         input->seek(3, librevenge::RVNG_SEEK_CUR);
1393       }
1394       f << dt;
1395     }
1396     zoneList[size_t(i)+1]=dt;
1397     asciiFile.addPos(pos);
1398     asciiFile.addNote(f.str().c_str());
1399   }
1400   if (input->tell() != zone->end()) {
1401     MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneStruct: end seems odd\n"));
1402   }
1403   // build child list
1404   for (size_t i = 0; i <= size_t(N); i++) {
1405     int fId = zoneList[i].m_fatherId;
1406     int nId = zoneList[i].m_nextId;
1407     if (nId && zoneList[size_t(nId)].m_fatherId != fId) {
1408       MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneStruct: find incoherent children: %d and %d\n", int(i), nId));
1409       continue;
1410     }
1411     auto cId = int(i);
1412     if (seenSet.find(int(cId))!=seenSet.end() || fId < 0) continue;
1413     // insert the child and its siblings
1414     while (cId > 0 && cId <= N) {
1415       zoneList[size_t(fId)].m_childList.push_back(cId);
1416       cId = zoneList[size_t(cId)].m_nextId;
1417     }
1418   }
1419   // check that we have no dag or cycle
1420   seenSet.clear();
1421   std::vector<int> toDoList;
1422   toDoList.push_back(0);
1423   seenSet.insert(0);
1424   while (toDoList.size()) {
1425     int id = toDoList.back();
1426     toDoList.pop_back();
1427 
1428     auto &nd = zoneList[size_t(id)];
1429     size_t c = 0;
1430     while (c < nd.m_childList.size()) {
1431       int cId = nd.m_childList[c++];
1432       if (seenSet.find(cId)==seenSet.end()) {
1433         seenSet.insert(cId);
1434         toDoList.push_back(cId);
1435         continue;
1436       }
1437       MWAW_DEBUG_MSG(("FullWrtParser::readDocZoneStruct: oops, find a unexpected dag or cycle\n"));
1438       c--;
1439       nd.m_childList.erase(nd.m_childList.begin()+int(c));
1440     }
1441   }
1442   for (auto const &nd : zoneList) {
1443     if (nd.m_childList.empty()) continue;
1444     f.str("");
1445     f << "childs=[";
1446     for (auto id : nd.m_childList)
1447       f << id << ",";
1448     f << "],";
1449     asciiFile.addPos(nd.m_pos >= 0 ? nd.m_pos : zone->begin());
1450     asciiFile.addNote(f.str().c_str());
1451   }
1452   zone->closeDebugFile();
1453   return true;
1454 }
1455 
1456 ////////////////////////////////////////////////////////////
1457 // read the correspondance data
readGenericDocData(FullWrtStruct::EntryPtr zone,FullWrtStruct::ZoneHeader & doc)1458 bool FullWrtParser::readGenericDocData(FullWrtStruct::EntryPtr zone, FullWrtStruct::ZoneHeader &doc)
1459 {
1460   MWAWInputStreamPtr input = zone->m_input;
1461   long pos = input->tell();
1462   if (!doc.read(zone)) {
1463     input->seek(pos, librevenge::RVNG_SEEK_SET);
1464     return false;
1465   }
1466 
1467   int const vers = version();
1468   libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
1469   libmwaw::DebugStream f;
1470 
1471   int val;
1472   int numSzFollowBlock = 0;
1473   switch (doc.m_type) {
1474   case 0xc:
1475   case 0xd:
1476   case 0xf:
1477   case 0x11:
1478   case 0x12:
1479   case 0x15:
1480     break;
1481   case 0xa:
1482   case 0xb:
1483   case 0xe:
1484   case 0x10:
1485   case 0x18:
1486     numSzFollowBlock = 1;
1487     break;
1488   case 0x13:
1489     numSzFollowBlock = 3;
1490     break;
1491   default:
1492     MWAW_DEBUG_MSG(("FullWrtParser::readGenericDocData: called with type=%d\n",doc.m_type));
1493     break;
1494   case -1:
1495     break;
1496   }
1497   if (input->tell()+1 > zone->end()) {
1498     input->seek(pos, librevenge::RVNG_SEEK_SET);
1499     return false;
1500   }
1501 
1502   f.str("");
1503   if (doc.m_type > 0)
1504     f << "Entries(DZone" << std::hex << doc.m_type << std::dec << "):";
1505   else
1506     f << "Entries(DZoneUnkn" << "):";
1507   f << doc;
1508   if (!m_state->addCorrespondance(doc.m_docId, doc.m_fileId))
1509     f << "#";
1510 
1511   asciiFile.addPos(pos);
1512   asciiFile.addNote(f.str().c_str());
1513 
1514   for (int i = 0; i < numSzFollowBlock; i++) {
1515     f.str("");
1516     f << "DZone" << std::hex << doc.m_type << std::dec << "[" << i << "]:";
1517     pos = input->tell();
1518     auto sz = long(input->readULong(4));
1519     if (sz < 0 || pos+sz+4 > zone->end()) {
1520       input->seek(pos, librevenge::RVNG_SEEK_SET);
1521       f << "#";
1522       asciiFile.addPos(pos);
1523       asciiFile.addNote(f.str().c_str());
1524       return true;
1525     }
1526     asciiFile.addPos(pos);
1527     asciiFile.addNote(f.str().c_str());
1528     if (sz) input->seek(sz, librevenge::RVNG_SEEK_CUR);
1529   }
1530 
1531   if (doc.m_type==0xa) {
1532     asciiFile.addPos(input->tell());
1533     asciiFile.addNote("DZonea[1]#");
1534     input->seek(vers==2 ? 8 : 66, librevenge::RVNG_SEEK_CUR);
1535   }
1536 
1537   val = int(input->readLong(1));
1538   if (doc.m_type==0xa) ;
1539   else if (val==1) {
1540     pos = input->tell();
1541     auto sz = long(input->readULong(4));
1542     if (sz && input->tell()+sz <= zone->end()) {
1543       f.str("");
1544       f << "DZone" << std::hex << doc.m_type << std::dec << "[end]:";
1545       asciiFile.addPos(pos);
1546       asciiFile.addNote(f.str().c_str());
1547       input->seek(sz, librevenge::RVNG_SEEK_CUR);
1548     }
1549     else {
1550       MWAW_DEBUG_MSG(("FullWrtParser::readGenericDocData: find bad end data\n"));
1551       input->seek(pos, librevenge::RVNG_SEEK_SET);
1552     }
1553   }
1554   else if (val) {
1555     MWAW_DEBUG_MSG(("FullWrtParser::readGenericDocData: find bad end data(II)\n"));
1556   }
1557   return true;
1558 }
1559 
readReferenceData(FullWrtStruct::EntryPtr zone)1560 bool FullWrtParser::readReferenceData(FullWrtStruct::EntryPtr zone)
1561 {
1562   MWAWInputStreamPtr input = zone->m_input;
1563   long pos = input->tell();
1564   if (pos+22 > zone->end()) {
1565     input->seek(pos, librevenge::RVNG_SEEK_SET);
1566     return false;
1567   }
1568 
1569   libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
1570   libmwaw::DebugStream f;
1571   f.str("");
1572   f << "Entries(RefData):";
1573   long val = int(input->readULong(2));
1574   int numOk = 0;
1575   if (val == 0xa || val == 0xc) numOk++;
1576   f << "type?=" << val << ",";
1577 
1578   f << "unkn=["; // 3 small number and 0, f0 or f2 is probably an Id, f1=0|1|2
1579   for (int i = 0; i < 4; i++) {
1580     val = long(input->readULong(2));
1581     if (val)
1582       f << val << ",";
1583     else
1584       f << "_,";
1585     if (i==3) break;
1586     if (val>0 && val < 0x100) numOk++;
1587   }
1588   f << "],";
1589   if (numOk <= 2) {
1590     input->seek(pos, librevenge::RVNG_SEEK_SET);
1591     return false;
1592   }
1593   f << "ptr=" << std::hex << input->readULong(4) << std::dec << ",";
1594   for (int i = 0; i < 2; i++) { // always 0 ?
1595     val = long(input->readULong(2));
1596     if (val)
1597       f << "f" << i << "=" << val << ",";
1598   }
1599 
1600   long sz = input->readLong(4);
1601   if (sz < 0 || pos+22+sz > zone->end()) {
1602     input->seek(pos, librevenge::RVNG_SEEK_SET);
1603     return false;
1604   }
1605   auto numZones=int(m_state->m_docZoneList.size());
1606   f << "callerId=["; // normally zone of type 0x1a
1607   for (long i = 0; i < sz/2; i++) {
1608     auto id = int(input->readLong(2));
1609     if (id < 0 || id >= numZones ||
1610         m_state->m_docZoneList[size_t(id)].m_type != 0x1a)
1611       f << "#";
1612     f << id << ",";
1613   }
1614   f << "],";
1615   input->seek(pos+22+sz, librevenge::RVNG_SEEK_SET);
1616   asciiFile.addPos(pos);
1617   asciiFile.addNote(f.str().c_str());
1618   return true;
1619 }
1620 
1621 ////////////////////////////////////////////////////////////
1622 // read the zone flag and positions
1623 ////////////////////////////////////////////////////////////
readFileZoneFlags(FullWrtStruct::EntryPtr zone)1624 bool FullWrtParser::readFileZoneFlags(FullWrtStruct::EntryPtr zone)
1625 {
1626   int vers = version();
1627   int dataSz = vers==1 ? 22 : 16;
1628   if (!zone || zone->length()%dataSz) {
1629     MWAW_DEBUG_MSG(("FullWrtParser::readFileZoneFlags: size seems odd\n"));
1630     return false;
1631   }
1632   zone->setParsed(true);
1633   MWAWInputStreamPtr input = zone->m_input;
1634   libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
1635 
1636   libmwaw::DebugStream f;
1637   long numElt = zone->length()/dataSz;
1638   input->seek(zone->begin(), librevenge::RVNG_SEEK_SET);
1639   std::multimap<int, FullWrtStruct::EntryPtr >::iterator it;
1640   int numNegZone=3;
1641   for (long i = 0; i < numElt; i++) {
1642     long pos = input->tell();
1643     auto id = static_cast<int>(input->readLong(2));
1644     it = m_state->m_entryMap.find(id);
1645     FullWrtStruct::EntryPtr entry;
1646     f.str("");
1647     if (it == m_state->m_entryMap.end()) {
1648       if (id != -2) {
1649         MWAW_DEBUG_MSG(("FullWrtParser::readFileZoneFlags: can not find entry %d\n",id));
1650         f << "###";
1651       }
1652       entry.reset(new FullWrtStruct::Entry(input));
1653       entry->setId(1000+id); // false id
1654     }
1655     else
1656       entry = it->second;
1657     entry->setType("UnknownZone");
1658     auto val = static_cast<int>(input->readLong(2)); // always -2 ?
1659     if (val != -2) f << "g0=" << val << ",";
1660     val  = static_cast<int>(input->readLong(2)); // always 0 ?
1661     if (val) f << "g1=" << val << ",";
1662     // a small number between 1 and 0x100
1663     entry->m_values[0] = static_cast<int>(input->readLong(2));
1664     for (int j = 0; j < 2; j++) { // always 0
1665       val  = static_cast<int>(input->readLong(2));
1666       if (val) f << "g" << j+2 << "=" << val << ",";
1667     }
1668     /** -1: generic, -2: null, other fId */
1669     entry->m_typeId = static_cast<int>(input->readLong(2));
1670     if (entry->m_typeId == -2) ;
1671     else if (entry->m_typeId != -1)
1672       entry->setId(int(i));
1673     else {
1674       bool find = false;
1675       for (int j = 0; j < 3; j++) {
1676         if (i!=m_state->m_zoneFlagsId[j]) continue;
1677         find = true;
1678         entry->setId(j);
1679         break;
1680       }
1681       if (!find) {
1682         MWAW_DEBUG_MSG(("FullWrtParser::readFileZoneFlags: can not find generic zone id %ld\n",i));
1683         f << "#";
1684         entry->setId(numNegZone);
1685       }
1686       numNegZone++;
1687     }
1688     // v2: always  0|0x14, v1 two small number or 0x7FFF
1689     entry->m_values[1] = static_cast<int>(input->readLong(1));
1690     entry->m_values[2] = static_cast<int>(input->readLong(1));
1691     if (vers == 1) {
1692       for (int j = 0; j < 3; j++) { // always 0, -2|0, 0 ?
1693         val  = static_cast<int>(input->readLong(2));
1694         if ((j==1 && val !=-2) || (j!=1 && val))
1695           f << "g" << j+4 << "=" << val << ",";
1696       }
1697     }
1698 
1699     std::string extra = f.str();
1700     f.str("");
1701     if (i==0) f << "Entries(FZoneFlags):";
1702     else f << "FZoneFlags-" << i << ":";
1703     f << *entry << ",";
1704     f << extra;
1705 
1706     if (entry->id() < 0) {
1707       if (entry->m_typeId != -2) {
1708         MWAW_DEBUG_MSG(("FullWrtParser::readFileZoneFlags: find a null zone with unexpected type\n"));
1709       }
1710     }
1711 
1712     input->seek(pos+dataSz, librevenge::RVNG_SEEK_SET);
1713     asciiFile.addPos(pos);
1714     asciiFile.addNote(f.str().c_str());
1715   }
1716   asciiFile.addPos(zone->end());
1717   asciiFile.addNote("Entries(ZoneAfter)");
1718   return true;
1719 }
1720 
readFileZonePos(FullWrtStruct::EntryPtr zone)1721 bool FullWrtParser::readFileZonePos(FullWrtStruct::EntryPtr zone)
1722 {
1723   int vers = version();
1724   int dataSz = vers==1 ? 10 : 8;
1725   if (zone->length()%dataSz) {
1726     MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: size seems odd\n"));
1727     return false;
1728   }
1729   zone->setParsed(true);
1730   MWAWInputStreamPtr input = zone->m_input;
1731   libmwaw::DebugFile &asciiFile = zone->getAsciiFile();
1732 
1733   libmwaw::DebugStream f;
1734   auto numElt = int(zone->length()/dataSz);
1735   input->seek(zone->begin(), librevenge::RVNG_SEEK_SET);
1736   int val;
1737 
1738   // read data
1739   std::set<long> filePositions;
1740   std::vector<FullWrtStruct::EntryPtr > listEntry;
1741   if (numElt>0)
1742     listEntry.resize(size_t(numElt));
1743   for (int i = 0; i < numElt; i++) {
1744     long pos = input->tell();
1745     long fPos = input->readLong(4);
1746 
1747     FullWrtStruct::EntryPtr entry(new FullWrtStruct::Entry(input));
1748     if (i == m_state->m_biblioId)
1749       entry->setType("Biblio");
1750     else
1751       entry->setType("Unknown");
1752     entry->m_nextId = static_cast<int>(input->readLong(2));
1753     auto id = static_cast<int>(input->readLong(2));
1754     f << "realId=" << id << ",";
1755     entry->setId(i);
1756     if (fPos >= 0) {
1757       filePositions.insert(fPos);
1758       entry->setBegin(fPos);
1759     }
1760     f.str("");
1761 
1762     if (entry->begin()>=0)
1763       f << "pos=" << std::hex << entry->begin() << std::dec << ",";
1764     if (entry->m_nextId != -2) f << "nextId=" << entry->m_nextId << ",";
1765     if (vers==1) {
1766       val = static_cast<int>(input->readLong(2));
1767       if (val) f << "f0=" << val << ",";
1768     }
1769 
1770     input->seek(pos+dataSz, librevenge::RVNG_SEEK_SET);
1771 
1772     entry->setExtra(f.str());
1773     f.str("");
1774     if (i == 0) f << "Entries(FZonePos):";
1775     else f << "FZonePos" << i << ":";
1776     f << *entry;
1777 
1778     asciiFile.addPos(pos);
1779     asciiFile.addNote(f.str().c_str());
1780 
1781     if (id != -2 && (id < 1 || id >= numElt)) {
1782       MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: entry id seems bad\n"));
1783     }
1784     listEntry[size_t(i)] = entry;
1785   }
1786   filePositions.insert(zone->begin());
1787 
1788   // compute end of each entry
1789   for (auto entry : listEntry) {
1790     if (!entry || entry->begin() < 0)
1791       continue;
1792     auto it=filePositions.find(entry->begin());
1793     if (it == filePositions.end()) {
1794       MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: can not find my entry\n"));
1795       continue;
1796     }
1797     if (++it == filePositions.end()) {
1798       MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: can not find my entry\n"));
1799       continue;
1800     }
1801 
1802     entry->setEnd(*it);
1803     if (entry->m_nextId < 0) continue;
1804     if (entry->m_nextId >= numElt) {
1805       entry->m_nextId = -1;
1806       MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos: can not find the next entry\n"));
1807       continue;
1808     }
1809     if (!listEntry[size_t(entry->m_nextId)] || listEntry[size_t(entry->m_nextId)]->isParsed()) {
1810       entry->m_nextId = -1;
1811       MWAW_DEBUG_MSG(("FullWrtParser::readFileZonePos:  next entry %d is already used\n",
1812                       entry->m_nextId));
1813       continue;
1814     }
1815     listEntry[size_t(entry->m_nextId)]->setParsed(true);
1816   }
1817 
1818   for (int i = 0; i < numElt; i++) {
1819     auto entry = listEntry[size_t(i)];
1820     if (!entry || !entry->valid() || entry->isParsed()) continue;
1821 
1822     m_state->m_entryMap.insert
1823     (std::multimap<int, FullWrtStruct::EntryPtr >::value_type(i, entry));
1824 
1825     if (entry->m_nextId < 0) {
1826       entry->m_input = input;
1827       entry->m_asciiFile = std::shared_ptr<libmwaw::DebugFile>
1828                            (&asciiFile, MWAW_shared_ptr_noop_deleter<libmwaw::DebugFile>());
1829       continue;
1830     }
1831     // ok we must reconstruct a file
1832     FullWrtStruct::EntryPtr actEnt = entry;
1833     librevenge::RVNGBinaryData &data = entry->m_data;
1834     while (1) {
1835       if (!actEnt->valid()) break;
1836       input->seek(actEnt->begin(), librevenge::RVNG_SEEK_SET);
1837       unsigned long read;
1838       const unsigned char *dt = input->read(size_t(actEnt->length()), read);
1839       data.append(dt, read);
1840       asciiFile.skipZone(actEnt->begin(), actEnt->end()-1);
1841       if (actEnt->m_nextId < 0) break;
1842       actEnt = listEntry[size_t(actEnt->m_nextId)];
1843       if (actEnt) actEnt->setParsed(true);
1844     }
1845     entry->update();
1846   }
1847 
1848   asciiFile.addPos(zone->end());
1849   asciiFile.addNote("_");
1850   return true;
1851 }
1852 
1853 ////////////////////////////////////////////////////////////
1854 // read the document header
1855 ////////////////////////////////////////////////////////////
readDocPosition()1856 bool FullWrtParser::readDocPosition()
1857 {
1858   MWAWInputStreamPtr input = getInput();
1859   if (!input->checkPosition(48))
1860     return false;
1861 
1862   libmwaw::DebugStream f;
1863   input->seek(-48, librevenge::RVNG_SEEK_END);
1864   long pos = input->tell();
1865   f << "Entries(DocPosition):";
1866 
1867   long val;
1868   m_state->m_biblioId = static_cast<int>(input->readLong(2));
1869   if (m_state->m_biblioId != -2)
1870     f << "bibId=" << m_state->m_biblioId << ",";
1871   for (int i = 0; i < 4; i++) { // always 0?
1872     val = input->readLong(2);
1873     if (val) f << "f" << i << "=" << val <<",";
1874   }
1875   long sz[2];
1876   for (int i = 0; i < 2; i++) {
1877     FullWrtStruct::EntryPtr zone(new FullWrtStruct::Entry(input));
1878     zone->m_asciiFile = std::shared_ptr<libmwaw::DebugFile>
1879                         (&ascii(), MWAW_shared_ptr_noop_deleter<libmwaw::DebugFile>());
1880     zone->setBegin(long(input->readULong(4)));
1881     zone->setLength((sz[i]=long(input->readULong(4))));
1882     if (!input->checkPosition(zone->end()) || !zone->valid())
1883       return false;
1884     if (i == 1) m_state->m_fileZoneList = zone;
1885     else m_state->m_fileZoneFlagsList = zone;
1886   }
1887   f << "flZones=[";
1888   for (int i = 0; i < 3; i++) {
1889     m_state->m_zoneFlagsId[2-i] = static_cast<int>(input->readLong(2));
1890     f << m_state->m_zoneFlagsId[2-i] << ",";
1891   }
1892   f << "],";
1893   val = input->readLong(2); // always 0 ?
1894   if (val) f << "g0=" << val << ",";
1895   // a big number
1896   f << std::hex << "unkn=" << input->readULong(2) << std::dec << ",";
1897   val = long(input->readULong(4));
1898   if (val != 1 && val != 0xbeecf54L) // always 1 in v1 and 0xbeecf54L in v2 ?
1899     f << std::hex << "unkn2=" << val << std::dec << ",";
1900   // always 1 ?
1901   val = long(input->readULong(4));
1902   if (val != 1) // always 1 in v1
1903     f << "g1=" << val << ",";
1904   val = long(input->readULong(4));
1905   if (val==0x46575254L) {
1906     if ((sz[0]%16)==0 && (sz[1]%8)==0)
1907       setVersion(2);
1908     else if ((sz[0]%22)==0 && (sz[1]%10)==0)
1909       setVersion(1);
1910     else
1911       return false;
1912   }
1913   else {
1914     if (val != 1) f << "g2=" << val << ",";
1915     if ((sz[0]%22)==0 && (sz[1]%10)==0)
1916       setVersion(1);
1917     else
1918       return false;
1919   }
1920   ascii().addPos(pos);
1921   ascii().addNote(f.str().c_str());
1922 
1923   return true;
1924 }
1925 
1926 ////////////////////////////////////////////////////////////
1927 // the variable part
1928 ////////////////////////////////////////////////////////////
sendReference(int id)1929 void FullWrtParser::sendReference(int id)
1930 {
1931   if (!getTextListener()) return;
1932 
1933   if (id < 0 || id >= int(m_state->m_docZoneList.size())) {
1934     MWAW_DEBUG_MSG(("FullWrtParser::sendReference: can not find data for id=%d\n", id));
1935     return;
1936   }
1937   if (m_state->m_docZoneList[size_t(id)].m_type != 0x1a) {
1938     MWAW_DEBUG_MSG(("FullWrtParser::sendReference: find unexpected type for fieldDataRedirect=%x\n", static_cast<unsigned int>(m_state->m_docZoneList[size_t(id)].m_type)));
1939     return;
1940   }
1941   if (m_state->m_referenceRedirectMap.find(id) == m_state->m_referenceRedirectMap.end())
1942     return; // ok, we have not read the reference
1943   int docId = m_state->m_referenceRedirectMap.find(id)->second.m_id;
1944   if (docId < 0 || docId >= int(m_state->m_docZoneList.size()) ||
1945       m_state->m_docZoneList[size_t(docId)].m_type != 0x19) {
1946     MWAW_DEBUG_MSG(("FullWrtParser::sendReference: find unexpected redirection id[%d] for reference %d\n", docId, id));
1947     return;
1948   }
1949   static bool first = true;
1950   if (first) {
1951     first = false;
1952     MWAW_DEBUG_MSG(("FullWrtParser::sendReference: sorry, this function is not implemented\n"));
1953   }
1954 }
1955 
1956 ////////////////////////////////////////////////////////////
1957 // the variable part
1958 ////////////////////////////////////////////////////////////
sendVariable(int id)1959 void FullWrtParser::sendVariable(int id)
1960 {
1961   if (!getTextListener()) return;
1962 
1963   if (id < 0 || id >= int(m_state->m_docZoneList.size())) {
1964     MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: can not find data for id=%d\n", id));
1965     return;
1966   }
1967   if (m_state->m_docZoneList[size_t(id)].m_type != 0x1e) {
1968     MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: find unexpected type for fieldDataRedirect=%x\n", static_cast<unsigned int>(m_state->m_docZoneList[size_t(id)].m_type)));
1969     return;
1970   }
1971   if (m_state->m_variableRedirectMap.find(id) == m_state->m_variableRedirectMap.end()) {
1972     MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: can not find redirection for id=%d\n", id));
1973     return;
1974   }
1975   int docId = m_state->m_variableRedirectMap.find(id)->second;
1976   if (docId < 0 || docId >= int(m_state->m_docZoneList.size())) {
1977     MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: find unexpected redirection id[%d] for variable %d\n", docId, id));
1978     return;
1979   }
1980   auto const &data = m_state->m_docZoneList[size_t(docId)];
1981   if (data.m_type==0x15)
1982     sendGraphic(docId);
1983   else if (data.m_type == 0x18) {
1984     /** in this case, the content seems to be a textbox which contains the field display,
1985         but as in general this zone is not read correctly (ie. the field is not found ) and
1986         as sending textbox is not implemented, better to stop here...
1987      */
1988     static bool first = true;
1989     if (first) {
1990       first = false;
1991       MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: sorry, send text/field variable is not implemented\n"));
1992     }
1993   }
1994   else {
1995     MWAW_DEBUG_MSG(("FullWrtParser::sendVariable: find unexpected redirection type[%x] for variable %d\n", static_cast<unsigned int>(data.m_type), id));
1996   }
1997 }
1998 
1999 ////////////////////////////////////////////////////////////
2000 // send a text zone
2001 ////////////////////////////////////////////////////////////
sendText(int id,libmwaw::SubDocumentType type,MWAWNote::Type wh)2002 void FullWrtParser::sendText(int id, libmwaw::SubDocumentType type, MWAWNote::Type wh)
2003 {
2004   if (!getTextListener()) return;
2005 
2006   if (id >= 0 && id < int(m_state->m_docZoneList.size())) {
2007     auto const &data = m_state->m_docZoneList[size_t(id)];
2008     int docType = data.m_type;
2009     if (type==libmwaw::DOC_NOTE && (docType==0xc|| docType==0xd)) ;
2010     else if (type == libmwaw::DOC_COMMENT_ANNOTATION && docType == 0xb) ;
2011     else {
2012       MWAW_DEBUG_MSG(("FullWrtParser::sendText: call with %d[%x]\n", int(type),static_cast<unsigned int>(docType)));
2013     }
2014   }
2015   else {
2016     MWAW_DEBUG_MSG(("FullWrtParser::sendText: can not find data for id=%d\n", id));
2017   }
2018   int fId = m_state->getFileZoneId(id);
2019   MWAWSubDocumentPtr subdoc(new FullWrtParserInternal::SubDocument(*this, getInput(), fId));
2020   if (type==libmwaw::DOC_NOTE)
2021     getTextListener()->insertNote(MWAWNote(wh), subdoc);
2022   else if (type==libmwaw::DOC_COMMENT_ANNOTATION)
2023     getTextListener()->insertComment(subdoc);
2024   else {
2025     MWAW_DEBUG_MSG(("FullWrtParser::sendText: unexpected type\n"));
2026   }
2027 }
2028 
2029 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
2030