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 /* Inspired of TN-012-Disk-Based-MW-Format.txt */
35 
36 #include <iomanip>
37 #include <iostream>
38 #include <limits>
39 #include <sstream>
40 
41 #include <librevenge/librevenge.h>
42 
43 #include "MWAWTextListener.hxx"
44 #include "MWAWFont.hxx"
45 #include "MWAWFontConverter.hxx"
46 #include "MWAWHeader.hxx"
47 #include "MWAWParagraph.hxx"
48 #include "MWAWPictMac.hxx"
49 #include "MWAWPosition.hxx"
50 #include "MWAWPrinter.hxx"
51 #include "MWAWRSRCParser.hxx"
52 #include "MWAWSubDocument.hxx"
53 
54 #include "MacWrtParser.hxx"
55 
56 /** Internal: the structures of a MacWrtParser */
57 namespace MacWrtParserInternal
58 {
59 
60 //! Document header
61 struct FileHeader {
FileHeaderMacWrtParserInternal::FileHeader62   FileHeader()
63     : m_hideFirstPageHeaderFooter(false)
64     , m_startNumberPage(1)
65     , m_freeListPos(0)
66     , m_freeListLength(0)
67     , m_freeListAllocated(0)
68     , m_dataPos(0)
69   {
70     for (auto &num : m_numParagraphs) num = 0;
71   }
72 
73   friend std::ostream &operator<<(std::ostream &o, FileHeader const &header);
74 
75   //! the number of lines : text, header footer
76   int m_numParagraphs[3];
77   //! true if the first page header/footer must be draw
78   bool m_hideFirstPageHeaderFooter;
79   //! the first number page
80   int m_startNumberPage;
81   //! free list start position
82   long m_freeListPos;
83   //! free list length
84   long m_freeListLength;
85   //! free list allocated
86   long m_freeListAllocated;
87   //! the begin of data ( if version == 3)
88   long m_dataPos;
89 };
90 
operator <<(std::ostream & o,FileHeader const & header)91 std::ostream &operator<<(std::ostream &o, FileHeader const &header)
92 {
93   for (int i=0; i < 3; i++) {
94     if (!header.m_numParagraphs[i]) continue;
95     o << "numParagraph";
96     if (i==1) o << "[header]";
97     else if (i==2) o << "[footer]";
98     o << "=" << header.m_numParagraphs[i] << ",";
99   }
100   if (header.m_hideFirstPageHeaderFooter)
101     o << "noHeaderFooter[FirstPage],";
102   if (header.m_startNumberPage != 1)
103     o << "firstPageNumber=" << header.m_startNumberPage << ",";
104   if (header.m_freeListPos) {
105     o << "FreeList=" << std::hex
106       << header.m_freeListPos
107       << "[" << header.m_freeListLength << "+" << header.m_freeListAllocated << "],"
108       << std::dec << ",";
109   }
110   if (header.m_dataPos)
111     o << "DataPos="  << std::hex << header.m_dataPos << std::dec << ",";
112 
113   return o;
114 }
115 
116 ////////////////////////////////////////
117 //! the paragraph... information
118 struct Information {
119   /** the different type */
120   enum Type { TEXT, RULER, GRAPHIC, PAGEBREAK, UNKNOWN };
121 
122   //! constructor
InformationMacWrtParserInternal::Information123   Information()
124     : m_type(UNKNOWN)
125     , m_compressed(false)
126     , m_pos()
127     , m_height(0)
128     , m_justify(MWAWParagraph::JustificationLeft)
129     , m_justifySet(false)
130     , m_data()
131     , m_font()
132   {
133   }
134 
135   //! operator<<
136   friend std::ostream &operator<<(std::ostream &o, Information const &info);
137 
138   //! the type
139   Type m_type;
140 
141   //! a flag to know if the text data are compressed
142   bool m_compressed;
143 
144   //! top left position
145   MWAWPosition m_pos;
146 
147   //! the paragraph height
148   int m_height;
149 
150   //! paragraph justification : MWAW_PARAGRAPH_JUSTIFICATION*
151   MWAWParagraph::Justification m_justify;
152 
153   //! true if the justification must be used
154   bool m_justifySet;
155   //! the position in the file
156   MWAWEntry m_data;
157 
158   //! the font
159   MWAWFont m_font;
160 };
161 
operator <<(std::ostream & o,Information const & info)162 std::ostream &operator<<(std::ostream &o, Information const &info)
163 {
164   switch (info.m_type) {
165   case Information::TEXT:
166     o << "text";
167     if (info.m_compressed) o << "[compressed]";
168     o << ",";
169     break;
170   case Information::RULER:
171     o << "indent,";
172     break;
173   case Information::GRAPHIC:
174     o << "graphics,";
175     break;
176   case Information::PAGEBREAK:
177     o << "pageBreak,";
178     break;
179   case Information::UNKNOWN:
180 #if !defined(__clang__)
181   default:
182 #endif
183     o << "###unknownType,";
184     break;
185   }
186   o << info.m_pos << ",";
187   if (info.m_height) o << "height=" << info.m_height << ",";
188 
189   if (info.m_justifySet) {
190     switch (info.m_justify) {
191     case MWAWParagraph::JustificationLeft:
192       o << "left[justify],";
193       break;
194     case MWAWParagraph::JustificationCenter:
195       o << "center[justify],";
196       break;
197     case MWAWParagraph::JustificationRight:
198       o << "right[justify],";
199       break;
200     case MWAWParagraph::JustificationFull:
201       o << "full[justify],";
202       break;
203     case MWAWParagraph::JustificationFullAllLines:
204       o << "fullAllLines[justify],";
205       break;
206 #if !defined(__clang__)
207     default:
208       o << "###unknown[justify],";
209       break;
210 #endif
211     }
212   }
213   if (info.m_data.begin() > 0)
214     o << std::hex << "data=[" << info.m_data.begin() << "-" << info.m_data.end() << "]," << std::dec;
215   return o;
216 }
217 
218 ////////////////////////////////////////
219 //! the windows structure
220 struct WindowsInfo {
WindowsInfoMacWrtParserInternal::WindowsInfo221   WindowsInfo()
222     : m_startSel()
223     , m_endSel()
224     , m_posTopY(0)
225     , m_informations()
226     , m_firstParagLine()
227     , m_linesHeight()
228     , m_pageNumber()
229     , m_date()
230     , m_time()
231   {
232   }
233 
234   /** small function used to recognized empty header or footer */
isEmptyMacWrtParserInternal::WindowsInfo235   bool isEmpty() const
236   {
237     if (m_informations.size() == 0) return true;
238     if (m_pageNumber.x() >= 0 || m_date.x() >= 0 || m_time.x() >= 0)
239       return false;
240     if (m_informations.size() > 2) return false;
241     for (auto const &info : m_informations) {
242       switch (info.m_type) {
243       case Information::GRAPHIC:
244         return false;
245       case Information::TEXT:
246         if (info.m_data.length() != 10)
247           return false;
248         // empty line : ok
249         break;
250       case Information::RULER:
251       case Information::PAGEBREAK:
252       case Information::UNKNOWN:
253 #if !defined(__clang__)
254       default:
255 #endif
256         break;
257       }
258     }
259     return true;
260   }
261 
262   friend std::ostream &operator<<(std::ostream &o, WindowsInfo const &w);
263 
264   MWAWVec2i m_startSel, m_endSel; // start end selection (parag, char)
265   int m_posTopY;
266   std::vector<Information> m_informations;
267   std::vector<int> m_firstParagLine, m_linesHeight;
268   MWAWVec2i m_pageNumber, m_date, m_time;
269 };
270 
operator <<(std::ostream & o,WindowsInfo const & w)271 std::ostream &operator<<(std::ostream &o, WindowsInfo const &w)
272 {
273   o << "sel=[" << w.m_startSel << "-" << w.m_endSel << "],";
274   if (w.m_posTopY) o << "windowsY=" << w.m_posTopY << ",";
275   o << "pageNumberPos=" << w.m_pageNumber << ",";
276   o << "datePos=" << w.m_date << ",";
277   o << "timePos=" << w.m_time << ",";
278   return o;
279 }
280 
281 ////////////////////////////////////////
282 //! Internal: the state of a MacWrtParser
283 struct State {
284   //! constructor
StateMacWrtParserInternal::State285   State()
286     : m_compressCorr(" etnroaisdlhcfp")
287     , m_actPage(0)
288     , m_numPages(0)
289     , m_fileHeader()
290     , m_headerHeight(0)
291     , m_footerHeight(0)
292   {
293   }
294 
295   //! the correspondance between int compressed and char : must be 15 character
296   std::string m_compressCorr;
297 
298   int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
299 
300   //! the header
301   FileHeader m_fileHeader;
302 
303   //! the information of main document, header, footer
304   WindowsInfo m_windows[3];
305 
306   int m_headerHeight /** the header height if known */,
307       m_footerHeight /** the footer height if known */;
308 };
309 
310 ////////////////////////////////////////
311 //! Internal: the subdocument of a MacWrtParser
312 class SubDocument final : public MWAWSubDocument
313 {
314 public:
SubDocument(MacWrtParser & pars,MWAWInputStreamPtr const & input,int zoneId)315   SubDocument(MacWrtParser &pars, MWAWInputStreamPtr const &input, int zoneId)
316     : MWAWSubDocument(&pars, input, MWAWEntry())
317     , m_id(zoneId)
318   {
319   }
320 
321   //! destructor
~SubDocument()322   ~SubDocument() final {}
323 
324   //! operator!=
325   bool operator!=(MWAWSubDocument const &doc) const final;
326 
327   //! the parser function
328   void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
329 
330 protected:
331   //! the subdocument id
332   int m_id;
333 };
334 
parse(MWAWListenerPtr & listener,libmwaw::SubDocumentType)335 void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
336 {
337   if (!listener.get()) {
338     MWAW_DEBUG_MSG(("MacWrtParserInternal::SubDocument::parse: no listener\n"));
339     return;
340   }
341   if (m_id != 1 && m_id != 2) {
342     MWAW_DEBUG_MSG(("MacWrtParserInternal::SubDocument::parse: unknown zone\n"));
343     return;
344   }
345   auto *parser=dynamic_cast<MacWrtParser *>(m_parser);
346   if (!parser) {
347     MWAW_DEBUG_MSG(("MacWrtParserInternal::SubDocument::parse: no parser\n"));
348     return;
349   }
350 
351   long pos = m_input->tell();
352   parser->sendWindow(m_id);
353   m_input->seek(pos, librevenge::RVNG_SEEK_SET);
354 }
355 
operator !=(MWAWSubDocument const & doc) const356 bool SubDocument::operator!=(MWAWSubDocument const &doc) const
357 {
358   if (MWAWSubDocument::operator!=(doc)) return true;
359   auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
360   if (!sDoc) return true;
361   if (m_id != sDoc->m_id) return true;
362   return false;
363 }
364 }
365 
366 ////////////////////////////////////////////////////////////
367 // constructor/destructor, ...
368 ////////////////////////////////////////////////////////////
MacWrtParser(MWAWInputStreamPtr const & input,MWAWRSRCParserPtr const & rsrcParser,MWAWHeader * header)369 MacWrtParser::MacWrtParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
370   : MWAWTextParser(input, rsrcParser, header)
371   , m_state()
372 {
373   init();
374 }
375 
~MacWrtParser()376 MacWrtParser::~MacWrtParser()
377 {
378 }
379 
init()380 void MacWrtParser::init()
381 {
382   resetTextListener();
383   setAsciiName("main-1");
384 
385   m_state.reset(new MacWrtParserInternal::State);
386 
387   // reduce the margin (in case, the page is not defined)
388   getPageSpan().setMargins(0.1);
389 }
390 
391 ////////////////////////////////////////////////////////////
392 // new page
393 ////////////////////////////////////////////////////////////
newPage(int number)394 void MacWrtParser::newPage(int number)
395 {
396   if (number <= m_state->m_actPage || number > m_state->m_numPages)
397     return;
398 
399   while (m_state->m_actPage < number) {
400     m_state->m_actPage++;
401     if (!getTextListener() || m_state->m_actPage == 1)
402       continue;
403     getTextListener()->insertBreak(MWAWTextListener::PageBreak);
404   }
405 }
406 
407 ////////////////////////////////////////////////////////////
408 // the parser
409 ////////////////////////////////////////////////////////////
parse(librevenge::RVNGTextInterface * docInterface)410 void MacWrtParser::parse(librevenge::RVNGTextInterface *docInterface)
411 {
412   if (!getInput().get() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
413   bool ok = true;
414   try {
415     // create the asciiFile
416     ascii().setStream(getInput());
417     ascii().open(asciiName());
418     checkHeader(nullptr);
419     if (getRSRCParser()) {
420       MWAWEntry corrEntry = getRSRCParser()->getEntry("STR ", 700);
421       std::string corrString("");
422       if (corrEntry.valid() && getRSRCParser()->parseSTR(corrEntry, corrString)) {
423         if (corrString.length() != 15) {
424           MWAW_DEBUG_MSG(("MacWrtParser::parse: resource correspondance string seems bad\n"));
425         }
426         else
427           m_state->m_compressCorr = corrString;
428       }
429     }
430     ok = (version() <= 3) ? createZonesV3() : createZones();
431     if (ok) {
432       createDocument(docInterface);
433       sendWindow(0);
434     }
435 
436     ascii().reset();
437   }
438   catch (...) {
439     MWAW_DEBUG_MSG(("MacWrtParser::parse: exception catched when parsing\n"));
440     ok = false;
441   }
442 
443   resetTextListener();
444   if (!ok) throw(libmwaw::ParseException());
445 }
446 
447 ////////////////////////////////////////////////////////////
448 // create the document
449 ////////////////////////////////////////////////////////////
createDocument(librevenge::RVNGTextInterface * documentInterface)450 void MacWrtParser::createDocument(librevenge::RVNGTextInterface *documentInterface)
451 {
452   if (!documentInterface) return;
453   if (getTextListener()) {
454     MWAW_DEBUG_MSG(("MacWrtParser::createDocument: listener already exist\n"));
455     return;
456   }
457 
458   // update the page
459   m_state->m_actPage = 0;
460 
461   // create the page list
462   MWAWPageSpan ps(getPageSpan());
463   for (int i = 1; i < 3; i++) {
464     if (m_state->m_windows[i].isEmpty()) {
465 #ifdef DEBUG
466       sendWindow(i); // force the parsing
467 #endif
468       continue;
469     }
470     MWAWHeaderFooter hF((i==1) ? MWAWHeaderFooter::HEADER : MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
471     hF.m_subDocument.reset(new MacWrtParserInternal::SubDocument(*this, getInput(), i));
472     ps.setHeaderFooter(hF);
473   }
474 
475   std::vector<MWAWPageSpan> pageList;
476   if (m_state->m_fileHeader.m_hideFirstPageHeaderFooter) {
477     pageList.push_back(getPageSpan());
478     ps.setPageSpan(m_state->m_numPages);
479   }
480   else
481     ps.setPageSpan(m_state->m_numPages+1);
482   if (ps.getPageSpan())
483     pageList.push_back(ps);
484   //
485   MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
486   setTextListener(listen);
487   listen->startDocument();
488 }
489 
490 
491 ////////////////////////////////////////////////////////////
492 //
493 // Intermediate level
494 //
495 ////////////////////////////////////////////////////////////
createZones()496 bool MacWrtParser::createZones()
497 {
498   MWAWInputStreamPtr input = getInput();
499   long pos = input->tell();
500 
501   if (!readPrintInfo()) {
502     // bad sign, but we can try to recover
503     ascii().addPos(pos);
504     ascii().addNote("###PrintInfo");
505     input->seek(pos+0x78, librevenge::RVNG_SEEK_SET);
506   }
507 
508   pos = input->tell();
509   for (int i = 0; i < 3; i++) {
510     if (readWindowsInfo(i))
511       continue;
512     if (i == 2) return false; // problem on the main zone, better quit
513 
514     // reset state
515     m_state->m_windows[2-i] = MacWrtParserInternal::WindowsInfo();
516     int const windowsSize = 46;
517 
518     // and try to continue
519     input->seek(pos+(i+1)*windowsSize, librevenge::RVNG_SEEK_SET);
520   }
521 
522 #ifdef DEBUG
523   checkFreeList();
524 #endif
525 
526   // ok, we can find calculate the number of pages and the header and the footer height
527   for (int i = 1; i < 3; i++) {
528     auto const &info = m_state->m_windows[i];
529     if (info.isEmpty()) // avoid reserving space for empty header/footer
530       continue;
531     int height = 0;
532     for (auto const &inf : info.m_informations)
533       height+=inf.m_height;
534     if (i == 1) m_state->m_headerHeight = height;
535     else m_state->m_footerHeight = height;
536   }
537   int numPages = 0;
538   auto const &mainInfo = m_state->m_windows[0];
539   for (auto &info : mainInfo.m_informations) {
540     if (info.m_pos.page() > numPages)
541       numPages = info.m_pos.page();
542   }
543   m_state->m_numPages = numPages+1;
544 
545   return true;
546 }
547 
createZonesV3()548 bool MacWrtParser::createZonesV3()
549 {
550   MWAWInputStreamPtr input = getInput();
551   long pos = input->tell();
552 
553   if (!readPrintInfo()) {
554     // bad sign, but we can try to recover
555     ascii().addPos(pos);
556     ascii().addNote("###PrintInfo");
557     input->seek(pos+0x78, librevenge::RVNG_SEEK_SET);
558   }
559 
560   pos = input->tell();
561   for (int i = 0; i < 3; i++) {
562     if (readWindowsInfo(i))
563       continue;
564     if (i == 2) return false; // problem on the main zone, better quit
565 
566     // reset state
567     m_state->m_windows[2-i] = MacWrtParserInternal::WindowsInfo();
568     int const windowsSize = 34;
569 
570     // and try to continue
571     input->seek(pos+(i+1)*windowsSize, librevenge::RVNG_SEEK_SET);
572   }
573 
574   auto const &header = m_state->m_fileHeader;
575 
576   for (int i = 0; i < 3; i++) {
577     if (!readInformationsV3
578         (header.m_numParagraphs[i], m_state->m_windows[i].m_informations))
579       return false;
580   }
581   if (int(input->tell()) != header.m_dataPos) {
582     MWAW_DEBUG_MSG(("MacWrtParser::createZonesV3: pb with dataPos\n"));
583     ascii().addPos(input->tell());
584     ascii().addNote("###FileHeader");
585 
586     // posibility to do very bad thing from here, so we stop
587     if (int(input->tell()) > header.m_dataPos)
588       return false;
589 
590     // and try to continue
591     input->seek(header.m_dataPos, librevenge::RVNG_SEEK_SET);
592     if (int(input->tell()) != header.m_dataPos)
593       return false;
594   }
595   for (int z = 0; z < 3; z++) {
596     int numParag = header.m_numParagraphs[z];
597     auto &wInfo = m_state->m_windows[z];
598     for (int p = 0; p < numParag; p++) {
599       pos = input->tell();
600       auto type = static_cast<int>(input->readLong(2));
601       auto sz = static_cast<int>(input->readLong(2));
602       input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
603       if (sz < 0 || long(input->tell()) !=  pos+4+sz) {
604         MWAW_DEBUG_MSG(("MacWrtParser::createZonesV3: pb with dataZone\n"));
605         return (p != 0);
606       }
607       MWAWEntry entry;
608       entry.setBegin(pos+4);
609       entry.setLength(sz);
610       if (int(wInfo.m_informations.size()) <= p)
611         continue;
612       wInfo.m_informations[size_t(p)].m_data = entry;
613       auto newType = MacWrtParserInternal::Information::UNKNOWN;
614 
615       switch ((type & 0x7)) {
616       case 0:
617         newType=MacWrtParserInternal::Information::RULER;
618         break;
619       case 1:
620         newType=MacWrtParserInternal::Information::TEXT;
621         break;
622       case 2:
623         newType=MacWrtParserInternal::Information::PAGEBREAK;
624         break;
625       default:
626         break;
627       }
628       if (newType != wInfo.m_informations[size_t(p)].m_type) {
629         MWAW_DEBUG_MSG(("MacWrtParser::createZonesV3: types are inconstant\n"));
630         if (newType != MacWrtParserInternal::Information::UNKNOWN)
631           wInfo.m_informations[size_t(p)].m_type = newType;
632       }
633     }
634   }
635   if (!input->isEnd()) {
636     ascii().addPos(input->tell());
637     ascii().addNote("Entries(END)");
638   }
639 
640   int numPages = 0;
641   auto const &mainInfo = m_state->m_windows[0];
642   for (auto const &info : mainInfo.m_informations) {
643     if (info.m_pos.page() > numPages)
644       numPages = info.m_pos.page();
645   }
646   m_state->m_numPages = numPages+1;
647   return true;
648 }
649 
sendWindow(int zone)650 bool MacWrtParser::sendWindow(int zone)
651 {
652   if (zone < 0 || zone >= 3) {
653     MWAW_DEBUG_MSG(("MacWrtParser::sendWindow: invalid zone %d\n", zone));
654     return false;
655   }
656 
657   auto const &info = m_state->m_windows[zone];
658   size_t numInfo = info.m_informations.size();
659   auto numPara = int(info.m_firstParagLine.size());
660 
661   if (version() <= 3 && zone == 0)
662     newPage(1);
663   for (size_t i=0; i < numInfo; i++) {
664     if (zone == 0)
665       newPage(info.m_informations[i].m_pos.page()+1);
666     switch (info.m_informations[i].m_type) {
667     case MacWrtParserInternal::Information::TEXT:
668       if (!zone || info.m_informations[i].m_data.length() != 10) {
669         std::vector<int> lineHeight;
670         if (int(i) < numPara) {
671           int firstLine = info.m_firstParagLine[i];
672           int lastLine = (int(i+1) < numPara) ?  info.m_firstParagLine[i+1] : int(info.m_linesHeight.size());
673           for (int line = firstLine; line < lastLine; line++)
674             lineHeight.push_back(info.m_linesHeight[size_t(line)]);
675         }
676         readText(info.m_informations[i], lineHeight);
677       }
678       break;
679     case MacWrtParserInternal::Information::RULER:
680       readParagraph(info.m_informations[i]);
681       break;
682     case MacWrtParserInternal::Information::GRAPHIC:
683       readGraphic(info.m_informations[i]);
684       break;
685     case MacWrtParserInternal::Information::PAGEBREAK:
686       readPageBreak(info.m_informations[i]);
687       if (zone == 0 && version() <= 3)
688         newPage(info.m_informations[i].m_pos.page()+2);
689       break;
690     case MacWrtParserInternal::Information::UNKNOWN:
691 #if !defined(__clang__)
692     default:
693 #endif
694       break;
695     }
696   }
697   if (getTextListener() && zone) {
698     // FIXME: try to insert field in the good place
699     if (info.m_pageNumber.x() >= 0 && info.m_pageNumber.y() >= 0)
700       getTextListener()->insertField(MWAWField(MWAWField::PageNumber));
701     if (info.m_date.x() >= 0 && info.m_date.y() >= 0)
702       getTextListener()->insertField(MWAWField(MWAWField::Date));
703     if (info.m_time.x() >= 0 && info.m_time.y() >= 0)
704       getTextListener()->insertField(MWAWField(MWAWField::Time));
705   }
706   return true;
707 }
708 
709 ////////////////////////////////////////////////////////////
710 //
711 // Low level
712 //
713 ////////////////////////////////////////////////////////////
714 
715 ////////////////////////////////////////////////////////////
716 // read the header
717 ////////////////////////////////////////////////////////////
checkHeader(MWAWHeader * header,bool)718 bool MacWrtParser::checkHeader(MWAWHeader *header, bool /*strict*/)
719 {
720   *m_state = MacWrtParserInternal::State();
721   MacWrtParserInternal::FileHeader fHeader = m_state->m_fileHeader;
722 
723   MWAWInputStreamPtr input = getInput();
724   if (!input || !input->hasDataFork())
725     return false;
726 
727   libmwaw::DebugStream f;
728   int headerSize=40;
729   if (!input->checkPosition(headerSize)) {
730     MWAW_DEBUG_MSG(("MacWrtParser::checkHeader: file is too short\n"));
731     return false;
732   }
733   input->seek(0,librevenge::RVNG_SEEK_SET);
734 
735   auto vers = static_cast<int>(input->readULong(2));
736   setVersion(vers);
737 
738   std::string vName("");
739 
740   switch (vers) {
741   case 3:
742     vName="v1.0-2.2";
743     break;
744   case 6: // version 4.5 ( also version 5.01 of Claris MacWrite )
745     vName="v4.5-5.01";
746     break;
747   default:
748     MWAW_DEBUG_MSG(("MacWrtParser::checkHeader: unknown version\n"));
749     return false;
750   }
751   if (vName.empty()) {
752     MWAW_DEBUG_MSG(("Maybe a MacWrite file unknown version(%d)\n", vers));
753   }
754   else {
755     MWAW_DEBUG_MSG(("MacWrite file %s\n", vName.c_str()));
756   }
757 
758   f << "FileHeader: vers=" << vers << ",";
759 
760   if (vers <= 3) fHeader.m_dataPos = static_cast<int>(input->readULong(2));
761 
762   for (int &numParagraph : fHeader.m_numParagraphs) {
763     auto numParag = static_cast<int>(input->readLong(2));
764     numParagraph = numParag;
765     if (numParag < 0) {
766       MWAW_DEBUG_MSG(("MacWrtParser::checkHeader: numParagraphs is negative : %d\n",
767                       numParag));
768       return false;
769     }
770   }
771 
772   if (vers <= 3) {
773     input->seek(6, librevenge::RVNG_SEEK_CUR); // unknown
774     if (input->readLong(1)) f << "hasFooter(?);";
775     if (input->readLong(1)) f << "hasHeader(?),";
776     fHeader.m_startNumberPage = static_cast<int>(input->readLong(2));
777     headerSize=20;
778   }
779   else {
780     fHeader.m_hideFirstPageHeaderFooter = (input->readULong(1)==0xFF);
781 
782     input->seek(7, librevenge::RVNG_SEEK_CUR); // unused + 4 display flags + active doc
783     fHeader.m_startNumberPage = static_cast<int>(input->readLong(2));
784     fHeader.m_freeListPos = long(input->readULong(4));
785     fHeader.m_freeListLength = static_cast<int>(input->readULong(2));
786     fHeader.m_freeListAllocated = static_cast<int>(input->readULong(2));
787     // 14 unused
788   }
789   f << fHeader;
790 
791   //
792   input->seek(headerSize, librevenge::RVNG_SEEK_SET);
793   if (!readPrintInfo()) {
794     input->seek(headerSize, librevenge::RVNG_SEEK_SET);
795     for (int i=0; i<10; ++i) // allow print info to be zero
796       if (input->readLong(2)) return false;
797     input->seek(headerSize+0x78, librevenge::RVNG_SEEK_SET);
798     for (int i=0; i<3; ++i)
799       if (!readWindowsInfo(i) && i==2) return false;
800   }
801   if (!input->checkPosition(vers <= 3 ? fHeader.m_dataPos : fHeader.m_freeListPos))
802     return false;
803 
804   input->seek(headerSize, librevenge::RVNG_SEEK_SET);
805   m_state->m_fileHeader = fHeader;
806 
807   // ok, we can finish initialization
808   if (header)
809     header->reset(MWAWDocument::MWAW_T_MACWRITE, version());
810 
811   ascii().addPos(0);
812   ascii().addNote(f.str().c_str());
813   ascii().addPos(headerSize);
814 
815   return true;
816 }
817 
818 ////////////////////////////////////////////////////////////
819 // read the print info
820 ////////////////////////////////////////////////////////////
readPrintInfo()821 bool MacWrtParser::readPrintInfo()
822 {
823   MWAWInputStreamPtr input = getInput();
824   long pos = input->tell();
825   libmwaw::DebugStream f;
826   // print info
827   libmwaw::PrinterInfo info;
828   if (!info.read(input)) return false;
829   f << "Entries(PrintInfo):"<< info;
830 
831   MWAWVec2i paperSize = info.paper().size();
832   MWAWVec2i pageSize = info.page().size();
833   if (pageSize.x() <= 0 || pageSize.y() <= 0 ||
834       paperSize.x() <= 0 || paperSize.y() <= 0) return false;
835 
836   // define margin from print info
837   MWAWVec2i lTopMargin= -1 * info.paper().pos(0);
838   MWAWVec2i rBotMargin=info.paper().pos(1) - info.page().pos(1);
839 
840   // move margin left | top
841   int decalX = lTopMargin.x() > 14 ? lTopMargin.x()-14 : 0;
842   int decalY = lTopMargin.y() > 14 ? lTopMargin.y()-14 : 0;
843   lTopMargin -= MWAWVec2i(decalX, decalY);
844   rBotMargin += MWAWVec2i(decalX, decalY);
845 
846   // decrease right | bottom
847   int rightMarg = rBotMargin.x() -10;
848   if (rightMarg < 0) rightMarg=0;
849   int botMarg = rBotMargin.y() -50;
850   if (botMarg < 0) botMarg=0;
851 
852   getPageSpan().setMarginTop(lTopMargin.y()/72.0);
853   getPageSpan().setMarginBottom(botMarg/72.0);
854   getPageSpan().setMarginLeft(lTopMargin.x()/72.0);
855   getPageSpan().setMarginRight(rightMarg/72.0);
856   getPageSpan().setFormLength(paperSize.y()/72.);
857   getPageSpan().setFormWidth(paperSize.x()/72.);
858 
859   ascii().addPos(pos);
860   ascii().addNote(f.str().c_str());
861   input->seek(pos+0x78, librevenge::RVNG_SEEK_SET);
862   if (long(input->tell()) != pos+0x78) {
863     MWAW_DEBUG_MSG(("MacWrtParser::readPrintInfo: file is too short\n"));
864     return false;
865   }
866   ascii().addPos(input->tell());
867 
868   return true;
869 }
870 
871 ////////////////////////////////////////////////////////////
872 // read the windows info
873 ////////////////////////////////////////////////////////////
readWindowsInfo(int wh)874 bool MacWrtParser::readWindowsInfo(int wh)
875 {
876   MWAWInputStreamPtr input = getInput();
877   long pos = input->tell();
878   int windowsSize = version() <= 3 ? 34 : 46;
879 
880   input->seek(pos+windowsSize, librevenge::RVNG_SEEK_SET);
881   if (long(input->tell()) !=pos+windowsSize) {
882     MWAW_DEBUG_MSG(("MacWrtParser::readWindowsInfo: file is too short\n"));
883     return false;
884   }
885 
886   input->seek(pos, librevenge::RVNG_SEEK_SET);
887   libmwaw::DebugStream f;
888   f << "Entries(Windows)";
889   switch (wh) {
890   case 0:
891     f << "[Footer]";
892     break;
893   case 1:
894     f << "[Header]";
895     break;
896   case 2:
897     break;
898   default:
899     MWAW_DEBUG_MSG(("MacWrtParser::readWindowsInfo: called with bad which=%d\n",wh));
900     return false;
901   }
902 
903   int which = 2-wh;
904   auto &info = m_state->m_windows[which];
905   f << ": ";
906 
907   MWAWEntry informations;
908   MWAWEntry lineHeightEntry;
909 
910   for (int i = 0; i < 2; i++) {
911     auto x = static_cast<int>(input->readLong(2));
912     auto y = static_cast<int>(input->readLong(2));
913     if (i == 0) info.m_startSel = MWAWVec2i(x,y);
914     else info.m_endSel = MWAWVec2i(x,y);
915   }
916 
917   if (version() <= 3) {
918     for (int i = 0; i < 2; i++) {
919       auto val = static_cast<int>(input->readLong(2));
920       if (val) f << "unkn" << i << "=" << val << ",";
921     }
922   }
923   else {
924     info.m_posTopY = static_cast<int>(input->readLong(2));
925     input->seek(2,librevenge::RVNG_SEEK_CUR); // need to redraw
926     informations.setBegin(long(input->readULong(4)));
927     informations.setLength(long(input->readULong(2)));
928     informations.setId(which);
929 
930     lineHeightEntry.setBegin(long(input->readULong(4)));
931     lineHeightEntry.setLength(long(input->readULong(2)));
932     lineHeightEntry.setId(which);
933 
934     f << std::hex
935       << "lineHeight=[" << lineHeightEntry.begin() << "-" << lineHeightEntry.end() << "],"
936       << "informations=[" << informations.begin() << "-" << informations.end() << "],"
937       << std::dec;
938   }
939   for (int i = 0; i < 3; i++) {
940     auto x = static_cast<int>(input->readLong(2));
941     auto y = static_cast<int>(input->readLong(2));
942     if (i == 0) info.m_pageNumber = MWAWVec2i(x,y);
943     else if (i == 1) info.m_date = MWAWVec2i(x,y);
944     else info.m_time = MWAWVec2i(x,y);
945   }
946   f << info;
947   bool ok=true;
948   if (version() <= 3) {
949     input->seek(6,librevenge::RVNG_SEEK_CUR); // unknown flags: ff ff ff ff ff 00
950     f << "actFont=" << input->readLong(1) << ",";
951     for (int i= 0; i < 2; i++) {
952       auto val = static_cast<int>(input->readULong(1));
953       if (val==255) f << "f" << i << "=true,";
954     }
955     f << "flg=" << input->readLong(1);
956   }
957   else {
958     input->seek(4,librevenge::RVNG_SEEK_CUR); // unused
959     if (input->readULong(1) == 0xFF) f << "redrawOval,";
960     if (input->readULong(1) == 0xFF) f << "lastOvalUpdate,";
961     f << "actStyle=" << input->readLong(2) << ",";
962     f << "actFont=" << input->readLong(2);
963 
964     if (!readLinesHeight(lineHeightEntry, info.m_firstParagLine, info.m_linesHeight)) {
965       // ok, try to continue without lineHeight
966       info.m_firstParagLine.resize(0);
967       info.m_linesHeight.resize(0);
968     }
969     ok = readInformations(informations, info.m_informations);
970     if (!ok) info.m_informations.resize(0);
971   }
972 
973   input->seek(pos+windowsSize, librevenge::RVNG_SEEK_SET);
974   ascii().addPos(pos);
975   ascii().addNote(f.str().c_str());
976   ascii().addPos(input->tell());
977 
978   return ok;
979 }
980 
981 ////////////////////////////////////////////////////////////
982 // read the lines height
983 ////////////////////////////////////////////////////////////
readLinesHeight(MWAWEntry const & entry,std::vector<int> & firstParagLine,std::vector<int> & linesHeight)984 bool MacWrtParser::readLinesHeight(MWAWEntry const &entry, std::vector<int> &firstParagLine, std::vector<int> &linesHeight)
985 {
986   firstParagLine.resize(0);
987   linesHeight.resize(0);
988 
989   if (!entry.valid()) return false;
990 
991   MWAWInputStreamPtr input = getInput();
992 
993   input->seek(entry.end()-1, librevenge::RVNG_SEEK_SET);
994   if (long(input->tell()) != entry.end()-1) {
995     MWAW_DEBUG_MSG(("MacWrtParser::readLinesHeight: file is too short\n"));
996     return false;
997   }
998 
999   long pos = entry.begin(), endPos = entry.end();
1000   input->seek(pos, librevenge::RVNG_SEEK_SET);
1001 
1002   libmwaw::DebugStream f;
1003   int numParag=0;
1004   while (input->tell() != endPos) {
1005     pos = input->tell();
1006     auto sz = static_cast<int>(input->readULong(2));
1007     if (pos+sz+2 > endPos) {
1008       MWAW_DEBUG_MSG(("MacWrtParser::readLinesHeight: find odd line\n"));
1009       ascii().addPos(pos);
1010       ascii().addNote("Entries(LineHeight):###");
1011       return false;
1012     }
1013 
1014     firstParagLine.push_back(int(linesHeight.size()));
1015     int actHeight = 0;
1016     bool heightOk = false;
1017     f.str("");
1018     f << "Entries(LineHeight)[" << entry.id() << "-" << ++numParag << "]:";
1019     for (int c = 0; c < sz; c++) {
1020       auto val = static_cast<int>(input->readULong(1));
1021       if (val & 0x80) {
1022         val &= 0x7f;
1023         if (!heightOk || val==0) {
1024           MWAW_DEBUG_MSG(("MacWrtParser::readLinesHeight: find factor without height \n"));
1025           return false;
1026         }
1027 
1028         for (int i = 0; i < val-1; i++)
1029           linesHeight.push_back(actHeight);
1030         if (val != 0x7f) heightOk = false;
1031         f << "x" << val;
1032         continue;
1033       }
1034       actHeight = val;
1035       linesHeight.push_back(actHeight);
1036       heightOk = true;
1037       if (c) f << ",";
1038       f << actHeight;
1039     }
1040 
1041     ascii().addPos(pos);
1042     ascii().addNote(f.str().c_str());
1043 
1044     if ((sz%2)==1) sz++;
1045     input->seek(pos+sz+2, librevenge::RVNG_SEEK_SET);
1046   }
1047   firstParagLine.push_back(int(linesHeight.size()));
1048 
1049   ascii().addPos(endPos);
1050   ascii().addNote("_");
1051   return true;
1052 }
1053 
1054 ////////////////////////////////////////////////////////////
1055 // read the entries
1056 ////////////////////////////////////////////////////////////
readInformationsV3(int numEntries,std::vector<MacWrtParserInternal::Information> & informations)1057 bool MacWrtParser::readInformationsV3(int numEntries, std::vector<MacWrtParserInternal::Information> &informations)
1058 {
1059   informations.resize(0);
1060 
1061   if (numEntries < 0) return false;
1062   if (numEntries == 0) return true;
1063 
1064   MWAWInputStreamPtr input = getInput();
1065 
1066   libmwaw::DebugStream f;
1067   for (int i = 0; i < numEntries; i++) {
1068     long pos = input->tell();
1069     MacWrtParserInternal::Information info;
1070     f.str("");
1071     f << "Entries(Information)[" << i+1 << "]:";
1072     auto height = static_cast<int>(input->readLong(2));
1073     info.m_height = height;
1074     if (info.m_height < 0) {
1075       info.m_height = 0;
1076       info.m_type = MacWrtParserInternal::Information::PAGEBREAK;
1077     }
1078     else if (info.m_height > 0)
1079       info.m_type = MacWrtParserInternal::Information::TEXT;
1080     else
1081       info.m_type = MacWrtParserInternal::Information::RULER;
1082 
1083     auto y = static_cast<int>(input->readLong(2));
1084     info.m_pos=MWAWPosition(MWAWVec2f(0,float(y)), MWAWVec2f(0, float(height)), librevenge::RVNG_POINT);
1085     info.m_pos.setPage(static_cast<int>(input->readLong(1)));
1086     f << info;
1087     informations.push_back(info);
1088 
1089     f << "unkn1=" << std::hex << input->readULong(2) << std::dec << ",";
1090     f << "unkn2=" << std::hex << input->readULong(1) << std::dec << ",";
1091     ascii().addPos(pos);
1092     ascii().addNote(f.str().c_str());
1093   }
1094   ascii().addPos(input->tell());
1095   ascii().addNote("_");
1096 
1097   return true;
1098 }
1099 
1100 ////////////////////////////////////////////////////////////
1101 // read the entries
1102 ////////////////////////////////////////////////////////////
readInformations(MWAWEntry const & entry,std::vector<MacWrtParserInternal::Information> & informations)1103 bool MacWrtParser::readInformations(MWAWEntry const &entry, std::vector<MacWrtParserInternal::Information> &informations)
1104 {
1105   informations.resize(0);
1106 
1107   if (!entry.valid()) return false;
1108 
1109   MWAWInputStreamPtr input = getInput();
1110 
1111   input->seek(entry.end()-1, librevenge::RVNG_SEEK_SET);
1112   if (long(input->tell()) != entry.end()-1) {
1113     MWAW_DEBUG_MSG(("MacWrtParser::readInformations: file is too short\n"));
1114     return false;
1115   }
1116 
1117   long pos = entry.begin(), endPos = entry.end();
1118   if ((endPos-pos)%16) {
1119     MWAW_DEBUG_MSG(("MacWrtParser::readInformations: entry size is odd\n"));
1120     return false;
1121   }
1122   auto numEntries = int((endPos-pos)/16);
1123   libmwaw::DebugStream f;
1124 
1125   input->seek(pos, librevenge::RVNG_SEEK_SET);
1126   for (int i = 0; i < numEntries; i++) {
1127     pos = input->tell();
1128 
1129     f.str("");
1130     f << "Entries(Information)[" << entry.id() << "-" << i+1 << "]:";
1131     MacWrtParserInternal::Information info;
1132     auto height = static_cast<int>(input->readLong(2));
1133     if (height < 0) {
1134       info.m_type = MacWrtParserInternal::Information::GRAPHIC;
1135       height *= -1;
1136     }
1137     else if (height == 0)
1138       info.m_type = MacWrtParserInternal::Information::RULER;
1139     else
1140       info.m_type = MacWrtParserInternal::Information::TEXT;
1141     info.m_height = height;
1142 
1143     auto y = static_cast<int>(input->readLong(2));
1144     auto page = static_cast<int>(input->readULong(1));
1145     input->seek(3, librevenge::RVNG_SEEK_CUR); // unused
1146     info.m_pos = MWAWPosition(MWAWVec2f(0,float(y)), MWAWVec2f(0, float(height)), librevenge::RVNG_POINT);
1147     info.m_pos.setPage(page);
1148 
1149     auto paragStatus = static_cast<int>(input->readULong(1));
1150     switch (paragStatus & 0x3) {
1151     case 0:
1152       info.m_justify = MWAWParagraph::JustificationLeft;
1153       break;
1154     case 1:
1155       info.m_justify = MWAWParagraph::JustificationCenter;
1156       break;
1157     case 2:
1158       info.m_justify = MWAWParagraph::JustificationRight;
1159       break;
1160     case 3:
1161       info.m_justify = MWAWParagraph::JustificationFull;
1162       break;
1163     default:
1164       break;
1165     }
1166     info.m_compressed = (paragStatus & 0x8);
1167     info.m_justifySet = (paragStatus & 0x20);
1168 
1169     // other bits used internally
1170     auto highPos = static_cast<unsigned int>(input->readULong(1));
1171     info.m_data.setBegin(long(highPos<<16)+long(input->readULong(2)));
1172     info.m_data.setLength(long(input->readULong(2)));
1173 
1174     auto paragFormat = static_cast<int>(input->readULong(2));
1175     uint32_t flags = 0;
1176     // bit 1 = plain
1177     if (paragFormat&0x2) flags |= MWAWFont::boldBit;
1178     if (paragFormat&0x4) flags |= MWAWFont::italicBit;
1179     if (paragFormat&0x8) info.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
1180     if (paragFormat&0x10) flags |= MWAWFont::embossBit;
1181     if (paragFormat&0x20) flags |= MWAWFont::shadowBit;
1182     if (paragFormat&0x40)
1183       info.m_font.set(MWAWFont::Script::super100());
1184     if (paragFormat&0x80)
1185       info.m_font.set(MWAWFont::Script::sub100());
1186     info.m_font.setFlags(flags);
1187 
1188     int fontSize = 0;
1189     switch ((paragFormat >> 8) & 7) {
1190     case 0:
1191       break;
1192     case 1:
1193       fontSize=9;
1194       break;
1195     case 2:
1196       fontSize=10;
1197       break;
1198     case 3:
1199       fontSize=12;
1200       break;
1201     case 4:
1202       fontSize=14;
1203       break;
1204     case 5:
1205       fontSize=18;
1206       break;
1207     case 6:
1208       fontSize=14;
1209       break;
1210     default: // rare, but can appears on some empty lines
1211       MWAW_DEBUG_MSG(("MacWrtParser::readInformations: unknown font size=7\n"));
1212       f << "##fSize=7,";
1213     }
1214     if (fontSize) info.m_font.setSize(float(fontSize));
1215     if ((paragFormat >> 11)&0x1F) info.m_font.setId((paragFormat >> 11)&0x1F);
1216 
1217     informations.push_back(info);
1218     f << info;
1219 #ifdef DEBUG
1220     f << "font=[" << info.m_font.getDebugString(getFontConverter()) << "]";
1221 #endif
1222 
1223     input->seek(pos+16, librevenge::RVNG_SEEK_SET);
1224     ascii().addPos(pos);
1225     ascii().addNote(f.str().c_str());
1226   }
1227 
1228   ascii().addPos(endPos);
1229   ascii().addNote("_");
1230   return true;
1231 }
1232 
1233 ////////////////////////////////////////////////////////////
1234 // read a text
1235 ////////////////////////////////////////////////////////////
readText(MacWrtParserInternal::Information const & info,std::vector<int> const & lineHeight)1236 bool MacWrtParser::readText(MacWrtParserInternal::Information const &info,
1237                             std::vector<int> const &lineHeight)
1238 {
1239   if (!getTextListener()) {
1240     MWAW_DEBUG_MSG(("MacWrtParser::readText: can not find the listener\n"));
1241     return false;
1242   }
1243   MWAWEntry const &entry = info.m_data;
1244   if (!entry.valid()) return false;
1245 
1246   MWAWInputStreamPtr input = getInput();
1247   input->seek(entry.end()-1, librevenge::RVNG_SEEK_SET);
1248   if (long(input->tell()) != entry.end()-1) {
1249     MWAW_DEBUG_MSG(("MacWrtParser::readText: file is too short\n"));
1250     return false;
1251   }
1252 
1253   long pos = entry.begin();
1254   input->seek(pos, librevenge::RVNG_SEEK_SET);
1255 
1256   libmwaw::DebugStream f;
1257   f << "Entries(Text):";
1258 
1259   auto numChar = static_cast<int>(input->readULong(2));
1260   std::string text("");
1261   if (!info.m_compressed) {
1262     if (numChar+2 >= entry.length()) {
1263       MWAW_DEBUG_MSG(("MacWrtParser::readText: text is too long\n"));
1264       return false;
1265     }
1266     for (int i = 0; i < numChar; i++)
1267       text += char(input->readULong(1));
1268   }
1269   else {
1270     std::string const &compressCorr = m_state->m_compressCorr;
1271 
1272     int actualChar = 0;
1273     bool actualCharSet = false;
1274 
1275     for (int i = 0; i < numChar; i++) {
1276       int highByte = 0;
1277       for (int st = 0; st < 3; st++) {
1278         int actVal;
1279         if (!actualCharSet) {
1280           if (long(input->tell()) >= entry.end()) {
1281             MWAW_DEBUG_MSG(("MacWrtParser::readText: text is too long\n"));
1282             return false;
1283           }
1284           actualChar = static_cast<int>(input->readULong(1));
1285           actVal = (actualChar >> 4);
1286         }
1287         else
1288           actVal = (actualChar & 0xf);
1289         actualCharSet = !actualCharSet;
1290         if (st == 0) {
1291           if (actVal == 0xf) continue;
1292           text += compressCorr[size_t(actVal)];
1293           break;
1294         }
1295         if (st == 1) { // high bytes
1296           highByte = (actVal<<4);
1297           continue;
1298         }
1299         text += char(highByte | actVal);
1300       }
1301     }
1302   }
1303   f << "'" << text << "'";
1304 
1305   long actPos = input->tell();
1306   if ((actPos-pos)%2==1) {
1307     input->seek(1,librevenge::RVNG_SEEK_CUR);
1308     actPos++;
1309   }
1310 
1311   auto formatSize = static_cast<int>(input->readULong(2));
1312   if ((formatSize%6)!=0 || actPos+2+formatSize > entry.end()) {
1313     MWAW_DEBUG_MSG(("MacWrtParser::readText: format is too long\n"));
1314     return false;
1315   }
1316   int numFormat = formatSize/6;
1317 
1318   std::vector<int> listPos;
1319   std::vector<MWAWFont> listFonts;
1320 
1321   for (int i = 0; i < numFormat; i++) {
1322     auto tPos = static_cast<int>(input->readULong(2));
1323 
1324     MWAWFont font;
1325     font.setSize(float(input->readULong(1)));
1326     auto flag = static_cast<int>(input->readULong(1));
1327     uint32_t flags = 0;
1328     // bit 1 = plain
1329     if (flag&0x1) flags |= MWAWFont::boldBit;
1330     if (flag&0x2) flags |= MWAWFont::italicBit;
1331     if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
1332     if (flag&0x8) flags |= MWAWFont::embossBit;
1333     if (flag&0x10) flags |= MWAWFont::shadowBit;
1334     if (flag&0x20) font.set(MWAWFont::Script::super100());
1335     if (flag&0x40) font.set(MWAWFont::Script::sub100());
1336     font.setFlags(flags);
1337     font.setId(static_cast<int>(input->readULong(2)));
1338     listPos.push_back(tPos);
1339     listFonts.push_back(font);
1340     f << ",f" << i << "=[pos=" << tPos;
1341 #ifdef DEBUG
1342     f << ",font=[" << font.getDebugString(getFontConverter()) << "]";
1343 #endif
1344     f << "]";
1345   }
1346 
1347   std::vector<int> const *lHeight = &lineHeight;
1348   int totalHeight = info.m_height;
1349   std::vector<int> textLineHeight;
1350   if (version() <= 3) {
1351     std::vector<int> fParagLines;
1352     pos = input->tell();
1353     MWAWEntry hEntry;
1354     hEntry.setBegin(pos);
1355     hEntry.setEnd(entry.end());
1356 
1357     if (readLinesHeight(hEntry, fParagLines, textLineHeight)) {
1358       lHeight = &textLineHeight;
1359       totalHeight = 0;
1360       for (auto height : textLineHeight)
1361         totalHeight+=height;
1362     }
1363     else
1364       input->seek(pos, librevenge::RVNG_SEEK_SET);
1365   }
1366   if (long(input->tell()) != entry.end()) {
1367     f << "#badend";
1368     ascii().addDelimiter(input->tell(), '|');
1369   }
1370 
1371   if (getTextListener()) {
1372     MWAWParagraph para=getTextListener()->getParagraph();
1373     if (totalHeight && lHeight->size()) // fixme find a way to associate the good size to each line
1374       para.setInterline(totalHeight/double(lHeight->size()), librevenge::RVNG_POINT);
1375     else
1376       para.setInterline(1.2, librevenge::RVNG_PERCENT);
1377     if (info.m_justifySet)
1378       para.m_justify=info.m_justify;
1379     getTextListener()->setParagraph(para);
1380 
1381     if (!numFormat || listPos[0] != 0)
1382       getTextListener()->setFont(info.m_font);
1383 
1384     int actFormat = 0;
1385     numChar = int(text.length());
1386     for (int i = 0; i < numChar; i++) {
1387       if (actFormat < numFormat && i == listPos[size_t(actFormat)]) {
1388         getTextListener()->setFont(listFonts[size_t(actFormat)]);
1389         actFormat++;
1390       }
1391       auto c = static_cast<unsigned char>(text[size_t(i)]);
1392       if (c == 0x9)
1393         getTextListener()->insertTab();
1394       else if (c == 0xd)
1395         getTextListener()->insertEOL();
1396       else if (c==0x11) // command key (found in some files)
1397         getTextListener()->insertUnicode(0x2318);
1398       else if (c==0x14) // apple logo: check me
1399         getTextListener()->insertUnicode(0xf8ff);
1400       else if (c<0x1f) {
1401         // MacWrite allows to add "invalid" characters in the text
1402         // (and do not display them), this does not imply that the
1403         // file is invalid...
1404         MWAW_DEBUG_MSG(("MacWrtParser::readText: find bad character %d at pos=0x%lx\n", int(c), version()<=3 ? entry.begin()-4 : entry.begin()));
1405         f << "###[" << int(c) << "]";
1406       }
1407       else
1408         getTextListener()->insertCharacter(c);
1409     }
1410   }
1411 
1412   ascii().addPos(version()<=3 ? entry.begin()-4 : entry.begin());
1413   ascii().addNote(f.str().c_str());
1414 
1415   return true;
1416 }
1417 
1418 ////////////////////////////////////////////////////////////
1419 // read a paragraph
1420 ////////////////////////////////////////////////////////////
readParagraph(MacWrtParserInternal::Information const & info)1421 bool MacWrtParser::readParagraph(MacWrtParserInternal::Information const &info)
1422 {
1423   MWAWEntry const &entry = info.m_data;
1424   if (!entry.valid()) return false;
1425   if (entry.length() != 34) {
1426     MWAW_DEBUG_MSG(("MacWrtParser::readParagraph: size is odd\n"));
1427     return false;
1428   }
1429 
1430   MWAWParagraph parag;
1431   MWAWInputStreamPtr input = getInput();
1432 
1433   input->seek(entry.end()-1, librevenge::RVNG_SEEK_SET);
1434   if (long(input->tell()) != entry.end()-1) {
1435     MWAW_DEBUG_MSG(("MacWrtParser::readParagraph: file is too short\n"));
1436     return false;
1437   }
1438 
1439   long pos = entry.begin();
1440   input->seek(pos, librevenge::RVNG_SEEK_SET);
1441 
1442   libmwaw::DebugStream f;
1443   f << "Entries(Paragraph):";
1444 
1445   parag.m_margins[1] = double(input->readLong(2))/80.;
1446   parag.m_margins[2] = double(input->readLong(2))/80.;
1447   auto justify = static_cast<int>(input->readLong(1));
1448   switch (justify) {
1449   case 0:
1450     parag.m_justify = MWAWParagraph::JustificationLeft;
1451     break;
1452   case 1:
1453     parag.m_justify = MWAWParagraph::JustificationCenter;
1454     break;
1455   case 2:
1456     parag.m_justify = MWAWParagraph::JustificationRight;
1457     break;
1458   case 3:
1459     parag.m_justify = MWAWParagraph::JustificationFull;
1460     break;
1461   default:
1462     f << "##justify=" << justify << ",";
1463     break;
1464   }
1465   auto numTabs = static_cast<int>(input->readLong(1));
1466   if (numTabs < 0 || numTabs > 10) {
1467     f << "##numTabs=" << numTabs << ",";
1468     numTabs = 0;
1469   }
1470   auto highspacing = static_cast<int>(input->readULong(1));
1471   if (highspacing==0x80) // 6 by line
1472     parag.setInterline(12, librevenge::RVNG_POINT);
1473   else if (highspacing) {
1474     f << "##highSpacing=" << std::hex << highspacing << std::dec << ",";
1475     MWAW_DEBUG_MSG(("MacWrtParser::readParagraph: high spacing bit set=%d\n", highspacing));
1476   }
1477   auto spacing = static_cast<int>(input->readLong(1));
1478   if (spacing < 0)
1479     f << "#interline=" << 1.+spacing/2.0 << ",";
1480   else if (spacing)
1481     parag.setInterline(1.+spacing/2.0, librevenge::RVNG_PERCENT);
1482   parag.m_margins[0] = double(input->readLong(2))/80.;
1483 
1484   parag.m_tabs->resize(size_t(numTabs));
1485   for (size_t i = 0; i < size_t(numTabs); i++) {
1486     auto numPixel = static_cast<int>(input->readLong(2));
1487     auto align = MWAWTabStop::LEFT;
1488     if (numPixel < 0) {
1489       align = MWAWTabStop::DECIMAL;
1490       numPixel *= -1;
1491     }
1492     (*parag.m_tabs)[i].m_alignment = align;
1493     (*parag.m_tabs)[i].m_position = numPixel/72.0;
1494   }
1495   *(parag.m_margins[0]) -= parag.m_margins[1].get();
1496   if (parag.m_margins[2].get() > 0.0)
1497     parag.m_margins[2]=getPageWidth()-parag.m_margins[2].get()-1.0;
1498   if (parag.m_margins[2].get() < 0) parag.m_margins[2] = 0;
1499   f << parag;
1500 
1501   if (getTextListener())
1502     getTextListener()->setParagraph(parag);
1503   ascii().addPos(version()<=3 ? pos-4 : pos);
1504   ascii().addNote(f.str().c_str());
1505 
1506   return true;
1507 }
1508 
1509 ////////////////////////////////////////////////////////////
1510 // read the page break
1511 ////////////////////////////////////////////////////////////
readPageBreak(MacWrtParserInternal::Information const & info)1512 bool MacWrtParser::readPageBreak(MacWrtParserInternal::Information const &info)
1513 {
1514   MWAWEntry const &entry = info.m_data;
1515   if (!entry.valid()) return false;
1516   if (entry.length() != 21) {
1517     MWAW_DEBUG_MSG(("MacWrtParser::readPageBreak: size is odd\n"));
1518     return false;
1519   }
1520 
1521   MWAWParagraph parag;
1522   MWAWInputStreamPtr input = getInput();
1523 
1524   input->seek(entry.end()-1, librevenge::RVNG_SEEK_SET);
1525   if (long(input->tell()) != entry.end()-1) {
1526     MWAW_DEBUG_MSG(("MacWrtParser::readPageBreak: file is too short\n"));
1527     return false;
1528   }
1529 
1530   long pos = entry.begin();
1531   input->seek(pos, librevenge::RVNG_SEEK_SET);
1532 
1533   libmwaw::DebugStream f;
1534 
1535   f << "Entries(PageBreak):";
1536   for (int i = 0; i < 2; i++) {
1537     auto val = static_cast<int>(input->readLong(2));
1538     if (val) f << "f" << i << "=" << val << ",";
1539   }
1540   int dim[2]= {0,0};
1541   for (auto &d : dim) d = static_cast<int>(input->readLong(2));
1542   f << "pageSize(?)=" << dim[0] << "x" << dim[1] << ",";
1543   f << "unk=" << input->readLong(2) << ","; // find 0xd
1544 
1545   // find MAGICPIC
1546   std::string name("");
1547   for (int i = 0; i < 8; i++)
1548     name += char(input->readULong(1));
1549   f << name << ",";
1550   // then I find 1101ff: end of quickdraw pict1 ?
1551   ascii().addPos(version()<=3 ? pos-4 : pos);
1552   ascii().addNote(f.str().c_str());
1553 
1554   return true;
1555 }
1556 
1557 ////////////////////////////////////////////////////////////
1558 // read a graphic
1559 ////////////////////////////////////////////////////////////
readGraphic(MacWrtParserInternal::Information const & info)1560 bool MacWrtParser::readGraphic(MacWrtParserInternal::Information const &info)
1561 {
1562   MWAWEntry const &entry = info.m_data;
1563   if (!entry.valid()) return false;
1564 
1565   if (entry.length() < 12) {
1566     MWAW_DEBUG_MSG(("MacWrtParser::readGraphic: file is too short\n"));
1567     return false;
1568   }
1569 
1570   MWAWInputStreamPtr input = getInput();
1571 
1572   input->seek(entry.end()-1, librevenge::RVNG_SEEK_SET);
1573   if (long(input->tell()) != entry.end()-1) {
1574     MWAW_DEBUG_MSG(("MacWrtParser::readGraphic: file is too short\n"));
1575     return false;
1576   }
1577   long pos = entry.begin();
1578   input->seek(pos, librevenge::RVNG_SEEK_SET);
1579 
1580   int dim[4];
1581   for (auto &d : dim) d = static_cast<int>(input->readLong(2));
1582   if (dim[2] < dim[0] || dim[3] < dim[1]) {
1583     MWAW_DEBUG_MSG(("MacWrtParser::readGraphic: bdbox is bad\n"));
1584     return false;
1585   }
1586   libmwaw::DebugStream f;
1587   f << "Entries(Graphic):";
1588 
1589   MWAWBox2f box;
1590   auto res = MWAWPictData::check(input, int(entry.length()-8), box);
1591   if (res == MWAWPict::MWAW_R_BAD) {
1592     MWAW_DEBUG_MSG(("MacWrtParser::readGraphic: can not find the picture\n"));
1593     return false;
1594   }
1595 
1596 
1597   MWAWVec2f actualSize(float(dim[3]-dim[1]), float(dim[2]-dim[0])), naturalSize(actualSize);
1598   if (box.size().x() > 0 && box.size().y()  > 0) naturalSize = box.size();
1599   MWAWPosition pictPos=MWAWPosition(MWAWVec2f(0,0),actualSize, librevenge::RVNG_POINT);
1600   pictPos.setRelativePosition(MWAWPosition::Char);
1601   pictPos.setNaturalSize(naturalSize);
1602   f << pictPos;
1603 
1604   // get the picture
1605   input->seek(pos+8, librevenge::RVNG_SEEK_SET);
1606 
1607   std::shared_ptr<MWAWPict> pict(MWAWPictData::get(input, int(entry.length()-8)));
1608   if (pict) {
1609     if (getTextListener()) {
1610       MWAWParagraph para=getTextListener()->getParagraph();
1611       para.setInterline(1.0, librevenge::RVNG_PERCENT);
1612       getTextListener()->setParagraph(para);
1613 
1614       MWAWEmbeddedObject picture;
1615       if (pict->getBinary(picture) && !picture.m_dataList.empty() && !isMagicPic(picture.m_dataList[0]))
1616         getTextListener()->insertPicture(pictPos, picture);
1617       getTextListener()->insertEOL();
1618 #ifdef DEBUG_WITH_FILES
1619       if (!picture.m_dataList.empty()) {
1620         static int volatile pictName = 0;
1621         libmwaw::DebugStream f2;
1622         f2 << "PICT-" << ++pictName;
1623         libmwaw::Debug::dumpFile(picture.m_dataList[0], f2.str().c_str());
1624         ascii().skipZone(pos+8, entry.end()-1);
1625       }
1626 #endif
1627     }
1628   }
1629 
1630   ascii().addPos(pos);
1631   ascii().addNote(f.str().c_str());
1632 
1633   return true;
1634 }
1635 
isMagicPic(librevenge::RVNGBinaryData const & dt)1636 bool MacWrtParser::isMagicPic(librevenge::RVNGBinaryData const &dt)
1637 {
1638   if (dt.size() != 526)
1639     return false;
1640   static char const *header="MAGICPIC";
1641   unsigned char const *dtBuf = dt.getDataBuffer()+514;
1642   for (int i=0; i < 8; i++)
1643     if (*(dtBuf++)!=header[i])
1644       return false;
1645   return true;
1646 }
1647 
1648 ////////////////////////////////////////////////////////////
1649 // read the free list
1650 ////////////////////////////////////////////////////////////
checkFreeList()1651 bool MacWrtParser::checkFreeList()
1652 {
1653   if (version() <= 3)
1654     return true;
1655   MWAWInputStreamPtr input = getInput();
1656   long pos = m_state->m_fileHeader.m_freeListPos;
1657   if (!input->checkPosition(pos+m_state->m_fileHeader.m_freeListLength)) {
1658     MWAW_DEBUG_MSG(("MacWrtParser::checkFreeList: zone is too short\n"));
1659     return false;
1660   }
1661   input->seek(pos, librevenge::RVNG_SEEK_SET);
1662 
1663   libmwaw::DebugStream f;
1664   int N=int(m_state->m_fileHeader.m_freeListLength/8);
1665   for (int n=0; n<N; ++n) {
1666     pos=input->tell();
1667     auto freePos = long(input->readULong(4));
1668     auto sz = long(input->readULong(4));
1669 
1670     f.str("");
1671     f << "Entries(FreeList)[" << n << "]:" << std::hex << freePos << "-" << sz << std::dec;
1672     ascii().addPos(pos);
1673     ascii().addNote(f.str().c_str());
1674 
1675     // if the file end by a free zone, pos+sz can be greater than the file size
1676     if (!input->checkPosition(freePos+1)) {
1677       if (!input->checkPosition(freePos)) {
1678         MWAW_DEBUG_MSG(("MacWrtParser::checkFreeList: bad free block: \n"));
1679         return false;
1680       }
1681       continue;
1682     }
1683     f.str("");
1684     f << "Entries(FreeBlock)[" << n << "]:";
1685     ascii().addPos(freePos);
1686     ascii().addNote(f.str().c_str());
1687   }
1688   if (m_state->m_fileHeader.m_freeListLength!=m_state->m_fileHeader.m_freeListAllocated) {
1689     ascii().addPos(input->tell());
1690     ascii().addNote("Entries(FreeList)[end]:");
1691   }
1692   return true;
1693 }
1694 
1695 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
1696