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