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 <cstring>
35 #include <iomanip>
36 #include <iostream>
37 #include <limits>
38 #include <map>
39 #include <set>
40 #include <sstream>
41 
42 #include <librevenge/librevenge.h>
43 
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 "MWAWSubDocument.hxx"
52 
53 #include "MsWrdParser.hxx"
54 
55 #include "MsWrdText.hxx"
56 
57 /** Internal: the structures of a MsWrdParser */
58 namespace MsWrdParserInternal
59 {
60 ////////////////////////////////////////
61 //! Internal: the object of MsWrdParser
62 struct Object {
ObjectMsWrdParserInternal::Object63   Object()
64     : m_textPos(-1)
65     , m_pos()
66     , m_name("")
67     , m_id(-1)
68     , m_annotation()
69     , m_extra("")
70   {
71     for (auto &id : m_ids) id=-1;
72     for (auto &idFlag : m_idsFlag) idFlag=0;
73     for (auto &flag : m_flags) flag=0;
74   }
75 
getEntryMsWrdParserInternal::Object76   MsWrdEntry getEntry() const
77   {
78     MsWrdEntry res;
79     res.setBegin(m_pos.begin());
80     res.setEnd(m_pos.end());
81     res.setType("ObjectData");
82     res.setId(m_id);
83     return res;
84   }
85 
86   //! operator<<
operator <<(std::ostream & o,Object const & obj)87   friend std::ostream &operator<<(std::ostream &o, Object const &obj)
88   {
89     if (obj.m_textPos >= 0)
90       o << std::hex << "textPos?=" << obj.m_textPos << std::dec << ",";
91     if (obj.m_id >= 0) o << "Obj" << obj.m_id << ",";
92     if (obj.m_name.length()) o << obj.m_name << ",";
93     for (int st = 0; st < 2; st++) {
94       if (obj.m_ids[st] == -1 && obj.m_idsFlag[st] == 0) continue;
95       o << "id" << st << "=" << obj.m_ids[st];
96       if (obj.m_idsFlag[st]) o << ":" << std::hex << obj.m_idsFlag[st] << std::dec << ",";
97     }
98     for (int st = 0; st < 2; st++) {
99       if (obj.m_flags[st])
100         o << "fl" << st << "=" << std::hex << obj.m_flags[st] << std::dec << ",";
101     }
102 
103     if (obj.m_extra.length()) o << "extras=[" << obj.m_extra << "],";
104     return o;
105   }
106   //! the text position
107   long m_textPos;
108 
109   //! the object entry
110   MWAWEntry m_pos;
111 
112   //! the object name
113   std::string m_name;
114 
115   //! the id
116   int m_id;
117 
118   //! some others id?
119   int m_ids[2];
120 
121   //! some flags link to m_ids
122   int m_idsFlag[2];
123 
124   //! some flags
125   int m_flags[2];
126 
127   //! the annotation entry
128   MWAWEntry m_annotation;
129 
130   //! some extra data
131   std::string m_extra;
132 };
133 
134 ////////////////////////////////////////
135 //! Internal: the picture of a MsWrdParser
136 struct Picture {
137   struct Zone;
PictureMsWrdParserInternal::Picture138   Picture()
139     : m_dim()
140     , m_picturesList()
141     , m_flag(0)
142   {
143   }
144   //! operator<<
operator <<(std::ostream & o,Picture const & pict)145   friend std::ostream &operator<<(std::ostream &o, Picture const &pict)
146   {
147     o << "dim=" << pict.m_dim << ",";
148     if (pict.m_flag) o << "f0=" << std::hex << pict.m_flag << std::dec << ",";
149     return o;
150   }
151 
152   //! the dimension
153   MWAWBox2i m_dim;
154   //! the list of picture
155   std::vector<Zone> m_picturesList;
156   //! an unknown flag
157   int m_flag;
158 
159   // ! a small zone
160   struct Zone {
ZoneMsWrdParserInternal::Picture::Zone161     Zone()
162       : m_pos()
163       , m_dim()
164     {
165       for (auto &fl : m_flags) fl=0;
166     }
167     //! operator<<
operator <<MsWrdParserInternal::Picture168     friend std::ostream &operator<<(std::ostream &o, Zone const &pict)
169     {
170       o << "dim=" << pict.m_dim << ",";
171       if (pict.m_flags[0] != 8) o << "f0=" << pict.m_flags[0] << ",";
172       if (pict.m_flags[1]) o << "f1=" << pict.m_flags[1] << ",";
173       if (pict.m_flags[2] != 1) o << "f2=" << pict.m_flags[2] << ","; // or 0
174       return o;
175     }
176     //! the position in file
177     MWAWEntry m_pos;
178     //! the dimension
179     MWAWBox2i m_dim;
180     //! three unknown flags
181     int m_flags[3];
182   };
183 
184 };
185 
186 ////////////////////////////////////////
187 //! Internal: the state of a MsWrdParser
188 struct State {
189   //! constructor
StateMsWrdParserInternal::State190   State()
191     : m_bot(-1)
192     , m_eot(-1)
193     , m_endNote(false)
194     , m_picturesMap()
195     , m_posToCommentMap()
196     , m_actPage(0)
197     , m_numPages(0)
198     , m_headersId()
199     , m_footersId()
200     , m_metaData()
201   {
202   }
203 
204   //! the begin of the text
205   long m_bot;
206   //! end of the text
207   long m_eot;
208   //! a flag to know if we must place the note at the end or in the foot part
209   bool m_endNote;
210   //! the map filePos -> Picture
211   std::map<long, Picture> m_picturesMap;
212   //! the map textPos -> comment entry
213   std::map<long,MWAWEntry> m_posToCommentMap;
214 
215   //! the list of object ( mainZone, other zone)
216   std::vector<Object> m_objectList[2];
217 
218   int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
219 
220   /** the list of header id which corresponds to each page */
221   std::vector<int> m_headersId;
222   /** the list of footer id which corresponds to each page */
223   std::vector<int> m_footersId;
224   /** the meta data */
225   librevenge::RVNGPropertyList m_metaData;
226 };
227 
228 ////////////////////////////////////////
229 //! Internal: the subdocument of a MsWrdParser
230 class SubDocument final : public MWAWSubDocument
231 {
232 public:
233   //! constructor for footnote, comment
SubDocument(MsWrdParser & pars,MWAWInputStreamPtr const & input,int id,libmwaw::SubDocumentType type)234   SubDocument(MsWrdParser &pars, MWAWInputStreamPtr const &input, int id, libmwaw::SubDocumentType type)
235     : MWAWSubDocument(&pars, input, MWAWEntry())
236     , m_id(id)
237     , m_type(type)
238     , m_pictFPos(-1)
239     , m_pictCPos(-1)
240   {
241   }
242   //! constructor for header/footer
SubDocument(MsWrdParser & pars,MWAWInputStreamPtr const & input,MWAWEntry const & entry,libmwaw::SubDocumentType type)243   SubDocument(MsWrdParser &pars, MWAWInputStreamPtr const &input, MWAWEntry const &entry, libmwaw::SubDocumentType type)
244     : MWAWSubDocument(&pars, input, entry)
245     , m_id(-1)
246     , m_type(type)
247     , m_pictFPos(-1)
248     , m_pictCPos(-1)
249   {
250   }
251   //! constructor for picture
SubDocument(MsWrdParser & pars,MWAWInputStreamPtr const & input,long fPos,int cPos)252   SubDocument(MsWrdParser &pars, MWAWInputStreamPtr const &input, long fPos, int cPos)
253     : MWAWSubDocument(&pars, input, MWAWEntry())
254     , m_id(-1)
255     , m_type(libmwaw::DOC_NONE)
256     , m_pictFPos(fPos)
257     , m_pictCPos(cPos)
258   {
259   }
260 
261   //! destructor
~SubDocument()262   ~SubDocument() final {}
263 
264   //! operator!=
265   bool operator!=(MWAWSubDocument const &doc) const final;
266 
267   //! the parser function
268   void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
269 
270 protected:
271   //! the subdocument id
272   int m_id;
273   //! the subdocument type
274   libmwaw::SubDocumentType m_type;
275   //! the picture file position
276   long m_pictFPos;
277   //! the picture char position
278   int m_pictCPos;
279 };
280 
parse(MWAWListenerPtr & listener,libmwaw::SubDocumentType type)281 void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type)
282 {
283   if (!listener.get()) {
284     MWAW_DEBUG_MSG(("MsWrdParserInternal::SubDocument::parse: no listener\n"));
285     return;
286   }
287   auto *parser=dynamic_cast<MsWrdParser *>(m_parser);
288   if (!parser) {
289     MWAW_DEBUG_MSG(("MsWrdParserInternal::SubDocument::parse: no parser\n"));
290     return;
291   }
292 
293   long pos = m_input->tell();
294   if (m_type == libmwaw::DOC_NONE && m_pictCPos >= 0 && m_pictFPos > 0)
295     parser->sendPicture(m_pictFPos, m_pictCPos, MWAWPosition::Frame);
296   else if (m_type == libmwaw::DOC_HEADER_FOOTER)
297     parser->send(m_zone);
298   else if (m_type == libmwaw::DOC_COMMENT_ANNOTATION)
299     parser->sendSimpleTextZone(listener, m_zone);
300   else
301     parser->send(m_id, type);
302   m_input->seek(pos, librevenge::RVNG_SEEK_SET);
303 }
304 
operator !=(MWAWSubDocument const & doc) const305 bool SubDocument::operator!=(MWAWSubDocument const &doc) const
306 {
307   if (MWAWSubDocument::operator!=(doc)) return true;
308   auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
309   if (!sDoc) return true;
310   if (m_id != sDoc->m_id) return true;
311   if (m_type != sDoc->m_type) return true;
312   if (m_pictFPos != sDoc->m_pictFPos) return true;
313   if (m_pictCPos != sDoc->m_pictCPos) return true;
314   return false;
315 }
316 }
317 
318 ////////////////////////////////////////////////////////////
319 // MsWrdEntry
320 ////////////////////////////////////////////////////////////
~MsWrdEntry()321 MsWrdEntry::~MsWrdEntry()
322 {
323 }
324 
operator <<(std::ostream & o,MsWrdEntry const & entry)325 std::ostream &operator<<(std::ostream &o, MsWrdEntry const &entry)
326 {
327   if (entry.type().length()) {
328     o << entry.type();
329     if (entry.m_id >= 0) o << "[" << entry.m_id << "]";
330     o << "=";
331   }
332   return o;
333 }
334 
335 ////////////////////////////////////////////////////////////
336 // constructor/destructor, ...
337 ////////////////////////////////////////////////////////////
MsWrdParser(MWAWInputStreamPtr const & input,MWAWRSRCParserPtr const & rsrcParser,MWAWHeader * header)338 MsWrdParser::MsWrdParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
339   : MWAWTextParser(input, rsrcParser, header)
340   , m_state()
341   , m_entryMap()
342   , m_textParser()
343 {
344   init();
345 }
346 
~MsWrdParser()347 MsWrdParser::~MsWrdParser()
348 {
349 }
350 
init()351 void MsWrdParser::init()
352 {
353   resetTextListener();
354   setAsciiName("main-1");
355 
356   m_state.reset(new MsWrdParserInternal::State);
357 
358   // reduce the margin (in case, the page is not defined)
359   getPageSpan().setMargins(0.1);
360 
361   m_textParser.reset(new MsWrdText(*this));
362 }
363 
364 ////////////////////////////////////////////////////////////
365 // new page and color
366 ////////////////////////////////////////////////////////////
newPage(int number)367 void MsWrdParser::newPage(int number)
368 {
369   if (number <= m_state->m_actPage || number > m_state->m_numPages)
370     return;
371 
372   while (m_state->m_actPage < number) {
373     m_state->m_actPage++;
374     if (!getTextListener() || m_state->m_actPage == 1)
375       continue;
376     getTextListener()->insertBreak(MWAWTextListener::PageBreak);
377   }
378 }
379 
getColor(int id,MWAWColor & col) const380 bool MsWrdParser::getColor(int id, MWAWColor &col) const
381 {
382   switch (id) {
383   case 0:
384     col=MWAWColor(0,0,0);
385     break; // black
386   case 1:
387     col=MWAWColor(0,0,255);
388     break; // blue
389   case 2:
390     col=MWAWColor(0, 255,255);
391     break; // cyan
392   case 3:
393     col=MWAWColor(0,255,0);
394     break; // green
395   case 4:
396     col=MWAWColor(255,0,255);
397     break; // magenta
398   case 5:
399     col=MWAWColor(255,0,0);
400     break; // red
401   case 6:
402     col=MWAWColor(255,255,0);
403     break; // yellow
404   case 7:
405     col=MWAWColor(255,255,255);
406     break; // white
407   default:
408     MWAW_DEBUG_MSG(("MsWrdParser::getColor: unknown color=%d\n", id));
409     return false;
410   }
411   return true;
412 }
413 
sendSimpleTextZone(MWAWListenerPtr & listener,MWAWEntry const & entry)414 void MsWrdParser::sendSimpleTextZone(MWAWListenerPtr &listener, MWAWEntry const &entry)
415 {
416   if (!listener || !entry.valid()) return;
417   auto input=getInput();
418   if (input->size()<entry.end()) {
419     MWAW_DEBUG_MSG(("MsWrdParser::sendSimpleTextZone: entry seems bad\n"));
420     return;
421   }
422   long pos=input->tell();
423 
424   input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
425   for (long i=0; i<entry.length(); ++i) {
426     char c=char(input->readULong(1));
427     switch (c) {
428     case 0x9:
429       listener->insertTab();
430       break;
431     case 0xd: // line break hard
432       if (i+1!=entry.length())
433         listener->insertEOL();
434       break;
435     default: // asume basic caracter, ie. will not works if Chinese, ...
436       listener->insertCharacter(static_cast<unsigned char>(c));
437       break;
438     }
439   }
440   input->seek(pos, librevenge::RVNG_SEEK_SET);
441 }
442 
sendFootnote(int id)443 void MsWrdParser::sendFootnote(int id)
444 {
445   if (!getTextListener()) return;
446 
447   MWAWSubDocumentPtr subdoc(new MsWrdParserInternal::SubDocument(*this, getInput(), id, libmwaw::DOC_NOTE));
448   getTextListener()->insertNote
449   (MWAWNote(m_state->m_endNote ? MWAWNote::EndNote : MWAWNote::FootNote), subdoc);
450 }
451 
sendFieldComment(int id)452 void MsWrdParser::sendFieldComment(int id)
453 {
454   if (!getTextListener()) return;
455 
456   MWAWSubDocumentPtr subdoc(new MsWrdParserInternal::SubDocument(*this, getInput(), id, libmwaw::DOC_COMMENT_ANNOTATION));
457   getTextListener()->insertComment(subdoc);
458 }
459 
send(MWAWEntry const & entry)460 void MsWrdParser::send(MWAWEntry const &entry)
461 {
462   m_textParser->sendText(entry, false);
463 }
464 
send(int id,libmwaw::SubDocumentType type)465 void MsWrdParser::send(int id, libmwaw::SubDocumentType type)
466 {
467   if (type==libmwaw::DOC_COMMENT_ANNOTATION)
468     m_textParser->sendFieldComment(id);
469   else if (type==libmwaw::DOC_NOTE)
470     m_textParser->sendFootnote(id);
471   else {
472     MWAW_DEBUG_MSG(("MsWrdParser::send: find unexpected type\n"));
473   }
474 }
475 
476 ////////////////////////////////////////////////////////////
477 // the parser
478 ////////////////////////////////////////////////////////////
parse(librevenge::RVNGTextInterface * docInterface)479 void MsWrdParser::parse(librevenge::RVNGTextInterface *docInterface)
480 {
481   if (!getInput().get() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
482   bool ok = true;
483   try {
484     // create the asciiFile
485     ascii().setStream(getInput());
486     ascii().open(asciiName());
487 
488     checkHeader(nullptr);
489     ascii().addPos(getInput()->tell());
490     ascii().addNote("_");
491 
492     ok = createZones();
493     if (ok) {
494       createDocument(docInterface);
495       m_textParser->sendMainText();
496 
497       m_textParser->flushExtra();
498     }
499 
500     ascii().reset();
501   }
502   catch (...) {
503     MWAW_DEBUG_MSG(("MsWrdParser::parse: exception catched when parsing\n"));
504     ok = false;
505   }
506 
507   resetTextListener();
508   if (!ok) throw(libmwaw::ParseException());
509 }
510 
511 ////////////////////////////////////////////////////////////
512 // create the document
513 ////////////////////////////////////////////////////////////
createDocument(librevenge::RVNGTextInterface * documentInterface)514 void MsWrdParser::createDocument(librevenge::RVNGTextInterface *documentInterface)
515 {
516   if (!documentInterface) return;
517   if (getTextListener()) {
518     MWAW_DEBUG_MSG(("MsWrdParser::createDocument: listener already exist\n"));
519     return;
520   }
521 
522   // update the page
523   m_state->m_actPage = 0;
524 
525   // create the page list
526   MWAWPageSpan ps(getPageSpan());
527   MWAWEntry entry = m_textParser->getHeader();
528   if (entry.valid()) {
529     MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
530     header.m_subDocument.reset
531     (new MsWrdParserInternal::SubDocument(*this, getInput(), entry, libmwaw::DOC_HEADER_FOOTER));
532     ps.setHeaderFooter(header);
533   }
534   entry = m_textParser->getFooter();
535   if (entry.valid()) {
536     MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
537     footer.m_subDocument.reset
538     (new MsWrdParserInternal::SubDocument(*this, getInput(), entry, libmwaw::DOC_HEADER_FOOTER));
539     ps.setHeaderFooter(footer);
540   }
541   int numPage = 1;
542   if (m_textParser->numPages() > numPage)
543     numPage = m_textParser->numPages();
544   m_state->m_numPages = numPage;
545 
546   ps.setPageSpan(m_state->m_numPages+1);
547   std::vector<MWAWPageSpan> pageList(1,ps);
548   //
549   MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
550   setTextListener(listen);
551   if (!m_state->m_metaData.empty())
552     listen->setDocumentMetaData(m_state->m_metaData);
553   listen->startDocument();
554 }
555 
556 
557 ////////////////////////////////////////////////////////////
558 //
559 // Intermediate level
560 //
561 ////////////////////////////////////////////////////////////
562 
563 ////////////////////////////////////////////////////////////
564 // try to find the different zone
565 ////////////////////////////////////////////////////////////
createZones()566 bool MsWrdParser::createZones()
567 {
568   if (!readZoneList()) return false;
569   MWAWInputStreamPtr input = getInput();
570   long pos = input->tell();
571   if (pos != m_state->m_bot) {
572     ascii().addPos(pos);
573     ascii().addNote("_");
574   }
575   libmwaw::DebugStream f;
576   ascii().addPos(m_state->m_eot);
577   ascii().addNote("_");
578 
579   auto it = m_entryMap.find("PrintInfo");
580   if (it != m_entryMap.end())
581     readPrintInfo(it->second);
582 
583   it = m_entryMap.find("DocSum");
584   if (it != m_entryMap.end())
585     readDocSum(it->second);
586 
587   it = m_entryMap.find("Printer");
588   if (it != m_entryMap.end())
589     readPrinter(it->second);
590 
591   readObjects();
592 
593   bool ok = m_textParser->createZones(m_state->m_bot);
594 
595   it = m_entryMap.find("DocumentInfo");
596   if (it != m_entryMap.end())
597     readDocumentInfo(it->second);
598 
599   it = m_entryMap.find("Zone17");
600   if (it != m_entryMap.end())
601     readZone17(it->second);
602 
603   it = m_entryMap.find("Picture");
604   while (it != m_entryMap.end()) {
605     if (!it->second.hasType("Picture")) break;
606     MsWrdEntry &entry=it++->second;
607     readPicture(entry);
608   }
609 
610   for (auto fIt : m_entryMap) {
611     MsWrdEntry const &entry = fIt.second;
612     if (entry.isParsed()) continue;
613     ascii().addPos(entry.begin());
614     f.str("");
615     f << entry;
616     ascii().addNote(f.str().c_str());
617     ascii().addPos(entry.end());
618     ascii().addNote("_");
619 
620   }
621   return ok;
622 }
623 
624 ////////////////////////////////////////////////////////////
625 // read the zone list ( FIB )
626 ////////////////////////////////////////////////////////////
readZoneList()627 bool MsWrdParser::readZoneList()
628 {
629   MWAWInputStreamPtr input = getInput();
630   int const vers = version();
631   getInput()->seek(vers <= 3 ? 30 : 64, librevenge::RVNG_SEEK_SET);
632   int numData = vers <= 3 ? 15: 20;
633   std::stringstream s;
634   for (int i = 0; i < numData; i++) {
635     switch (i) {
636     // the first two zone are often simillar : even/odd header/footer ?
637     case 0: // original styles zone, often invalid
638       readEntry("Styles", 0);
639       break;
640     case 1: // STSH
641       readEntry("Styles", 1);
642       break;
643     case 2: // FFNDRef
644       readEntry("FootnotePos");
645       break;
646     case 3: // FFNDText
647       readEntry("FootnoteDef");
648       break;
649     case 4: // SED
650       readEntry("Section");
651       break;
652     case 5: //
653       readEntry("PageBreak");
654       break;
655     case 6: // fandRef
656       readEntry("FieldName");
657       break;
658     case 7: // fandText
659       readEntry("FieldPos");
660       break;
661     case 8: // Hdd
662       readEntry("HeaderFooter");
663       break;
664     case 9: // BteChpx
665       readEntry("CharList", 0);
666       break;
667     case 10: // BtePapx
668       readEntry("ParagList", 1);
669       break;
670     case 12: // SttbfFfn
671       readEntry("FontIds");
672       break;
673     case 13: // PrDrvr: checkme: is it ok also for v3 file ?
674       readEntry("PrintInfo");
675       break;
676     case 14: // Clx/Phe
677       readEntry(vers <= 3 ? "TextStruct" : "ParaInfo");
678       break;
679     case 15: // Dop?
680       readEntry("DocumentInfo");
681       break;
682     case 16:
683       readEntry("Printer");
684       break;
685     case 18: // Clx (ie. a list of Pcd )
686       readEntry("TextStruct");
687       break;
688     case 19:
689       readEntry("FootnoteData");
690       break;
691     default:
692       s.str("");
693       s << "Zone" << i;
694       if (i < 4) s << "_";
695       readEntry(s.str());
696       break;
697     }
698   }
699 
700   if (vers <= 3) return true;
701   long pos = input->tell();
702   libmwaw::DebugStream f;
703   f << "Entries(ListZoneData)[0]:";
704   for (int i = 0; i < 2; i++) // two small int
705     f << "f" << i << "=" << input->readLong(2) << ",";
706 
707   ascii().addPos(pos);
708   ascii().addNote(f.str().c_str());
709   if (vers <= 4) return true;
710 
711   // main
712   readEntry("ObjectName",0);
713   readEntry("FontNames");
714   readEntry("ObjectList",0);
715   readEntry("ObjectFlags",0);
716   readEntry("DocSum",0);
717   for (int i = 25; i < 31; i++) {
718     /* check me: Zone25, Zone26, Zone27: also some object name, list, flags ? */
719     // header/footer
720     if (i==28) readEntry("ObjectName",1);
721     else if (i==29) readEntry("ObjectList",1);
722     else if (i==30) readEntry("ObjectFlags",1);
723     else {
724       s.str("");
725       s << "Zone" << i;
726       readEntry(s.str());
727     }
728   }
729 
730   pos = input->tell();
731   f.str("");
732   f << "ListZoneData[1]:";
733 
734   long val = input->readLong(2);
735   if (val) f << "unkn=" << val << ",";
736   ascii().addPos(pos);
737   ascii().addNote(f.str().c_str());
738 
739   if (input->isEnd()) {
740     MWAW_DEBUG_MSG(("MsWrdParser::readZoneList: can not read list zone\n"));
741     return false;
742   }
743   return true;
744 }
745 
746 ////////////////////////////////////////////////////////////
747 //
748 // Low level
749 //
750 ////////////////////////////////////////////////////////////
751 
752 
753 
754 ////////////////////////////////////////////////////////////
755 // read the header
756 ////////////////////////////////////////////////////////////
checkHeader(MWAWHeader * header,bool strict)757 bool MsWrdParser::checkHeader(MWAWHeader *header, bool strict)
758 {
759   *m_state = MsWrdParserInternal::State();
760 
761   MWAWInputStreamPtr input = getInput();
762   if (!input || !input->hasDataFork())
763     return false;
764 
765   libmwaw::DebugStream f;
766   int headerSize=64;
767   if (!input->checkPosition(0x88)) {
768     MWAW_DEBUG_MSG(("MsWrdParser::checkHeader: file is too short\n"));
769     return false;
770   }
771   long pos = 0;
772   input->seek(pos, librevenge::RVNG_SEEK_SET);
773   auto val = static_cast<int>(input->readULong(2));
774   switch (val) {
775   case 0xfe34:
776     switch (input->readULong(2)) {
777     case 0x0:
778       headerSize = 30;
779       setVersion(3);
780       break;
781     default:
782       return false;
783     }
784     break;
785   case 0xfe37:
786     switch (input->readULong(2)) {
787     case 0x1c:
788       setVersion(4);
789       break;
790     case 0x23:
791       setVersion(5);
792       break;
793     default:
794       return false;
795     }
796     break;
797   default:
798     return false;
799   }
800 
801   int const vers = version();
802   f << "FileHeader:";
803   val = static_cast<int>(input->readULong(1)); // v1: ab other 0 ?
804   if (val) f << "f0=" << val << ",";
805   for (int i = 1; i < 3; i++) { // always 0
806     val = static_cast<int>(input->readLong(2));
807     if (val) f << "f" << i << "=" << val << ",";
808   }
809   if (vers > 3) {
810     // find 4, 8, c, 24, 2c
811     val = static_cast<int>(input->readLong(2));
812     if (val)
813       f << "unkn=" << std::hex << val << std::dec << ",";
814     // 0,0,0x19,0
815     for (int i = 4; i < 8; i++) {
816       val = static_cast<int>(input->readLong(1));
817       if (val) f << "f" << i << "=" << val << ",";
818     }
819   }
820 
821   for (int i = 0; i < 5; i++) { // always 0 ?
822     val = static_cast<int>(input->readLong(1));
823     if (val) f << "g" << i << "=" << val << ",";
824   }
825 
826   m_state->m_bot = vers <= 3 ? 0x100 : long(input->readULong(4));
827   m_state->m_eot = long(input->readULong(4));
828   f << "text=" << std::hex << m_state->m_bot << "<->" << m_state->m_eot << ",";
829   if (m_state->m_bot > m_state->m_eot) {
830     f << "#text,";
831     if (0x100 <= m_state->m_eot) {
832       MWAW_DEBUG_MSG(("MsWrdParser::checkHeader: problem with text position: reset begin to default\n"));
833       m_state->m_bot = 0x100;
834     }
835     else {
836       MWAW_DEBUG_MSG(("MsWrdParser::checkHeader: problem with text position: reset to empty\n"));
837       m_state->m_bot = m_state->m_eot = 0x100;
838     }
839   }
840 
841   if (vers <= 3) { // always 0
842     for (int i = 0; i < 6; i++) {
843       val = static_cast<int>(input->readLong(2));
844       if (val) f << "h" << i << "=" << val << ",";
845     }
846     ascii().addPos(pos);
847     ascii().addNote(f.str().c_str());
848     if (!readHeaderEndV3())
849       return false;
850     if (header)
851       header->reset(MWAWDocument::MWAW_T_MICROSOFTWORD, vers);
852     return true;
853   }
854 
855   auto endOfData = long(input->readULong(4));
856   f << "eof=" << std::hex << endOfData << std::dec << ",";
857   if (endOfData < 100 || !input->checkPosition(endOfData)) {
858     MWAW_DEBUG_MSG(("MsWrdParser::checkHeader: end of file pos is too small\n"));
859     if (endOfData < m_state->m_eot || strict)
860       return false;
861     f << "#endOfData,";
862   }
863   ascii().addPos(endOfData);
864   ascii().addNote("Entries(End)");
865 
866   val = static_cast<int>(input->readLong(4)); // always 0 ?
867   if (val) f << "unkn2=" << val << ",";
868   ascii().addPos(pos);
869   ascii().addNote(f.str().c_str());
870 
871   if (!m_textParser->readHeaderTextLength())
872     return false;
873 
874   pos = input->tell();
875   f.str("");
876   f << "FileHeader[A]:";
877   for (int i = 0; i < 8; i++) {
878     val = static_cast<int>(input->readLong(2));
879     if (val) f << "f" << i << "=" << val << ",";
880   }
881 
882   // ok, we can finish initialization
883   if (header)
884     header->reset(MWAWDocument::MWAW_T_MICROSOFTWORD, vers);
885 
886   if (long(input->tell()) != headerSize)
887     ascii().addDelimiter(input->tell(), '|');
888 
889   ascii().addPos(pos);
890   ascii().addNote(f.str().c_str());
891 
892   return true;
893 }
894 
895 ////////////////////////////////////////////////////////////
896 // try to the end of the header
897 ////////////////////////////////////////////////////////////
readHeaderEndV3()898 bool MsWrdParser::readHeaderEndV3()
899 {
900   MWAWInputStreamPtr input = getInput();
901   if (!input->checkPosition(0xb8))
902     return false;
903   libmwaw::DebugStream f;
904   input->seek(0x78, librevenge::RVNG_SEEK_SET);
905   long pos = input->tell();
906   long val = input->readLong(4); // normally 0x100
907   if (val != 0x100)
908     f << "FileHeader[A]:" << std::hex << val << std::dec << ",";
909   else
910     f << "_";
911   ascii().addPos(pos);
912   ascii().addNote(f.str().c_str());
913   if (!m_textParser->readHeaderTextLength())
914     return false;
915   pos = input->tell();
916   f << "FileHeader[B]:";
917   for (int i = 0; i < 18; i++) { // always 0 ?
918     val = input->readLong(2);
919     if (val)
920       f << "f" << i << "=" << val << ",";
921   }
922   float dim[6]; // H, W+margin T, L, B, R
923   for (auto &d : dim) d = float(input->readLong(2))/1440.0f;
924 
925   f << "page=" << dim[1] << "x" << dim[0] << ",";
926   f << "margins=" << dim[3] << "x" << dim[2] << "-" << dim[5] << "x" << dim[4] << ",";
927   bool dimOk = true;
928   if (dim[0]>0 && dim[1]>0) {
929     for (int i = 2; i < 6; i++)
930       if (dim[i] < 0) dimOk = false;
931     if (2*(dim[3]+dim[5]) > dim[1] || 2*(dim[2]+dim[4]) > dim[0]) dimOk = false;
932     if (!dimOk) {
933       f << "###";
934       MWAW_DEBUG_MSG(("MsWrdParser::readHeaderEndV3: page dimensions seem bad\n"));
935     }
936     else {
937       getPageSpan().setMarginTop(double(dim[2]));
938       getPageSpan().setMarginLeft(double(dim[3]));
939       getPageSpan().setMarginBottom((dim[4]< 0.5f) ? 0.0 : double(dim[4])-0.5);
940       getPageSpan().setMarginRight((dim[5]< 0.5f) ? 0.0 : double(dim[5])-0.5);
941       getPageSpan().setFormLength(double(dim[0]));
942       getPageSpan().setFormWidth(double(dim[1]));
943     }
944   }
945   else
946     dimOk = false;
947   ascii().addPos(pos);
948   ascii().addNote(f.str().c_str());
949 
950   pos = input->tell();
951   f.str("");
952   f << "FileHeader[C]:";
953   val = input->readLong(2); // always 0 ?
954   if (val)
955     f << "margins[binding]=" << float(val)/1440.f << ",";
956   val = input->readLong(2);
957   f << "defTabs=" << float(val)/1440.f << ",";
958   auto flags = static_cast<int>(input->readULong(1));
959   if (flags & 0x80) // page vis a vis
960     f << "facingpage,";
961   if (flags & 0x40) // ligne creuse
962     f << "defTabs[emptyline],";
963   switch ((flags>>1) & 0x3) {
964   case 0:
965     if (dimOk) m_state->m_endNote = true;
966     f << "endnote,";
967     break;
968   case 1:
969     f << "footnote,";
970     break;
971   case 2:
972     f << "footnote[undertext],";
973     break;
974   default:
975     f << "#notepos=3,";
976     break;
977   }
978   if (flags&1) {
979     f << "landscape,";
980     if (dimOk)
981       getPageSpan().setFormOrientation(MWAWPageSpan::LANDSCAPE);
982   }
983   flags &= 0x38;
984   if (flags)
985     f << "#flags=" << std::hex << flags << std::dec << ",";
986   flags = static_cast<int>(input->readULong(1));
987   if (flags) // always 1
988     f << "fl1=" << std::hex << flags << std::dec << ",";
989   char const *wh[] = { "note", "line", "page" };
990   for (auto const *what : wh) {
991     val = long(input->readULong(2));
992     if (val == 1) continue;
993     if (val & 0x8000)
994       f << what << "[firstNumber]=" << (val&0x7FFF) << "[auto],";
995     else
996       f << what << "[firstNumber]=" << val << ",";
997   }
998   for (int i = 0; i < 2; i++) { // first flags often 0x40, second?
999     flags = static_cast<int>(input->readULong(1));
1000     if (flags) // always 1
1001       f << "fl" << 2+i << "=" << std::hex << flags << std::dec << ",";
1002   }
1003   for (int i = 0; i < 13; i++) { // always 0?
1004     val = input->readLong(2);
1005     if (val)
1006       f << "f" << i << "=" << val << ",";
1007   }
1008   ascii().addPos(pos);
1009   ascii().addNote(f.str().c_str());
1010 
1011   pos = input->tell();
1012   f.str("");
1013   f << "FileHeader[D]:";
1014   auto sz = static_cast<int>(input->readULong(1));
1015   if (sz == 0) {
1016     ascii().addPos(pos);
1017     ascii().addNote("_");
1018     return true;
1019   }
1020   if (sz > 31) {
1021     f << "###";
1022     MWAW_DEBUG_MSG(("MsWrdParser::readHeaderEndV3: next filename seems bad\n"));
1023   }
1024   else {
1025     std::string fName("");
1026     for (int i = 0; i < sz; i++)
1027       fName += char(input->readULong(1));
1028     f << "nextFile=" << fName;
1029   }
1030   ascii().addPos(pos);
1031   ascii().addNote(f.str().c_str());
1032   input->seek(0x100, librevenge::RVNG_SEEK_SET);
1033   return true;
1034 }
1035 
1036 ////////////////////////////////////////////////////////////
1037 // try to read an entry
1038 ////////////////////////////////////////////////////////////
readEntry(std::string type,int id)1039 MsWrdEntry MsWrdParser::readEntry(std::string type, int id)
1040 {
1041   MWAWInputStreamPtr input = getInput();
1042   MsWrdEntry entry;
1043   entry.setType(type);
1044   entry.setId(id);
1045   long pos = input->tell();
1046   libmwaw::DebugStream f;
1047 
1048   auto debPos = long(input->readULong(4));
1049   auto sz = long(input->readULong(2));
1050   if (id >= 0) f << "Entries(" << type << ")[" << id << "]:";
1051   else f << "Entries(" << type << "):";
1052   if (sz == 0) {
1053     ascii().addPos(pos);
1054     ascii().addNote("_");
1055     return entry;
1056   }
1057   if (!input->checkPosition(debPos+sz)) {
1058     MWAW_DEBUG_MSG(("MsWrdParser::readEntry: problem reading entry: %s\n", type.c_str()));
1059     f << "#";
1060     ascii().addPos(pos);
1061     ascii().addNote(f.str().c_str());
1062     return entry;
1063   }
1064 
1065   entry.setBegin(debPos);
1066   entry.setLength(sz);
1067   m_entryMap.insert
1068   (std::multimap<std::string, MsWrdEntry>::value_type(type, entry));
1069 
1070   f << std::hex << debPos << "[" << sz << "],";
1071   ascii().addPos(pos);
1072   ascii().addNote(f.str().c_str());
1073 
1074   return entry;
1075 }
1076 
1077 ////////////////////////////////////////////////////////////
1078 // read the document information
1079 ////////////////////////////////////////////////////////////
readDocumentInfo(MsWrdEntry & entry)1080 bool MsWrdParser::readDocumentInfo(MsWrdEntry &entry)
1081 {
1082   if (entry.length() != 0x20) {
1083     MWAW_DEBUG_MSG(("MsWrdParser::readDocumentInfo: the zone size seems odd\n"));
1084     return false;
1085   }
1086   MWAWInputStreamPtr input = getInput();
1087   long pos = entry.begin();
1088   entry.setParsed(true);
1089   input->seek(pos, librevenge::RVNG_SEEK_SET);
1090   libmwaw::DebugStream f;
1091   f << "DocumentInfo:";
1092 
1093   float dim[2];
1094   for (float &i : dim) i =  float(input->readLong(2))/1440.f;
1095   f << "dim?=" << dim[1] << "x" << dim[0] << ",";
1096 
1097   float margin[4];
1098   f << ",marg=["; // top, left?, bottom, right?
1099   for (auto &marg : margin) {
1100     marg = float(input->readLong(2))/1440.f;
1101     f << marg << ",";
1102     if (marg < 0) marg *= -1.0f;
1103   }
1104   f << "],";
1105 
1106   if (dim[0] > margin[0]+margin[2] && dim[1] > margin[1]+margin[3]) {
1107     getPageSpan().setMarginTop(double(margin[0]));
1108     getPageSpan().setMarginLeft(double(margin[1]));
1109     /* decrease a little the right/bottom margin to allow fonts discrepancy*/
1110     getPageSpan().setMarginBottom((margin[2]< 0.5f) ? 0.0 : double(margin[2])-0.5);
1111     getPageSpan().setMarginRight((margin[3]< 0.5f) ? 0.0 : double(margin[3])-0.5);
1112 
1113     getPageSpan().setFormLength(double(dim[0]));
1114     getPageSpan().setFormWidth(double(dim[1]));
1115   }
1116   else {
1117     MWAW_DEBUG_MSG(("MsWrdParser::readDocumentInfo: the page dimensions seems odd\n"));
1118   }
1119 
1120   auto val = static_cast<int>(input->readLong(2)); // always 0 ?
1121   if (val) f << "unkn=" << val << ",";
1122   val = static_cast<int>(input->readLong(2)); // 0x2c5 or 0x2d0?
1123   f << "f0=" << val << ",";
1124   for (int i = 0; i < 4; i++) { //[a|12|40|42|4a|52|54|d2],0,0|80,1
1125     val = static_cast<int>(input->readULong(1));
1126     if (val) f << "fl" << i << "=" << std::hex << val << std::dec << ",";
1127   }
1128   val = static_cast<int>(input->readLong(2)); // always 1 ?
1129   if (val != 1) f << "f1=" << val << ",";
1130   // a small number between 0 and 77
1131   f << "f2=" << static_cast<int>(input->readLong(2)) << ",";
1132   for (int i = 0; i < 4; i++) { //[0|2|40|42|44|46|48|58],0|64,0|10|80,[0|2|5]
1133     val = static_cast<int>(input->readULong(1));
1134     if (val) f << "flA" << i << "=" << std::hex << val << std::dec << ",";
1135   }
1136   val = static_cast<int>(input->readLong(2)); // always 0 ?
1137   if (val != 1) f << "f3=" << val << ",";
1138   val = static_cast<int>(input->readLong(2)); // 0, 48, 50
1139   if (val) f << "f4=" << val << ",";
1140 
1141   ascii().addPos(entry.begin());
1142   ascii().addNote(f.str().c_str());
1143   ascii().addPos(entry.end());
1144   ascii().addNote("_");
1145   return true;
1146 }
1147 
1148 ////////////////////////////////////////////////////////////
1149 // read the zone 17
1150 ////////////////////////////////////////////////////////////
readZone17(MsWrdEntry & entry)1151 bool MsWrdParser::readZone17(MsWrdEntry &entry)
1152 {
1153   if (entry.length() != 0x2a) {
1154     MWAW_DEBUG_MSG(("MsWrdParser::readZone17: the zone size seems odd\n"));
1155     return false;
1156   }
1157   MWAWInputStreamPtr input = getInput();
1158   long pos = entry.begin();
1159   entry.setParsed(true);
1160   input->seek(pos, librevenge::RVNG_SEEK_SET);
1161   libmwaw::DebugStream f;
1162   f << "Zone17:";
1163   if (version() < 5) {
1164     f << "bdbox?=[";
1165     for (int i = 0; i < 4; i++)
1166       f << input->readLong(2) << ",";
1167     f << "],";
1168     f << "bdbox2?=[";
1169     for (int i = 0; i < 4; i++)
1170       f << input->readLong(2) << ",";
1171     f << "],";
1172   }
1173 
1174   /*
1175     f0=0, 80, 82, 84, b0, b4, c2, c4, f0, f2 : type and ?
1176     f1=0|1|8|34|88 */
1177   int val;
1178   for (int i = 0; i < 2; i++) {
1179     val = static_cast<int>(input->readULong(1));
1180     if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1181   }
1182   // 0 or 1, followed by 0
1183   for (int i = 2; i < 4; i++) {
1184     val = static_cast<int>(input->readLong(1));
1185     if (val) f << "f" << i << "=" << val << ",";
1186   }
1187   auto ptr = long(input->readULong(4)); // a text ptr ( often near to textLength )
1188   f << "textPos[sel?]=" << std::hex << ptr << std::dec << ",";
1189   val  = static_cast<int>(input->readULong(4)); // almost always ptr
1190   if (val != ptr)
1191     f << "textPos1=" << std::hex << val << std::dec << ",";
1192   // a small int between 6 and b
1193   val = static_cast<int>(input->readLong(2));
1194   if (val) f << "f4=" << val << ",";
1195 
1196   for (int i = 5; i < 7; i++) { // 0,0 or 3,5 or 8000, 8000
1197     val = static_cast<int>(input->readULong(2));
1198     if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1199   }
1200   val  = static_cast<int>(input->readULong(4)); // almost always ptr
1201   if (val != ptr)
1202     f << "textPos2=" << std::hex << val << std::dec << ",";
1203   /* g0=[0,1,5,c], g1=[0,1,3,4] */
1204   for (int i = 0; i < 2; i++) {
1205     val = static_cast<int>(input->readLong(2));
1206     if (val) f << "g" << i << "=" << val << ",";
1207   }
1208   if (version() == 5) {
1209     f << "bdbox?=[";
1210     for (int i = 0; i < 4; i++)
1211       f << input->readLong(2) << ",";
1212     f << "],";
1213     f << "bdbox2?=[";
1214     for (int i = 0; i < 4; i++)
1215       f << input->readLong(2) << ",";
1216     f << "],";
1217   }
1218   ascii().addPos(pos);
1219   ascii().addNote(f.str().c_str());
1220   ascii().addPos(entry.end());
1221   ascii().addNote("_");
1222   return true;
1223 }
1224 
1225 ////////////////////////////////////////////////////////////
1226 // read the printer name
1227 ////////////////////////////////////////////////////////////
readPrinter(MsWrdEntry & entry)1228 bool MsWrdParser::readPrinter(MsWrdEntry &entry)
1229 {
1230   if (entry.length() < 2) {
1231     MWAW_DEBUG_MSG(("MsWrdParser::readPrinter: the zone seems to short\n"));
1232     return false;
1233   }
1234 
1235   MWAWInputStreamPtr input = getInput();
1236   long pos = entry.begin();
1237   input->seek(pos, librevenge::RVNG_SEEK_SET);
1238   libmwaw::DebugStream f;
1239   f << "Printer:";
1240   auto sz = static_cast<int>(input->readULong(2));
1241   if (sz > entry.length()) {
1242     MWAW_DEBUG_MSG(("MsWrdParser::readPrinter: the zone seems to short\n"));
1243     return false;
1244   }
1245   auto strSz = static_cast<int>(input->readULong(1));
1246   if (strSz+2> sz) {
1247     MWAW_DEBUG_MSG(("MsWrdParser::readPrinter: name seems to big\n"));
1248     return false;
1249   }
1250   std::string name("");
1251   for (int i = 0; i < strSz; i++)
1252     name+=char(input->readLong(1));
1253   f << name << ",";
1254   int i= 0;
1255   while (long(input->tell())+2 <= entry.end()) { // almost always a,0,0
1256     auto val = static_cast<int>(input->readLong(2));
1257     if (val) f << "f" << i << "=" << val << ",";
1258     i++;
1259   }
1260   if (long(input->tell()) != entry.end())
1261     ascii().addDelimiter(input->tell(), '|');
1262 
1263   entry.setParsed(true);
1264   ascii().addPos(pos);
1265   ascii().addNote(f.str().c_str());
1266 
1267   ascii().addPos(entry.end());
1268   ascii().addNote("_");
1269   return true;
1270 }
1271 
1272 ////////////////////////////////////////////////////////////
1273 // read the document summary
1274 ////////////////////////////////////////////////////////////
readDocSum(MsWrdEntry & entry)1275 bool MsWrdParser::readDocSum(MsWrdEntry &entry)
1276 {
1277   MWAWInputStreamPtr input = getInput();
1278   if (entry.length() < 8 || !input->checkPosition(entry.end())) {
1279     MWAW_DEBUG_MSG(("MsWrdParser::readDocSum: the zone seems to short\n"));
1280     return false;
1281   }
1282 
1283   long pos = entry.begin();
1284   input->seek(pos, librevenge::RVNG_SEEK_SET);
1285   libmwaw::DebugStream f;
1286   f << "DocSum:";
1287   auto sz = static_cast<int>(input->readULong(2));
1288   if (sz > entry.length()) {
1289     MWAW_DEBUG_MSG(("MsWrdParser::readDocSum: the zone seems to short\n"));
1290     return false;
1291   }
1292   entry.setParsed(true);
1293 
1294   if (sz != entry.length()) f << "#";
1295   char const *what[] = { "title", "subject","author","version","keyword",
1296                          "creator", "author1", "author2" // unsure why there are 4 authors...
1297                        };
1298   char const *attribNames[] = { "dc:title", "dc:subject", "meta:initial-creator", nullptr,
1299                                 "meta:keywords", "dc:creator", nullptr, nullptr
1300                               };
1301   auto fontConverter = getFontConverter();
1302   for (int i = 0; i < 8; i++) {
1303     long actPos = input->tell();
1304     if (actPos == entry.end()) break;
1305 
1306     sz = static_cast<int>(input->readULong(1));
1307     if (sz == 0 || sz == 0xFF) continue;
1308 
1309     if (actPos+1+sz > entry.end()) {
1310       MWAW_DEBUG_MSG(("MsWrdParser::readDocSum: string %d to short...\n", i));
1311       f << "#";
1312       input->seek(actPos, librevenge::RVNG_SEEK_SET);
1313       break;
1314     }
1315     librevenge::RVNGString s;
1316     for (int j = 0; j < sz; j++) {
1317       auto c= static_cast<unsigned char>(input->readULong(1));
1318       // assume standart encoding here
1319       int unicode = fontConverter ? fontConverter->unicode(3, c) : -1;
1320       if (unicode!=-1)
1321         libmwaw::appendUnicode(uint32_t(unicode), s);
1322       else if (c<0x20)
1323         f << "##" << int(c);
1324       else
1325         s.append(char(c));
1326     }
1327     if (!s.empty() && attribNames[i]!=nullptr)
1328       m_state->m_metaData.insert(attribNames[i],s);
1329     f << what[i] << "=" <<  s.cstr() << ",";
1330   }
1331 
1332   ascii().addPos(pos);
1333   ascii().addNote(f.str().c_str());
1334 
1335   if (long(input->tell()) != entry.end())
1336     ascii().addDelimiter(input->tell(), '|');
1337 
1338   ascii().addPos(entry.end());
1339   ascii().addNote("_");
1340   return true;
1341 }
1342 
1343 ////////////////////////////////////////////////////////////
1344 // read  a list of strings zone
1345 ////////////////////////////////////////////////////////////
readStringsZone(MsWrdEntry & entry,std::vector<std::string> & list)1346 bool MsWrdParser::readStringsZone(MsWrdEntry &entry, std::vector<std::string> &list)
1347 {
1348   list.resize(0);
1349   MWAWInputStreamPtr input = getInput();
1350   if (entry.length() < 2 || !input->checkPosition(entry.end())) {
1351     MWAW_DEBUG_MSG(("MsWrdParser::readStringsZone: the zone seems to short\n"));
1352     return false;
1353   }
1354 
1355   long pos = entry.begin();
1356   input->seek(pos, librevenge::RVNG_SEEK_SET);
1357   libmwaw::DebugStream f;
1358   f << entry;
1359   auto sz = static_cast<int>(input->readULong(2));
1360   if (sz > entry.length()) {
1361     MWAW_DEBUG_MSG(("MsWrdParser::readStringsZone: the zone seems to short\n"));
1362     return false;
1363   }
1364   ascii().addPos(entry.begin());
1365   ascii().addNote(f.str().c_str());
1366 
1367   int id = 0;
1368   while (long(input->tell()) != entry.end()) {
1369     pos = input->tell();
1370     auto strSz = static_cast<int>(input->readULong(1));
1371     if (pos+strSz+1> entry.end()) {
1372       MWAW_DEBUG_MSG(("MsWrdParser::readStringsZone: a string seems to big\n"));
1373       f << "#";
1374       break;
1375     }
1376     std::string name("");
1377     for (int i = 0; i < strSz; i++)
1378       name+=char(input->readLong(1));
1379     list.push_back(name);
1380     f.str("");
1381     f << entry << "id" << id++ << "," << name << ",";
1382     ascii().addPos(pos);
1383     ascii().addNote(f.str().c_str());
1384   }
1385 
1386   if (long(input->tell()) != entry.end()) {
1387     ascii().addPos(input->tell());
1388     f.str("");
1389     f << entry << "#";
1390     ascii().addNote(f.str().c_str());
1391   }
1392 
1393   entry.setParsed(true);
1394 
1395   ascii().addPos(entry.end());
1396   ascii().addNote("_");
1397   return true;
1398 }
1399 
1400 ////////////////////////////////////////////////////////////
1401 // read the objects
1402 ////////////////////////////////////////////////////////////
readObjects()1403 bool MsWrdParser::readObjects()
1404 {
1405   MWAWInputStreamPtr input = getInput();
1406 
1407   auto it = m_entryMap.find("ObjectList");
1408   while (it != m_entryMap.end()) {
1409     if (!it->second.hasType("ObjectList")) break;
1410     MsWrdEntry &entry=it++->second;
1411     readObjectList(entry);
1412   }
1413 
1414   it = m_entryMap.find("ObjectFlags");
1415   while (it != m_entryMap.end()) {
1416     if (!it->second.hasType("ObjectFlags")) break;
1417     MsWrdEntry &entry=it++->second;
1418     readObjectFlags(entry);
1419   }
1420 
1421   it = m_entryMap.find("ObjectName");
1422   while (it != m_entryMap.end()) {
1423     if (!it->second.hasType("ObjectName")) break;
1424     MsWrdEntry &entry=it++->second;
1425     std::vector<std::string> list;
1426     readStringsZone(entry, list);
1427 
1428     if (entry.id() < 0 || entry.id() > 1) {
1429       MWAW_DEBUG_MSG(("MsWrdParser::readObjects: unexpected entry id: %d\n", entry.id()));
1430       continue;
1431     }
1432     auto &listObject = m_state->m_objectList[entry.id()];
1433     size_t numObjects = listObject.size();
1434     if (list.size() != numObjects) {
1435       MWAW_DEBUG_MSG(("MsWrdParser::readObjects: unexpected number of name\n"));
1436       if (list.size() < numObjects) numObjects = list.size();
1437     }
1438     for (size_t i = 0; i < numObjects; i++)
1439       listObject[i].m_name = list[i];
1440   }
1441 
1442   std::map<long,MWAWEntry> posToComments;
1443   for (auto &listObject : m_state->m_objectList) {
1444     for (auto &obj : listObject) {
1445       readObject(obj);
1446       if (obj.m_annotation.valid() && obj.m_textPos>=0)
1447         posToComments[obj.m_textPos]=obj.m_annotation;
1448     }
1449   }
1450   m_state->m_posToCommentMap=posToComments;
1451   return true;
1452 }
1453 
readObjectList(MsWrdEntry & entry)1454 bool MsWrdParser::readObjectList(MsWrdEntry &entry)
1455 {
1456   if (entry.id() < 0 || entry.id() > 1) {
1457     MWAW_DEBUG_MSG(("MsWrdParser::readObjectList: unexpected entry id: %d\n", entry.id()));
1458     return false;
1459   }
1460   std::vector<MsWrdParserInternal::Object> &listObject = m_state->m_objectList[entry.id()];
1461   listObject.resize(0);
1462   if (entry.length() < 4 || (entry.length()%18) != 4) {
1463     MWAW_DEBUG_MSG(("MsWrdParser::readObjectList: the zone size seems odd\n"));
1464     return false;
1465   }
1466   MWAWInputStreamPtr input = getInput();
1467   long pos = entry.begin();
1468   entry.setParsed(true);
1469   input->seek(pos, librevenge::RVNG_SEEK_SET);
1470   libmwaw::DebugStream f;
1471   f << "ObjectList[" << entry.id() << "]:";
1472   auto N=int(entry.length()/18);
1473 
1474   auto &plcMap=m_textParser->getTextPLCMap();
1475   MsWrdText::PLC plc(MsWrdText::PLC::Object);
1476   std::vector<long> textPos; // checkme
1477   textPos.resize(size_t(N)+1);
1478   f << "[";
1479   for (int i = 0; i < N+1; i++) {
1480     auto tPos = long(input->readULong(4));
1481     textPos[size_t(i)] = tPos;
1482     f << std::hex << tPos << std::dec << ",";
1483     if (i == N)
1484       break;
1485     plc.m_id = i;
1486     plcMap.insert(std::multimap<long, MsWrdText::PLC>::value_type(tPos,plc));
1487   }
1488   f << "],";
1489   ascii().addPos(pos);
1490   ascii().addNote(f.str().c_str());
1491 
1492   for (int i = 0; i < N; i++) {
1493     MsWrdParserInternal::Object object;
1494     object.m_textPos = textPos[size_t(i)];
1495     pos = input->tell();
1496     f.str("");
1497     object.m_id = static_cast<int>(input->readLong(2));
1498     // id0=<small number>:[8|48], id1: <small number>:60->normal, :7c?, 0->annotation ?
1499     for (int st = 0; st < 2; st++) {
1500       object.m_ids[st] = static_cast<int>(input->readLong(2));
1501       object.m_idsFlag[st] = static_cast<int>(input->readULong(1));
1502     }
1503 
1504     object.m_pos.setBegin(long(input->readULong(4)));
1505     auto val = static_cast<int>(input->readLong(2)); // always 0 ?
1506     if (val) f << "#f1=" << val << ",";
1507     object.m_extra = f.str();
1508     f.str("");
1509     f << "ObjectList-" << i << ":" << object;
1510     if (!input->checkPosition(object.m_pos.begin())) {
1511       MWAW_DEBUG_MSG(("MsWrdParser::readObjectList: pb with ptr\n"));
1512       f << "#ptr=" << std::hex << object.m_pos.begin() << std::dec << ",";
1513       object.m_pos.setBegin(0);
1514     }
1515 
1516     listObject.push_back(object);
1517     ascii().addPos(pos);
1518     ascii().addNote(f.str().c_str());
1519   }
1520 
1521   ascii().addPos(entry.end());
1522   ascii().addNote("_");
1523   return true;
1524 
1525 }
1526 
readObjectFlags(MsWrdEntry & entry)1527 bool MsWrdParser::readObjectFlags(MsWrdEntry &entry)
1528 {
1529   if (entry.id() < 0 || entry.id() > 1) {
1530     MWAW_DEBUG_MSG(("MsWrdParser::readObjectFlags: unexpected entry id: %d\n", entry.id()));
1531     return false;
1532   }
1533   std::vector<MsWrdParserInternal::Object> &listObject = m_state->m_objectList[entry.id()];
1534   auto numObject = static_cast<int>(listObject.size());
1535   if (entry.length() < 4 || (entry.length()%6) != 4) {
1536     MWAW_DEBUG_MSG(("MsWrdParser::readObjectFlags: the zone size seems odd\n"));
1537     return false;
1538   }
1539   MWAWInputStreamPtr input = getInput();
1540   long pos = entry.begin();
1541   entry.setParsed(true);
1542   input->seek(pos, librevenge::RVNG_SEEK_SET);
1543   libmwaw::DebugStream f;
1544   f << "ObjectFlags[" << entry.id() << "]:";
1545   auto N=int(entry.length()/6);
1546   if (N != numObject) {
1547     MWAW_DEBUG_MSG(("MsWrdParser::readObjectFlags: unexpected number of object\n"));
1548   }
1549 
1550   f << "[";
1551   for (int i = 0; i < N+1; i++) {
1552     auto textPos = long(input->readULong(4));
1553     if (i < numObject && textPos != listObject[size_t(i)].m_textPos && textPos != listObject[size_t(i)].m_textPos+1)
1554       f << "#";
1555     f << std::hex << textPos << std::dec << ",";
1556   }
1557   f << "],";
1558   ascii().addPos(pos);
1559   ascii().addNote(f.str().c_str());
1560 
1561   for (int i = 0; i < N; i++) {
1562     pos = input->tell();
1563     int flags[2];
1564     for (auto &flag : flags) flag = static_cast<int>(input->readULong(1));
1565     f.str("");
1566     f << "ObjectFlags-" << i << ":";
1567     if (i < numObject) {
1568       for (int st = 0; st < 2; st++) listObject[size_t(i)].m_flags[st] = flags[st];
1569       f << "Obj" << listObject[size_t(i)].m_id << ",";
1570     }
1571     // indentical to ObjectList id0[low] ?
1572     if (flags[0] != 0x48) f << "fl0="  << std::hex << flags[0] << std::dec << ",";
1573     if (flags[1]) f << "fl1="  << std::hex << flags[1] << std::dec << ",";
1574     ascii().addPos(pos);
1575     ascii().addNote(f.str().c_str());
1576   }
1577 
1578   ascii().addPos(entry.end());
1579   ascii().addNote("_");
1580   return true;
1581 
1582 }
1583 
readObject(MsWrdParserInternal::Object & obj)1584 bool MsWrdParser::readObject(MsWrdParserInternal::Object &obj)
1585 {
1586   MWAWInputStreamPtr input = getInput();
1587   libmwaw::DebugStream f;
1588 
1589   long pos = obj.m_pos.begin(), beginPos = pos;
1590   if (!pos) return false;
1591 
1592   input->seek(pos, librevenge::RVNG_SEEK_SET);
1593   auto sz = static_cast<int>(input->readULong(4));
1594 
1595   f << "Entries(ObjectData):Obj" << obj.m_id << ",";
1596   if (!input->checkPosition(pos+sz) || sz < 6) {
1597     MWAW_DEBUG_MSG(("MsWrdParser::readObject: pb finding object data sz\n"));
1598     f << "#";
1599     ascii().addPos(beginPos);
1600     ascii().addNote(f.str().c_str());
1601     return false;
1602   }
1603   obj.m_pos.setLength(sz);
1604   long endPos = obj.m_pos.end();
1605   ascii().addPos(endPos);
1606   ascii().addNote("_");
1607 
1608   auto fSz = static_cast<int>(input->readULong(2));
1609   if (fSz < 0 || fSz+6 > sz) {
1610     MWAW_DEBUG_MSG(("MsWrdParser::readObject: pb reading the name\n"));
1611     f << "#";
1612     ascii().addPos(beginPos);
1613     ascii().addNote(f.str().c_str());
1614     return false;
1615   }
1616   MsWrdEntry fileEntry = obj.getEntry();
1617   fileEntry.setParsed(true);
1618   m_entryMap.insert
1619   (std::multimap<std::string, MsWrdEntry>::value_type(fileEntry.type(), fileEntry));
1620 
1621   long zoneEnd = pos+6+fSz;
1622   std::string name(""); // first equation, second "" or Equation Word?
1623   while (long(input->tell()) != zoneEnd) {
1624     auto c = static_cast<int>(input->readULong(1));
1625     if (c == 0) {
1626       if (name.length()) f << name << ",";
1627       name = "";
1628       continue;
1629     }
1630     name += char(c);
1631   }
1632   if (name.length()) f << name << ",";
1633 
1634   pos = input->tell();
1635   // Equation Word? : often contains not other data
1636   if (pos==endPos) {
1637     ascii().addPos(beginPos);
1638     ascii().addNote(f.str().c_str());
1639     return true;
1640   }
1641 
1642   // 0 or a small size c for annotation an equivalent of file type?
1643   fSz = static_cast<int>(input->readULong(1));
1644   if (pos+fSz+1 > endPos) {
1645     MWAW_DEBUG_MSG(("MsWrdParser::readObject: pb reading the second field zone\n"));
1646     f << "#fSz=" << fSz;
1647     ascii().addPos(beginPos);
1648     ascii().addNote(f.str().c_str());
1649     return false;
1650   }
1651   int val;
1652   bool isAnnotation=false;
1653   if (fSz==12) { // possible annotation
1654     f << "type=[";
1655     for (int i = 0; i < 4; i++) { // f0=f1=f2=0, f3=0x100
1656       val = static_cast<int>(input->readLong(2));
1657       if (val) f << "g0=" << std::hex << val << std::dec << ",";
1658     }
1659     std::string type("");
1660     for (int i = 0; i < 4; i++)
1661       type += char(input->readULong(1));
1662     f << type << "],";
1663     isAnnotation=type=="ANOT";
1664   }
1665   else if (fSz) {
1666     f << "##data2[sz]=" << fSz << ",";
1667     ascii().addDelimiter(input->tell(),'|');
1668     input->seek(pos+fSz+1, librevenge::RVNG_SEEK_SET);
1669     ascii().addDelimiter(input->tell(),'|');
1670   }
1671   pos = input->tell();
1672   if (pos+2>endPos) {
1673     if (pos!= endPos)
1674       f << "###";
1675     ascii().addPos(beginPos);
1676     ascii().addNote(f.str().c_str());
1677     return true;
1678   }
1679   val = static_cast<int>(input->readLong(2));
1680   if (val) f << "#f0=" << val << ",";
1681 
1682   pos = input->tell();
1683   if (pos+4>endPos) {
1684     if (pos!= endPos)
1685       f << "##";
1686     ascii().addPos(beginPos);
1687     ascii().addNote(f.str().c_str());
1688     return true;
1689   }
1690   auto dataSz = long(input->readULong(4));
1691   pos = input->tell();
1692   if (pos+dataSz > endPos) {
1693     MWAW_DEBUG_MSG(("MsWrdParser::readObject: pb reading the last field size zone\n"));
1694     f << "#fSz[last]=" << dataSz;
1695     ascii().addPos(beginPos);
1696     ascii().addNote(f.str().c_str());
1697     return false;
1698   }
1699   if (isAnnotation && dataSz>9) {
1700     f << "annot=[";
1701     for (int i = 0; i < 3; i++) { // h0=1|2, h1,h2: big numbers
1702       val = static_cast<int>(input->readULong(2));
1703       if (val)
1704         f << "h" << i << "=" << std::hex << val << std::dec << ",";
1705     }
1706     fSz = static_cast<int>(input->readULong(1));
1707     bool ok=true;
1708     if (fSz+7 > dataSz) {
1709       MWAW_DEBUG_MSG(("MsWrdParser::readObject: can not read the annotation string\n"));
1710       f << "###";
1711       ok = false;
1712     }
1713     else {
1714       std::string annotation("");
1715       for (int i = 0; i < fSz; i++)
1716         annotation += char(input->readULong(1));
1717       if (!annotation.empty())
1718         f << "annot[inText]=" << annotation << ",";
1719     }
1720 
1721     if (ok) {
1722       val = static_cast<int>(input->readULong(1)); // always 0
1723       if (val)
1724         f << "h3=" << std::hex << val << std::dec << ",";
1725       fSz = static_cast<int>(input->readULong(1));
1726     }
1727     if (!ok) {
1728     }
1729     else if (fSz+9 > dataSz) {
1730       MWAW_DEBUG_MSG(("MsWrdParser::readObject: can not read the annotation comment\n"));
1731       f << "###";
1732     }
1733     else {
1734       // store the comment
1735       obj.m_annotation.setBegin(input->tell());
1736       obj.m_annotation.setLength(fSz);
1737       std::string annotation("");
1738       for (int i = 0; i < fSz; i++)
1739         annotation += char(input->readULong(1));
1740       if (!annotation.empty())
1741         f << "annot[comment]=" << annotation << ",";
1742     }
1743   }
1744   else if (dataSz)
1745     ascii().addDelimiter(pos, '|');
1746   input->seek(pos+dataSz, librevenge::RVNG_SEEK_SET);
1747 
1748   pos = input->tell();
1749   ascii().addPos(beginPos);
1750   ascii().addNote(f.str().c_str());
1751   if (pos != endPos)
1752     ascii().addDelimiter(pos, '#');
1753 
1754   return true;
1755 }
1756 
1757 ////////////////////////////////////////////////////////////
1758 // check if a zone is a picture/read a picture
1759 ////////////////////////////////////////////////////////////
checkPicturePos(long pos,int type)1760 bool MsWrdParser::checkPicturePos(long pos, int type)
1761 {
1762   MWAWInputStreamPtr input = getInput();
1763   if (pos < 0x100 || !input->checkPosition(pos))
1764     return false;
1765 
1766   input->seek(pos, librevenge::RVNG_SEEK_SET);
1767   auto sz = long(input->readULong(4));
1768   long endPos = pos+sz;
1769   if (sz < 14 || !input->checkPosition(sz+pos)) return false;
1770   auto num = static_cast<int>(input->readLong(1));
1771   if (num < 0 || num > 4) return false;
1772   input->seek(pos+14, librevenge::RVNG_SEEK_SET);
1773   for (int n = 0; n < num; n++) {
1774     long actPos = input->tell();
1775     auto pSz = long(input->readULong(4));
1776     if (pSz+actPos > endPos) return false;
1777     input->seek(pSz+actPos, librevenge::RVNG_SEEK_SET);
1778   }
1779   if (input->tell() != endPos)
1780     return false;
1781 
1782   static int id = 0;
1783   MsWrdEntry entry;
1784   entry.setBegin(pos);
1785   entry.setEnd(endPos);
1786   entry.setType("Picture");
1787   entry.setPictType(type);
1788   entry.setId(id++);
1789   m_entryMap.insert
1790   (std::multimap<std::string, MsWrdEntry>::value_type(entry.type(), entry));
1791 
1792   return true;
1793 }
1794 
readPicture(MsWrdEntry & entry)1795 bool MsWrdParser::readPicture(MsWrdEntry &entry)
1796 {
1797   if (m_state->m_picturesMap.find(entry.begin())!=m_state->m_picturesMap.end())
1798     return true;
1799   if (entry.length() < 30 && entry.length() != 14) {
1800     MWAW_DEBUG_MSG(("MsWrdParser::readPicture: the zone seems too short\n"));
1801     return false;
1802   }
1803   MWAWInputStreamPtr input = getInput();
1804   long pos = entry.begin();
1805   entry.setParsed(true);
1806   input->seek(pos, librevenge::RVNG_SEEK_SET);
1807   libmwaw::DebugStream f;
1808   f << "Entries(Picture)[" << entry.pictType() << "-" << entry.id() << "]:";
1809   auto sz = long(input->readULong(4));
1810   if (sz > entry.length()) {
1811     MWAW_DEBUG_MSG(("MsWrdParser::readPicture: the zone size seems too big\n"));
1812     return false;
1813   }
1814   auto N = static_cast<int>(input->readULong(1));
1815   f << "N=" << N << ",";
1816   MsWrdParserInternal::Picture pict;
1817   pict.m_flag = static_cast<int>(input->readULong(1)); // find 0 or 0x80
1818   int dim[4];
1819   for (auto &d : dim) d = static_cast<int>(input->readLong(2));
1820   pict.m_dim=MWAWBox2i(MWAWVec2i(dim[1],dim[0]), MWAWVec2i(dim[3],dim[2]));
1821   f << pict;
1822   ascii().addPos(pos);
1823   ascii().addNote(f.str().c_str());
1824 
1825   for (int n=0; n < N; n++) {
1826     MsWrdParserInternal::Picture::Zone zone;
1827     pos = input->tell();
1828     f.str("");
1829     f << "Picture-" << n << "[" << entry.pictType() << "-" << entry.id() << "]:";
1830     sz = long(input->readULong(4));
1831     if (sz < 16 || sz+pos > entry.end()) {
1832       MWAW_DEBUG_MSG(("MsWrdParser::readPicture: pb with the picture size\n"));
1833       f << "#";
1834       ascii().addPos(pos);
1835       ascii().addNote(f.str().c_str());
1836       return false;
1837     }
1838     for (int i = 0; i < 3; ++i) zone.m_flags[i] = static_cast<int>(input->readULong((i==2) ? 2 : 1));
1839     for (auto &d : dim) d = static_cast<int>(input->readLong(2));
1840     zone.m_dim=MWAWBox2i(MWAWVec2i(dim[1],dim[0]), MWAWVec2i(dim[3],dim[2]));
1841     zone.m_pos.setBegin(pos+16);
1842     zone.m_pos.setLength(sz-16);
1843     f << zone;
1844     ascii().addPos(pos);
1845     ascii().addNote(f.str().c_str());
1846     if (sz <= 16)
1847       continue;
1848     pict.m_picturesList.push_back(zone);
1849 #ifdef DEBUG_WITH_FILES
1850     ascii().skipZone(pos+16, pos+sz-1);
1851     librevenge::RVNGBinaryData file;
1852     input->seek(pos+16, librevenge::RVNG_SEEK_SET);
1853     input->readDataBlock(sz-16, file);
1854     static int volatile pictName = 0;
1855     libmwaw::DebugStream f2;
1856     f2 << "PICT-" << ++pictName << ".pct";
1857     libmwaw::Debug::dumpFile(file, f2.str().c_str());
1858 #endif
1859 
1860     input->seek(pos+sz, librevenge::RVNG_SEEK_SET);
1861   }
1862   m_state->m_picturesMap[entry.begin()]=pict;
1863   pos = input->tell();
1864   if (pos != entry.end())
1865     ascii().addDelimiter(pos, '|');
1866   ascii().addPos(entry.end());
1867   ascii().addNote("_");
1868   return true;
1869 }
1870 
sendPicture(long fPos,int cPos,MWAWPosition::AnchorTo anchor)1871 void MsWrdParser::sendPicture(long fPos, int cPos, MWAWPosition::AnchorTo anchor)
1872 {
1873   if (!getTextListener()) {
1874     MWAW_DEBUG_MSG(("MsWrdParser::sendPicture: listener is not set\n"));
1875     return;
1876   }
1877   if ((anchor == MWAWPosition::Char || anchor == MWAWPosition::CharBaseLine) &&
1878       m_state->m_posToCommentMap.find(long(cPos-1))!=m_state->m_posToCommentMap.end()) {
1879     MWAWInputStreamPtr input = getInput();
1880     std::shared_ptr<MWAWSubDocument> subdoc=std::make_shared<MsWrdParserInternal::SubDocument>(*this, input, m_state->m_posToCommentMap.find(long(cPos-1))->second, libmwaw::DOC_COMMENT_ANNOTATION);
1881     getTextListener()->insertComment(subdoc);
1882     return;
1883   }
1884   if (m_state->m_picturesMap.find(fPos)==m_state->m_picturesMap.end()) {
1885     MWAW_DEBUG_MSG(("MsWrdParser::sendPicture: can not find picture for pos %lx\n", static_cast<long unsigned int>(fPos)));
1886     return;
1887   }
1888   auto const &pict= m_state->m_picturesMap.find(fPos)->second;
1889   MWAWInputStreamPtr input = getInput();
1890   if (pict.m_picturesList.size()!=1 &&
1891       (anchor == MWAWPosition::Char || anchor == MWAWPosition::CharBaseLine)) {
1892     std::shared_ptr<MsWrdParserInternal::SubDocument> subdoc
1893     (new MsWrdParserInternal::SubDocument(*this, input, fPos, cPos));
1894     MWAWPosition pictPos(MWAWVec2f(pict.m_dim.min()), MWAWVec2f(pict.m_dim.size()), librevenge::RVNG_POINT);
1895     pictPos.setRelativePosition(MWAWPosition::Char,
1896                                 MWAWPosition::XLeft, MWAWPosition::YTop);
1897     pictPos.m_wrapping =  MWAWPosition::WBackground;
1898     getTextListener()->insertTextBox(pictPos, subdoc);
1899     return;
1900   }
1901   MWAWPosition basicPos(MWAWVec2f(0.,0.), MWAWVec2f(100.,100.), librevenge::RVNG_POINT);
1902   if (anchor != MWAWPosition::Page && anchor != MWAWPosition::Frame) {
1903     basicPos.setRelativePosition(anchor, MWAWPosition::XLeft, MWAWPosition::YCenter);
1904     basicPos.m_wrapping =  MWAWPosition::WBackground;
1905   }
1906   else
1907     basicPos.setRelativePosition(anchor);
1908 
1909   long actPos = input->tell();
1910   MWAWBox2f naturalBox;
1911   int n=0;
1912   for (auto const &zone : pict.m_picturesList) {
1913     n++;
1914     if (!zone.m_pos.valid()) continue;
1915     MWAWPosition pos(basicPos);
1916     pos.setOrigin(pos.origin()+MWAWVec2f(zone.m_dim.min()));
1917     pos.setSize(MWAWVec2f(zone.m_dim.size()));
1918 
1919     input->seek(zone.m_pos.begin(), librevenge::RVNG_SEEK_SET);
1920     MWAWPict::ReadResult res = MWAWPictData::check(input, static_cast<int>(zone.m_pos.length()), naturalBox);
1921     if (res == MWAWPict::MWAW_R_BAD) {
1922       MWAW_DEBUG_MSG(("MsWrdParser::sendPicture: can not find the picture %d\n", int(n-1)));
1923       continue;
1924     }
1925 
1926     input->seek(zone.m_pos.begin(), librevenge::RVNG_SEEK_SET);
1927     std::shared_ptr<MWAWPict> thePict(MWAWPictData::get(input, static_cast<int>(zone.m_pos.length())));
1928     if (!thePict) continue;
1929     MWAWEmbeddedObject picture;
1930     if (thePict->getBinary(picture))
1931       getTextListener()->insertPicture(pos, picture);
1932   }
1933   input->seek(actPos, librevenge::RVNG_SEEK_SET);
1934 }
1935 
1936 ////////////////////////////////////////////////////////////
1937 // read the print info
1938 ////////////////////////////////////////////////////////////
readPrintInfo(MsWrdEntry & entry)1939 bool MsWrdParser::readPrintInfo(MsWrdEntry &entry)
1940 {
1941   if (entry.length() < 0x78) {
1942     MWAW_DEBUG_MSG(("MsWrdParser::readPrintInfo: the zone seems to short\n"));
1943     return false;
1944   }
1945   MWAWInputStreamPtr input = getInput();
1946   long pos = entry.begin();
1947   entry.setParsed(true);
1948   input->seek(pos, librevenge::RVNG_SEEK_SET);
1949   libmwaw::DebugStream f;
1950   // print info
1951   libmwaw::PrinterInfo info;
1952   if (!info.read(input)) return false;
1953   f << "PrintInfo:"<< info;
1954 
1955   MWAWVec2i paperSize = info.paper().size();
1956   MWAWVec2i pageSize = info.page().size();
1957   if (pageSize.x() <= 0 || pageSize.y() <= 0 ||
1958       paperSize.x() <= 0 || paperSize.y() <= 0) return false;
1959 
1960   // define margin from print info
1961   MWAWVec2i lTopMargin= -1 * info.paper().pos(0);
1962   MWAWVec2i rBotMargin=info.paper().size() - info.page().size();
1963 
1964   // move margin left | top
1965   int decalX = lTopMargin.x() > 14 ? lTopMargin.x()-14 : 0;
1966   int decalY = lTopMargin.y() > 14 ? lTopMargin.y()-14 : 0;
1967   lTopMargin -= MWAWVec2i(decalX, decalY);
1968   rBotMargin += MWAWVec2i(decalX, decalY);
1969 
1970   int leftMargin = lTopMargin.x();
1971   int topMargin = lTopMargin.y();
1972 
1973   // decrease right | bottom
1974   int rightMarg = rBotMargin.x() -50;
1975   if (rightMarg < 0) {
1976     leftMargin -= (-rightMarg);
1977     if (leftMargin < 0) leftMargin=0;
1978     rightMarg=0;
1979   }
1980   int botMarg = rBotMargin.y() -50;
1981   if (botMarg < 0) {
1982     topMargin -= (-botMarg);
1983     if (topMargin < 0) topMargin=0;
1984     botMarg=0;
1985   }
1986 
1987   getPageSpan().setFormOrientation(MWAWPageSpan::PORTRAIT);
1988   getPageSpan().setMarginTop(topMargin/72.0);
1989   getPageSpan().setMarginBottom(botMarg/72.0);
1990   getPageSpan().setMarginLeft(leftMargin/72.0);
1991   getPageSpan().setMarginRight(rightMarg/72.0);
1992   getPageSpan().setFormLength(paperSize.y()/72.);
1993   getPageSpan().setFormWidth(paperSize.x()/72.);
1994 
1995   ascii().addPos(pos);
1996   ascii().addNote(f.str().c_str());
1997 
1998   if (long(input->tell()) != entry.end())
1999     ascii().addDelimiter(input->tell(), '|');
2000 
2001   ascii().addPos(entry.end());
2002   ascii().addNote("_");
2003 
2004   return true;
2005 }
2006 
2007 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
2008