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