1 /* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3 /* libmwaw
4 * Version: MPL 2.0 / LGPLv2+
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 2.0 (the "License"); you may not use this file except in compliance with
8 * the License or as specified alternatively below. You may obtain a copy of
9 * the License at http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * Major Contributor(s):
17 * Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18 * Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20 * Copyright (C) 2006, 2007 Andrew Ziem
21 * Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22 *
23 *
24 * All Rights Reserved.
25 *
26 * For minor contributions see the git repository.
27 *
28 * Alternatively, the contents of this file may be used under the terms of
29 * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30 * in which case the provisions of the LGPLv2+ are applicable
31 * instead of those above.
32 */
33
34 #include <cstring>
35 #include <iomanip>
36 #include <iostream>
37 #include <limits>
38 #include <sstream>
39
40 #include <librevenge/librevenge.h>
41
42 #include "MWAWCell.hxx"
43 #include "MWAWTextListener.hxx"
44 #include "MWAWFont.hxx"
45 #include "MWAWFontConverter.hxx"
46 #include "MWAWHeader.hxx"
47 #include "MWAWParagraph.hxx"
48 #include "MWAWPosition.hxx"
49 #include "MWAWPictMac.hxx"
50 #include "MWAWPrinter.hxx"
51 #include "MWAWSection.hxx"
52 #include "MWAWSubDocument.hxx"
53 #include "MWAWTable.hxx"
54
55 #include "WriterPlsParser.hxx"
56
57 /** Internal: the structures of a WriterPlsParser */
58 namespace WriterPlsParserInternal
59 {
60 //! Page informations
61 struct PageInfo {
PageInfoWriterPlsParserInternal::PageInfo62 PageInfo()
63 : m_firstLine(0)
64 , m_height(0)
65 , m_heightFromBegin(0)
66 {
67 std::memset(m_unknown, 0, sizeof(m_unknown));
68 }
operator <<(std::ostream & o,PageInfo const & p)69 friend std::ostream &operator<<(std::ostream &o, PageInfo const &p)
70 {
71 o << "firstLine=" << p.m_firstLine
72 << ", height=" << p.m_height
73 << ", height[fromStart]=" << p.m_heightFromBegin;
74 if (p.m_unknown[0] != 1) o << ", unkn0=" << p.m_unknown[0];
75 if (p.m_unknown[1]) o << ", unkn1=" << p.m_unknown[1];
76 return o;
77 }
78
79 int m_firstLine;
80 int m_unknown[2]; // 1 0
81 int m_height, m_heightFromBegin;
82 };
83
84 //! Column informations
85 struct ColumnInfo {
ColumnInfoWriterPlsParserInternal::ColumnInfo86 ColumnInfo()
87 : m_firstLine(0)
88 , m_height(0)
89 , m_col(0)
90 , m_numCol(1)
91 {
92 std::memset(m_unknown, 0, sizeof(m_unknown));
93 }
operator <<(std::ostream & o,ColumnInfo const & c)94 friend std::ostream &operator<<(std::ostream &o, ColumnInfo const &c)
95 {
96 o << "firstLine=" << c.m_firstLine
97 << ", col=" << c.m_col << "/" << c.m_numCol
98 << ", height=" << c.m_height
99 << ", dim?=" << c.m_unknown[3];
100 if (c.m_unknown[0]) o << ", unkn0=" << c.m_unknown[0];
101 if (c.m_unknown[1] != 1) o << ", unkn1=" << c.m_unknown[1];
102 if (c.m_unknown[2]) o << ", unkn2=" << c.m_unknown[2];
103 return o;
104 }
105
106 int m_firstLine;
107 int m_unknown[4]; // 0 1 0
108 int m_height, m_col, m_numCol;
109 };
110
111 //! Column informations in a table
112 struct ColumnTableInfo {
ColumnTableInfoWriterPlsParserInternal::ColumnTableInfo113 ColumnTableInfo()
114 : m_height(0)
115 , m_numData(0)
116 , m_flags(0)
117 {
118 for (auto &col : m_colX) col=0;
119 for (auto &text : m_textX) text=0;
120 }
operator <<(std::ostream & o,ColumnTableInfo const & c)121 friend std::ostream &operator<<(std::ostream &o, ColumnTableInfo const &c)
122 {
123 o << "height=" << c.m_height
124 << ", numData=" << c.m_numData
125 << ", colX=" << c.m_colX[0] << "<->" << c.m_colX[1]
126 << ", textX=" << c.m_textX[0] << "<->" << c.m_textX[1];
127 if (c.m_textX[0] != c.m_textX[2])
128 o << ", textX[begin?]=" << c.m_textX[2];
129 if (c.m_flags) o << ", flags=" << c.m_flags;
130 return o;
131 }
132
133 int m_height;
134 int m_numData;
135 int m_colX[2]; // x : begin and end pos
136 int m_textX[3]; // x : begin|end|begin pos
137 int m_flags; // 0
138 };
139
140 //! Paragraph informations
141 struct ParagraphInfo {
ParagraphInfoWriterPlsParserInternal::ParagraphInfo142 ParagraphInfo()
143 : m_pos(0)
144 , m_type(-2)
145 , m_height(0)
146 , m_height2(0)
147 , m_width(0)
148 , m_numLines(0)
149 , m_linesHeight()
150 , m_unknowns()
151 {
152 for (auto &fl : m_flags) fl=0;
153 }
getTypeWriterPlsParserInternal::ParagraphInfo154 int getType() const
155 {
156 if (m_type >= 8) return (m_type & 0x7);
157 return m_type;
158 }
operator <<(std::ostream & o,ParagraphInfo const & p)159 friend std::ostream &operator<<(std::ostream &o, ParagraphInfo const &p)
160 {
161 int type = p.m_type;
162 bool typeFlag = false;
163 if (type >= 8) {
164 typeFlag = true;
165 type &= 7;
166 }
167 switch (type) {
168 case 0:
169 o << "text";
170 break;
171 case 1:
172 o << "section";
173 break;
174 case 2:
175 o << "text2";
176 break;
177 case 3:
178 o << "colBreak";
179 break;
180 case 4:
181 o << "graphics";
182 break;
183 case 5:
184 o << "table";
185 break;
186
187 case -1:
188 o << "empty";
189 break;
190 case -2:
191 break;
192 default:
193 o << "type=" << type;
194 break;
195 }
196 if (typeFlag) o << "[in table],";
197 else o << ",";
198
199 if (p.m_pos) o << "pos=" << std::hex << p.m_pos << std::dec << ",";
200 o << "h=" << p.m_height << ",";
201 if (p.m_height2 != p.m_height) o << "h[next]=" << p.m_height2 << ",";
202 if (p.m_width) o << "w=" << p.m_width << ",";
203 if (type == 5) {
204 o << "numCols=" << p.m_numLines << ",";
205 if (!p.m_linesHeight.empty()) {
206 o << "numDataByCols=[";
207 for (auto i : p.m_linesHeight)
208 o << i << ",";
209 o << "],";
210 }
211 }
212 else {
213 if (p.m_numLines) o << "numLines=" << p.m_numLines << ",";
214 if (!p.m_linesHeight.empty()) {
215 o << "lineH=[";
216 for (auto i : p.m_linesHeight)
217 o << i << ",";
218 o << "],";
219 }
220 }
221 for (int i= 0; i < 6; i++) {
222 if (!p.m_flags[i]) continue;
223 o << "f" << i << "=" << std::hex << p.m_flags[i] << std::dec << ",";
224 }
225 if (p.m_unknowns.size()) {
226 o << "unkn=[";
227 for (auto unk : p.m_unknowns) {
228 if (unk)
229 o << unk << ",";
230 else
231 o << "_,";
232 }
233 o << "],";
234 }
235 return o;
236 }
237
238 long m_pos;
239 int m_type;
240
241 int m_height, m_height2, m_width, m_numLines;
242 std::vector<int> m_linesHeight;
243 int m_flags[6];
244 std::vector<int> m_unknowns;
245 };
246
247 //! Windows informations
248 struct WindowsInfo {
249 struct Zone {
ZoneWriterPlsParserInternal::WindowsInfo::Zone250 Zone()
251 : m_number(0)
252 , m_size(0)
253 , m_width(0)
254 {
255 for (auto &unk : m_unknown) unk = 0;
256 }
emptyWriterPlsParserInternal::WindowsInfo::Zone257 bool empty() const
258 {
259 return m_number==0 && m_size==0;
260 }
operator <<WriterPlsParserInternal::WindowsInfo261 friend std::ostream &operator<<(std::ostream &o, Zone const &z)
262 {
263 o << "N=" << z.m_number << ", sz=" << std::hex << z.m_size << std::dec;
264 o << ", w=" << z.m_width;
265 for (int i = 0; i < 3; i++) {
266 if (!z.m_unknown[i]) continue;
267 o << ", f" << i << "=" << z.m_unknown[i];
268 }
269 return o;
270 }
271 int m_number;
272 int m_size;
273 int m_width;
274 int m_unknown[3];
275 };
276
277 friend std::ostream &operator<<(std::ostream &o, WindowsInfo const &w);
278
WindowsInfoWriterPlsParserInternal::WindowsInfo279 WindowsInfo()
280 : m_pageDim()
281 , m_headerY(0)
282 , m_footerY(0)
283 , m_pages()
284 , m_columns()
285 , m_paragraphs()
286 {
287 }
288
dimensionInvalidWriterPlsParserInternal::WindowsInfo289 bool dimensionInvalid() const
290 {
291 return (m_pageDim.x() < 0 || m_pageDim.y() < 0 ||
292 m_headerY < 0 || m_footerY < 0 ||
293 m_headerY+m_footerY > m_pageDim.y());
294 }
295
296 bool getColumnLimitsFor(int line, std::vector<int> &listPos);
297
298 MWAWVec2i m_pageDim;
299 int m_headerY, m_footerY;
300 std::vector<PageInfo> m_pages;
301 std::vector<ColumnInfo> m_columns;
302 std::vector<ParagraphInfo> m_paragraphs;
303
304 // ????, pages, columns, parag, ???, ???, ???
305 Zone m_zone[7];
306 };
307
getColumnLimitsFor(int line,std::vector<int> & listPos)308 bool WindowsInfo::getColumnLimitsFor(int line, std::vector<int> &listPos)
309 {
310 listPos.resize(0);
311
312 size_t numColumns = m_columns.size();
313 size_t firstColumn = 0; // initialized to make clang analyzer happier
314 int numCols = 0;
315 for (size_t i = 0; i < numColumns; i++) {
316 if (m_columns[i].m_firstLine == line+2) {
317 numCols=m_columns[i].m_numCol;
318 firstColumn = i;
319 if (numCols > int(unsigned(numColumns - firstColumn)))
320 numCols = int(unsigned(numColumns - firstColumn));
321 if (numCols <= 1 || m_columns[i].m_col != 1) return false;
322 break;
323 }
324 if (m_columns[i].m_firstLine > line+2)
325 return true;
326 }
327 if (numCols <= 1)
328 return true;
329
330 size_t numPara = m_paragraphs.size();
331 listPos.resize(size_t(numCols));
332 for (size_t i = 0; i < size_t(numCols); i++) {
333 ColumnInfo const &colInfo = m_columns[firstColumn++];
334 int l = colInfo.m_firstLine-1;
335 if (l < 0 || l >= static_cast<int>(numPara)) {
336 MWAW_DEBUG_MSG(("WindowsInfo::getColumnLimitsFor: pb with line position\n"));
337 return false;
338 }
339 if (i && m_paragraphs[size_t(l)].getType() != 3) {
340 MWAW_DEBUG_MSG(("WindowsInfo::getColumnLimitsFor: can not find cols break\n"));
341 return false;
342 }
343
344 listPos[i] = (i == 0) ? l-1 : l;
345 }
346 return true;
347 }
348
operator <<(std::ostream & o,WindowsInfo const & w)349 std::ostream &operator<<(std::ostream &o, WindowsInfo const &w)
350 {
351 if (w.m_pageDim.x() || w.m_pageDim.y())
352 o << "pagesDim=" << w.m_pageDim << ",";
353 if (w.m_headerY) o << "header[Height]=" << w.m_headerY << ",";
354 if (w.m_footerY) o << "footer[Height]=" << w.m_footerY << ",";
355 for (int i = 0; i < 7; i++) {
356 if (w.m_zone[i].empty()) continue;
357 switch (i) {
358 case 1:
359 o << "zonePages";
360 break;
361 case 2:
362 o << "zoneCols?";
363 break;
364 case 3:
365 o << "zoneParag";
366 break;
367 default:
368 o << "unkZone" << i;
369 break;
370 }
371 o << "=[" << w.m_zone[i] << "], ";
372 }
373 return o;
374 }
375
376 ////////////////////////////////////////
377 /** Internal: class to store the font properties */
378 struct Font {
FontWriterPlsParserInternal::Font379 Font()
380 : m_font()
381 , m_firstChar(0)
382 {
383 }
384
385 //! operator<<
operator <<(std::ostream & o,Font const & f)386 friend std::ostream &operator<<(std::ostream &o, Font const &f)
387 {
388 if (f.m_firstChar) o << "firstChar=" << f.m_firstChar << ",";
389 return o;
390 }
391 //! the font
392 MWAWFont m_font;
393 //! the first character
394 int m_firstChar;
395 };
396
397 ////////////////////////////////////////
398 /** Internal: class to store the line properties */
399 struct Line {
LineWriterPlsParserInternal::Line400 Line()
401 : m_firstChar(0)
402 , m_height(0)
403 , m_width(0)
404 , m_maxFontSize(0)
405 {
406 for (auto &fl : m_flags) fl=0;
407 }
408
409 //! operator<<
operator <<(std::ostream & o,Line const & l)410 friend std::ostream &operator<<(std::ostream &o, Line const &l)
411 {
412 if (l.m_firstChar) o << "firstChar=" << l.m_firstChar << ",";
413 o << "height=" << l.m_height << ", width=" << l.m_width;
414 for (int i = 0; i < 4; i++) {
415 if (!l.m_flags[i]) continue;
416 o << ", lF" << i << "=" << std::hex << l.m_flags[i] << std::dec;
417 }
418 return o;
419 }
420 //! the first character
421 int m_firstChar;
422 int m_height/** the height */, m_width /** the width */;
423 int m_maxFontSize; /** the maximum font size */
424 //! some flag
425 int m_flags[4]; // flags[0] a small number : a,b,c
426 };
427
428 ////////////////////////////////////////
429 /** Internal: class to store the Graphic properties */
430 struct GraphicInfo {
GraphicInfoWriterPlsParserInternal::GraphicInfo431 GraphicInfo()
432 : m_width(0)
433 , m_graphicWidth(0)
434 {
435 for (auto &fl : m_flags) fl=0;
436 }
437
438 //! operator<<
operator <<(std::ostream & o,GraphicInfo const & g)439 friend std::ostream &operator<<(std::ostream &o, GraphicInfo const &g)
440 {
441 o << "width=" << g.m_graphicWidth << ", width[line]=" << g.m_width;
442 for (int i = 0; i < 6; i++) { // m_flags[6] seems to be junk
443 if (!g.m_flags[i]) continue;
444 o << ", gF" << i << "=" << std::hex << g.m_flags[i] << std::dec;
445 }
446 return o;
447 }
448 //! the first character
449 int m_width/** the line width */, m_graphicWidth /** the graphic width */;
450 //! some flag
451 int m_flags[7];
452 };
453
454 ////////////////////////////////////////
455 /** Internal: class to store the Section properties */
456 struct SectionInfo {
SectionInfoWriterPlsParserInternal::SectionInfo457 SectionInfo()
458 : m_numCol(0)
459 {
460 for (auto &dim : m_dim) dim = 0;
461 for (auto &fl : m_flags) fl = 0;
462 }
463
emptyWriterPlsParserInternal::SectionInfo464 bool empty() const
465 {
466 if (m_numCol) return false;
467 for (auto dim : m_dim)
468 if (dim) return false;
469 for (auto fl : m_flags)
470 if (fl) return false;
471 return true;
472 }
473 //! operator<<
operator <<(std::ostream & o,SectionInfo const & s)474 friend std::ostream &operator<<(std::ostream &o, SectionInfo const &s)
475 {
476 if (s.m_numCol) o << "numCols?=" << s.m_numCol << ",";
477 o << "dim?=[";
478 for (auto dim : s.m_dim)
479 o << dim << ",";
480 o << "],";
481 for (int i = 0; i < 4; i++) {
482 if (!s.m_flags[i]) continue;
483 o << ", sF" << i << "=" << std::hex << s.m_flags[i] << std::dec;
484 }
485 return o;
486 }
487 //! the number of columns(?)
488 int m_numCol;
489 //! unknown dimension
490 int m_dim[3];
491 //! some flag
492 int m_flags[4];
493 };
494
495 ////////////////////////////////////////
496 /** Internal: class to store the beginning of all paragraph data */
497 struct ParagraphData {
498 //! Constructor
ParagraphDataWriterPlsParserInternal::ParagraphData499 ParagraphData()
500 : m_type(-1)
501 , m_typeFlag(0)
502 , m_height(0)
503 , m_width(0)
504 , m_unknown(0)
505 , m_text("")
506 , m_fonts()
507 , m_endPos(0)
508 {
509 for (auto &indent : m_indent) indent=0;
510 for (auto &numData : m_numData) numData=0;
511 }
512 //! operator<<
operator <<(std::ostream & o,ParagraphData const & p)513 friend std::ostream &operator<<(std::ostream &o, ParagraphData const &p)
514 {
515 switch (p.m_type) {
516 case 0:
517 o << "text";
518 break;
519 case 1:
520 o << "section";
521 break; // checkme find only one time before a colbreak
522 case 2:
523 o << "text2";
524 break; // what is the difference with 1
525 case 3:
526 o << "colBreak";
527 break; // find only one time to change column
528 case 4:
529 o << "graphic";
530 break;
531 case 5:
532 o << "table";
533 break;
534 default:
535 o << "type=" << p.m_type;
536 break;
537 }
538 switch (p.m_typeFlag) {
539 case 0:
540 break;
541 case 0x80:
542 o << "[in table]";
543 break;
544 default:
545 o << "[" << std::hex << p.m_typeFlag << std::dec << "],";
546 }
547 o << ",";
548
549 o << "height=" << p.m_height << ",";
550 o << "witdh=" << p.m_width << ",";
551 if (p.m_indent[0]) o << "indent[left]=" << p.m_indent[0] << ",";
552 if (p.m_indent[1] != p.m_indent[0])
553 o << "indent[firstPos]=" << p.m_indent[1] << ",";
554 if (p.m_text.length()) o << "text='" << p.m_text << "',";
555 if (p.m_type==5) o << "numData[total]=" << p.m_unknown << ",";
556 else o << "unkn=" << p.m_unknown << ","; /* in text2: often 1, but can be 5|13|25|29 */
557 return o;
558 }
559
560 int m_type, m_typeFlag;
561 int m_height, m_width;
562 int m_indent[2]; // left indent and ?
563 int m_unknown;
564
565 std::string m_text;
566 std::vector<Font> m_fonts;
567
568 long m_endPos; // end of the data ( except if there is auxilliary data )
569 int m_numData[2]; // number of data[1] ( always font?), data[2]
570 };
571
572 ////////////////////////////////////////
573 //! Internal: the state of a WriterPlsParser
574 struct State {
575 //! constructor
StateWriterPlsParserInternal::State576 State()
577 : m_actPage(0)
578 , m_numPages(0)
579 , m_headerHeight(0)
580 , m_footerHeight(0)
581 {
582 }
583
584 int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
585
586 //! the information ( 0: main, 1: header, 2: footer)
587 WindowsInfo m_windows[3];
588
589 int m_headerHeight /** the header height if known */,
590 m_footerHeight /** the footer height if known */;
591 };
592
593 ////////////////////////////////////////
594 //! Internal: the subdocument of a WriterPlsParser
595 class SubDocument final : public MWAWSubDocument
596 {
597 public:
SubDocument(WriterPlsParser & pars,MWAWInputStreamPtr const & input,int zoneId)598 SubDocument(WriterPlsParser &pars, MWAWInputStreamPtr const &input, int zoneId)
599 : MWAWSubDocument(&pars, input, MWAWEntry()), m_id(zoneId)
600 {
601 }
602
603 //! destructor
~SubDocument()604 ~SubDocument() final {}
605
606 //! operator!=
operator !=(MWAWSubDocument const & doc) const607 bool operator!=(MWAWSubDocument const &doc) const final
608 {
609 if (MWAWSubDocument::operator!=(doc)) return true;
610 auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
611 if (!sDoc) return true;
612 if (m_id != sDoc->m_id) return true;
613 return false;
614 }
615
616 //! the parser function
617 void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
618
619 protected:
620 //! the subdocument id
621 int m_id;
622 };
623
parse(MWAWListenerPtr & listener,libmwaw::SubDocumentType)624 void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
625 {
626 if (!listener.get()) {
627 MWAW_DEBUG_MSG(("WriterPlsParserInternal::SubDocument::parse: no listener\n"));
628 return;
629 }
630 if (m_id != 1 && m_id != 2) {
631 MWAW_DEBUG_MSG(("WriterPlsParserInternal::SubDocument::parse: unknown zone\n"));
632 return;
633 }
634 auto *parser=dynamic_cast<WriterPlsParser *>(m_parser);
635 if (!parser) {
636 MWAW_DEBUG_MSG(("WriterPlsParserInternal::SubDocument::parse: no parser\n"));
637 return;
638 }
639
640 long pos = m_input->tell();
641 parser->sendWindow(m_id);
642 m_input->seek(pos, librevenge::RVNG_SEEK_SET);
643 }
644 }
645
646 ////////////////////////////////////////////////////////////
647 // constructor/destructor, ...
648 ////////////////////////////////////////////////////////////
WriterPlsParser(MWAWInputStreamPtr const & input,MWAWRSRCParserPtr const & rsrcParser,MWAWHeader * header)649 WriterPlsParser::WriterPlsParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
650 : MWAWTextParser(input, rsrcParser, header)
651 , m_state()
652 {
653 init();
654 }
655
~WriterPlsParser()656 WriterPlsParser::~WriterPlsParser()
657 {
658 }
659
init()660 void WriterPlsParser::init()
661 {
662 resetTextListener();
663 setAsciiName("main-1");
664
665 m_state.reset(new WriterPlsParserInternal::State);
666
667 // reduce the margin (in case, the page is not defined)
668 getPageSpan().setMargins(0.1);
669 }
670
671 ////////////////////////////////////////////////////////////
672 // position and height
673 ////////////////////////////////////////////////////////////
getTextHeight() const674 double WriterPlsParser::getTextHeight() const
675 {
676 return getPageSpan().getPageLength()-m_state->m_headerHeight/72.0-m_state->m_footerHeight/72.0;
677 }
678
679 ////////////////////////////////////////////////////////////
680 // new page
681 ////////////////////////////////////////////////////////////
newPage(int number)682 void WriterPlsParser::newPage(int number)
683 {
684 if (number <= m_state->m_actPage || number > m_state->m_numPages)
685 return;
686
687 while (m_state->m_actPage < number) {
688 m_state->m_actPage++;
689 if (!getTextListener() || m_state->m_actPage == 1)
690 continue;
691 getTextListener()->insertBreak(MWAWTextListener::PageBreak);
692 }
693 }
694
695
696
697 ////////////////////////////////////////////////////////////
698 // the parser
699 ////////////////////////////////////////////////////////////
parse(librevenge::RVNGTextInterface * docInterface)700 void WriterPlsParser::parse(librevenge::RVNGTextInterface *docInterface)
701 {
702 if (!getInput().get() || !checkHeader(nullptr)) throw(libmwaw::ParseException());
703 bool ok = true;
704 try {
705 // create the asciiFile
706 ascii().setStream(getInput());
707 ascii().open(asciiName());
708
709 checkHeader(nullptr);
710 ok = createZones();
711 ascii().addPos(getInput()->tell());
712 ascii().addNote("_");
713 if (ok) {
714 createDocument(docInterface);
715 sendWindow(0);
716 }
717
718 ascii().reset();
719 }
720 catch (...) {
721 MWAW_DEBUG_MSG(("WriterPlsParser::parse: exception catched when parsing\n"));
722 ok = false;
723 }
724
725 resetTextListener();
726 if (!ok) throw(libmwaw::ParseException());
727 }
728
729 ////////////////////////////////////////////////////////////
730 // create the document
731 ////////////////////////////////////////////////////////////
createDocument(librevenge::RVNGTextInterface * documentInterface)732 void WriterPlsParser::createDocument(librevenge::RVNGTextInterface *documentInterface)
733 {
734 if (!documentInterface) return;
735 if (getTextListener()) {
736 MWAW_DEBUG_MSG(("WriterPlsParser::createDocument: listener already exist\n"));
737 return;
738 }
739
740 // update the page
741 m_state->m_actPage = 0;
742
743 // create the page list
744 MWAWPageSpan ps(getPageSpan());
745 for (int i = 1; i < 3; i++) {
746 if (m_state->m_windows[i].m_paragraphs.size() == 0)
747 continue;
748 MWAWHeaderFooter hF((i==1) ? MWAWHeaderFooter::HEADER : MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
749 hF.m_subDocument.reset(new WriterPlsParserInternal::SubDocument(*this, getInput(), i));
750 ps.setHeaderFooter(hF);
751 }
752
753 m_state->m_numPages = int(m_state->m_windows[0].m_pages.size());
754 ps.setPageSpan(m_state->m_numPages+1);
755 std::vector<MWAWPageSpan> pageList(1,ps);
756 //
757 MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
758 setTextListener(listen);
759 listen->startDocument();
760 }
761
762
763 ////////////////////////////////////////////////////////////
764 //
765 // Intermediate level
766 //
767 ////////////////////////////////////////////////////////////
createZones()768 bool WriterPlsParser::createZones()
769 {
770 if (!readWindowsInfo(0) || !readPrintInfo())
771 return false;
772 for (int st = 1; st < 4; st++) {
773 bool ok = true;
774 switch (st) {
775 case 1:
776 ok = m_state->m_headerHeight > 0;
777 break;
778 case 2:
779 ok = m_state->m_footerHeight > 0;
780 break;
781 default:
782 break;
783 }
784 if (!ok) continue;
785 if (st !=3 && !readWindowsInfo(st))
786 return false;
787 if (!readWindowsZone(st==3 ? 0 : st))
788 return (st==3);
789 }
790
791 return true;
792 }
793
794 ////////////////////////////////////////////////////////////
795 //
796 // Low level
797 //
798 ////////////////////////////////////////////////////////////
799
800 ////////////////////////////////////////////////////////////
801 // read the header
802 ////////////////////////////////////////////////////////////
checkHeader(MWAWHeader * header,bool)803 bool WriterPlsParser::checkHeader(MWAWHeader *header, bool /*strict*/)
804 {
805 *m_state = WriterPlsParserInternal::State();
806
807 MWAWInputStreamPtr input = getInput();
808 if (!input || !input->hasDataFork())
809 return false;
810
811 int const headerSize=2;
812 if (!input->checkPosition(headerSize)) {
813 MWAW_DEBUG_MSG(("WriterPlsParser::checkHeader: file is too short\n"));
814 return false;
815 }
816 input->seek(0,librevenge::RVNG_SEEK_SET);
817 if (input->readULong(2) != 0x110)
818 return false;
819 ascii().addPos(0);
820 ascii().addNote("FileHeader");
821
822 if (!readWindowsInfo(0) || !readPrintInfo())
823 return false;
824 input->seek(2,librevenge::RVNG_SEEK_SET);
825 if (header)
826 header->reset(MWAWDocument::MWAW_T_WRITERPLUS, 1);
827
828 return true;
829 }
830
readWindowsInfo(int zone)831 bool WriterPlsParser::readWindowsInfo(int zone)
832 {
833 if (zone<0 || zone>=3) {
834 MWAW_DEBUG_MSG(("WriterPlsParser::readWindowsInfo:the zone seems bad\n"));
835 return false;
836 }
837 MWAWInputStreamPtr input = getInput();
838
839 long debPos = input->tell();
840 if (!input->checkPosition(debPos+0xf4)) {
841 MWAW_DEBUG_MSG(("WriterPlsParser::readWindowsInfo: file is too short\n"));
842 return false;
843 }
844
845 WriterPlsParserInternal::WindowsInfo info;
846 libmwaw::DebugStream f;
847 f << "Entries(WindowsZone)";
848 switch (zone) {
849 case 0:
850 break;
851 case 1:
852 f << "[Header]";
853 break;
854 case 2:
855 f << "[Footer]";
856 break;
857 default:
858 f << "[Unknown]";
859 break;
860 }
861 f << ":";
862 for (int i = 0; i < 2; i++) {
863 auto val = static_cast<int>(input->readLong(1));
864 f << "f" << i << "=" << val << ",";
865 }
866 f << "unkn=" << input->readLong(2);
867
868 long pos;
869 for (auto &i : info.m_zone) {
870 WriterPlsParserInternal::WindowsInfo::Zone infoZone;
871 infoZone.m_unknown[0] = static_cast<int>(input->readULong(1));
872 infoZone.m_width = static_cast<int>(input->readULong(2));
873 infoZone.m_unknown[1] = static_cast<int>(input->readULong(1));
874 infoZone.m_unknown[2] = static_cast<int>(input->readULong(2));
875 infoZone.m_size = static_cast<int>(input->readULong(2));
876 infoZone.m_number = static_cast<int>(input->readULong(2));
877 i = infoZone;
878 }
879 f << "," << info;
880
881 ascii().addPos(debPos);
882 ascii().addNote(f.str().c_str());
883
884 pos = input->tell();
885 ascii().addPos(pos);
886 ascii().addNote("WindowsZone(A-1)");
887 ascii().addPos(pos+12);
888 ascii().addNote("WindowsZone(A-2)");
889 ascii().addPos(pos+30);
890 ascii().addNote("WindowsZone(A-3)");
891 ascii().addPos(pos+60);
892 ascii().addNote("WindowsZone(A-4)");
893 ascii().addPos(pos+60+14);
894 ascii().addNote("WindowsZone(A-5)");
895 ascii().addPos(pos+60+14*2);
896 ascii().addNote("WindowsZone(A-6)");
897
898 pos = debPos+0xc2;
899 input->seek(pos, librevenge::RVNG_SEEK_SET);
900 f.str("");
901 f << "WindowsZone(A-7):";
902 auto val = static_cast<int>(input->readLong(2));
903 if (val) f << "unkn=" << val << ",";
904 auto width = static_cast<int>(input->readLong(2));
905 info.m_footerY = static_cast<int>(input->readLong(2));
906 info.m_headerY = static_cast<int>(input->readLong(2));
907 auto height = static_cast<int>(input->readLong(2));
908 info.m_pageDim = MWAWVec2i(width, height);
909 f << "page=" << info.m_pageDim << ",";
910 if (info.m_headerY)
911 f << "header[height]=" << info.m_headerY << ",";
912 if (info.m_footerY)
913 f << "footer[height]=" << info.m_footerY << ",";
914 for (int i = 0; i < 3; i++) // always 17 12 0 left|right ?
915 f << "f" << i << "=" << static_cast<int>(input->readLong(2)) << ",";
916 ascii().addPos(pos);
917 ascii().addNote(f.str().c_str());
918 if (info.dimensionInvalid())
919 return false;
920 if (zone == 0) {
921 m_state->m_headerHeight = info.m_headerY;
922 m_state->m_footerHeight = info.m_footerY;
923 }
924 pos = input->tell();
925 f.str("");
926 f << "WindowsZone(B):";
927 int dim[4];
928 for (auto &d : dim) d = static_cast<int>(input->readLong(2));
929 f << "dim(?)=" << dim[1] << "x" << dim[0] << "-" << dim[3] << "x" << dim[2] << ",";
930 for (int i = 0; i < 2; i++) {
931 auto fl = static_cast<int>(input->readLong(1)); // almost always 0 except some time 1
932 if (fl) f << "fl" << i << "=" << fl << ",";
933 }
934 for (int i = 0; i < 6; i++) {
935 int values[3];
936 values[0] = static_cast<int>(input->readULong(1));
937 values[1] = static_cast<int>(input->readLong(2));
938 values[2] = static_cast<int>(input->readULong(1));
939 if (values[0] == 0 && values[1] == 0 && values[2] == 0) continue;
940 f << "f" << i << "=[" << values[0] << ", w=" << values[1]
941 << ", " << std::hex << values[2] << std::dec << "],";
942 }
943
944 m_state->m_windows[zone]=info;
945
946 ascii().addPos(pos);
947 ascii().addNote(f.str().c_str());
948
949 return true;
950 }
951
952 ////////////////////////////////////////////////////////////
953 // read all the windows zone info
954 ////////////////////////////////////////////////////////////
readWindowsZone(int zone)955 bool WriterPlsParser::readWindowsZone(int zone)
956 {
957 if (zone<0 || zone>=3) {
958 MWAW_DEBUG_MSG(("WriterPlsParser::readWindowsZone:the zone seems bad\n"));
959 return false;
960 }
961
962 MWAWInputStreamPtr input = getInput();
963 auto &wInfo = m_state->m_windows[zone];
964
965 libmwaw::DebugStream f;
966 for (int wh=1; wh < 7; wh++) {
967 auto const &z = wInfo.m_zone[wh];
968 int length = z.m_size;
969 if (!length) continue;
970
971 long pos = input->tell();
972 input->seek(length, librevenge::RVNG_SEEK_CUR);
973 if (long(input->tell()) != pos+length) {
974 MWAW_DEBUG_MSG(("WriterPlsParser::readWindowsZone: zone is too short\n"));
975 return false;
976 }
977 input->seek(pos, librevenge::RVNG_SEEK_SET);
978
979 bool ok = false;
980 switch (wh) {
981 case 1:
982 ok=readPageInfo(zone);
983 break;
984 case 2:
985 ok=readColInfo(zone);
986 break;
987 case 3:
988 // need to get next block
989 ok = readParagraphInfo(zone);
990 if (!ok) return false;
991 break;
992 default:
993 break;
994 }
995 if (ok) continue;
996
997 input->seek(pos, librevenge::RVNG_SEEK_SET);
998 if (z.m_number && (length % z.m_number) == 0) {
999 int dataSz = length / z.m_number;
1000 for (int i = 0; i < z.m_number; i++) {
1001 f.str("");
1002 f << "Entries(Zone" << wh << ")-" << i << ":";
1003 ascii().addPos(input->tell());
1004 ascii().addNote(f.str().c_str());
1005 input->seek(dataSz, librevenge::RVNG_SEEK_CUR);
1006 }
1007 }
1008 else {
1009 f.str("");
1010 f << "Entries(Zone" << wh << "):";
1011 ascii().addPos(input->tell());
1012 ascii().addNote(f.str().c_str());
1013 input->seek(length, librevenge::RVNG_SEEK_CUR);
1014 }
1015 }
1016
1017 for (int i = int(wInfo.m_paragraphs.size())-1; i >= 0; i--) {
1018 auto const &pInfo = wInfo.m_paragraphs[size_t(i)];
1019 if (!pInfo.m_pos) continue;
1020
1021 input->seek(pInfo.m_pos, librevenge::RVNG_SEEK_SET);
1022 auto length = long(input->readULong(2));
1023 auto length2 = long(input->readULong(2));
1024 long endPos = pInfo.m_pos+4+length+length2;
1025 input->seek(endPos, librevenge::RVNG_SEEK_SET);
1026 if (long(input->tell()) != endPos) {
1027 MWAW_DEBUG_MSG(("WriterPlsParser::readWindowsZone: data zone is too short\n"));
1028 return false;
1029 }
1030 switch (pInfo.getType()) {
1031 case 4:
1032 length = long(input->readULong(4));
1033 input->seek(length, librevenge::RVNG_SEEK_CUR);
1034 if (long(input->tell()) != endPos+length+4) {
1035 MWAW_DEBUG_MSG(("WriterPlsParser::readWindowsZone: graphics zone is too short\n"));
1036 return false;
1037 }
1038 break;
1039 default: // 0,1,2,3, 5 : ok, other ?
1040 break;
1041 }
1042 return true;
1043 }
1044 return true;
1045 }
1046
1047 ////////////////////////////////////////////////////////////
1048 // send the windows zone info
1049 ////////////////////////////////////////////////////////////
sendWindow(int zone,MWAWVec2i limits)1050 bool WriterPlsParser::sendWindow(int zone, MWAWVec2i limits)
1051 {
1052 MWAWTextListenerPtr listener=getTextListener();
1053 if (!listener) {
1054 MWAW_DEBUG_MSG(("WriterPlsParser::sendWindow: can not find a listener\n"));
1055 return false;
1056 }
1057 if (zone<0 || zone>=3) {
1058 MWAW_DEBUG_MSG(("WriterPlsParser::sendWindow:the zone seems bad\n"));
1059 return false;
1060 }
1061 auto &wInfo = m_state->m_windows[zone];
1062
1063 bool sendAll = limits[0] < 0;
1064
1065 auto maxPages = int(wInfo.m_pages.size());
1066 if (maxPages == 0 || zone || !sendAll) maxPages = 1;
1067
1068 int actParag = 0;
1069 int actCol = 0, numCols = 0;
1070 for (int pg = 0; pg < maxPages; pg++) {
1071 int endParag = 0;
1072 if (!sendAll) {
1073 actParag = limits[0];
1074 endParag = limits[1];
1075 if (endParag <= actParag) {
1076 MWAW_DEBUG_MSG(("WriterPlsParser::sendWindow: pb2 with limits\n"));
1077 return true;
1078 }
1079 }
1080 else {
1081 if (zone == 0) {
1082 newPage(pg+1);
1083 actCol = numCols ? 1 : 0;
1084 }
1085 if (pg == maxPages-1 || wInfo.m_pages.size() == 0)
1086 endParag = int(wInfo.m_paragraphs.size());
1087 else {
1088 endParag = wInfo.m_pages[size_t(pg)+1].m_firstLine-1;
1089 if (endParag == -1 || endParag < actParag) {
1090 MWAW_DEBUG_MSG(("WriterPlsParser::sendWindow: pb with page zone\n"));
1091 continue;
1092 }
1093 }
1094 }
1095 if (endParag > int(wInfo.m_paragraphs.size())) {
1096 MWAW_DEBUG_MSG(("WriterPlsParser::sendWindow: pb with limits\n"));
1097 endParag = int(wInfo.m_paragraphs.size());
1098 }
1099
1100 for (int i = actParag; i < endParag; i++) {
1101 auto const &pInfo = wInfo.m_paragraphs[size_t(i)];
1102 if (!pInfo.m_pos) {
1103 readText(pInfo);
1104 continue;
1105 }
1106 bool ok = true;
1107 switch (pInfo.getType()) {
1108 case 3: // col break: seems similar to an entry data (with a text zone which does not contain any character)
1109 if (numCols) {
1110 if (actCol >numCols) {
1111 MWAW_DEBUG_MSG(("WriterPlsParser::sendWindow: pb with col break\n"));
1112 }
1113 else {
1114 actCol++;
1115 listener->insertBreak(MWAWTextListener::ColumnBreak);
1116 }
1117 }
1118 MWAW_FALLTHROUGH;
1119 case 0:
1120 case 2:
1121 ok = readText(pInfo);
1122 break;
1123 case 1: {
1124 MWAWSection section;
1125 bool canCreateSection = sendAll && zone == 0 && actCol == numCols;
1126 if (findSection(zone, MWAWVec2i(i, endParag), section)) {
1127 if (!canCreateSection) {
1128 if (section.numColumns()>1) {
1129 MWAW_DEBUG_MSG(("WriterPlsParser::sendWindow: find a section in auxilliary zone\n"));
1130 }
1131 }
1132 else {
1133 if (listener->isSectionOpened())
1134 listener->closeSection();
1135 listener->openSection(section);
1136 numCols = listener->getSection().numColumns();
1137 if (numCols<=1) numCols=0;
1138 actCol = numCols ? 1 : 0;
1139 canCreateSection = false;
1140 }
1141 }
1142
1143 ok = readSection(pInfo, canCreateSection);
1144 break;
1145 }
1146 case 4:
1147 ok = readGraphic(pInfo);
1148 break;
1149 case 5:
1150 if (pInfo.m_numLines + i <= endParag) {
1151 if ((ok = readTable(pInfo))) {
1152 listener->openTableRow(float(pInfo.m_height), librevenge::RVNG_POINT);
1153
1154 for (size_t j = 0; j < pInfo.m_linesHeight.size(); j++) {
1155 int numData = pInfo.m_linesHeight[j];
1156 MWAWCell cell;
1157 cell.setPosition(MWAWVec2i(int(j), 0));
1158 listener->openTableCell(cell);
1159 sendWindow(zone, MWAWVec2i(i+1, i+1+numData));
1160 i += numData;
1161 listener->closeTableCell();
1162 }
1163
1164 listener->closeTableRow();
1165 listener->closeTable();
1166 }
1167 }
1168 else {
1169 MWAW_DEBUG_MSG(("WriterPlsParser::sendWindow: table across a page\n"));
1170 }
1171 break;
1172 default:
1173 ok = readUnknown(pInfo);
1174 break;
1175 }
1176 if (!ok) {
1177 libmwaw::DebugStream f;
1178 f << "Entries(Unknown):" << pInfo;
1179 ascii().addPos(pInfo.m_pos);
1180 ascii().addNote(f.str().c_str());
1181 }
1182 }
1183 actParag = endParag;
1184 }
1185 return true;
1186 }
1187
1188
1189 /*
1190 * find the column size which correspond to a limit
1191 *
1192 * Note: complex because we need to read the file in order to find the limit
1193 */
findSection(int zone,MWAWVec2i limits,MWAWSection & sec)1194 bool WriterPlsParser::findSection(int zone, MWAWVec2i limits, MWAWSection &sec)
1195 {
1196 if (zone<0 || zone>=3) {
1197 MWAW_DEBUG_MSG(("WriterPlsParser::findSection:the zone seems bad\n"));
1198 return false;
1199 }
1200 auto &wInfo = m_state->m_windows[zone];
1201
1202 sec=MWAWSection();
1203 std::vector<int> listPos;
1204 if (!wInfo.getColumnLimitsFor(limits[0], listPos))
1205 return false;
1206
1207 size_t numPos = listPos.size();
1208 if (!numPos)
1209 return true;
1210 if (listPos[numPos-1] >= limits[1]) {
1211 MWAW_DEBUG_MSG(("WriterPlsParser::findSection: columns across a page\n"));
1212 return false;
1213 }
1214
1215 MWAWInputStreamPtr input = getInput();
1216 int totalSize = 0;
1217 for (auto &line : listPos) {
1218 long pos = wInfo.m_paragraphs[size_t(line)].m_pos;
1219 if (!pos) {
1220 MWAW_DEBUG_MSG(("WriterPlsParser::findSection: bad data pos\n"));
1221 return false;
1222 }
1223 input->seek(pos, librevenge::RVNG_SEEK_SET);
1224 if (input->readLong(2)) {
1225 MWAW_DEBUG_MSG(("WriterPlsParser::findSection: find a text size\n"));
1226 return false;
1227 }
1228 input->seek(8, librevenge::RVNG_SEEK_CUR); // sz2 and type, h, indent
1229 auto val = static_cast<int>(input->readLong(2));
1230 if (val <= 0 || long(input->tell()) != pos + 12) {
1231 MWAW_DEBUG_MSG(("WriterPlsParser::findSection: file is too short\n"));
1232 return false;
1233 }
1234 totalSize += val;
1235 MWAWSection::Column col;
1236 col.m_width=val;
1237 col.m_widthUnit=librevenge::RVNG_POINT;
1238 sec.m_columns.push_back(col);
1239 }
1240 if (sec.m_columns.size()==1)
1241 sec.m_columns.resize(0);
1242 if (totalSize >= int(72.*getPageWidth())) {
1243 MWAW_DEBUG_MSG(("WriterPlsParser::findSection: total size is too big\n"));
1244 return false;
1245 }
1246 return true;
1247 }
1248
1249 ////////////////////////////////////////////////////////////
1250 // read all the windows zone info
1251 ////////////////////////////////////////////////////////////
readPageInfo(int zone)1252 bool WriterPlsParser::readPageInfo(int zone)
1253 {
1254 if (zone<0 || zone>=3) {
1255 MWAW_DEBUG_MSG(("WriterPlsParser::readPageInfo:the zone seems bad\n"));
1256 return false;
1257 }
1258
1259 MWAWInputStreamPtr input = getInput();
1260 libmwaw::DebugStream f;
1261
1262 auto &wInfo = m_state->m_windows[zone];
1263 int numPages = wInfo.m_zone[1].m_number;
1264 if (wInfo.m_zone[1].m_size != numPages * 10) {
1265 MWAW_DEBUG_MSG(("WriterPlsParser::readPageInfo: odd page size\n"));
1266 return false;
1267 }
1268
1269 int actNumLine = 0;
1270 auto maxHeight = int(72.*getTextHeight()+20.);
1271 if (maxHeight < 1000) maxHeight = 1000;
1272 int prevTotalHeight = 0;
1273
1274 for (int page = 0; page < numPages; page++) {
1275 long pos = input->tell();
1276 WriterPlsParserInternal::PageInfo pInfo;
1277 pInfo.m_firstLine = static_cast<int>(input->readLong(2));
1278 if ((page == 0 && pInfo.m_firstLine != 1) || pInfo.m_firstLine < actNumLine)
1279 return false;
1280 actNumLine=pInfo.m_firstLine;
1281 for (auto &unkn : pInfo.m_unknown) unkn = static_cast<int>(input->readLong(2));
1282 pInfo.m_heightFromBegin = static_cast<int>(input->readULong(2));
1283 if (pInfo.m_heightFromBegin < prevTotalHeight) return false;
1284 prevTotalHeight = pInfo.m_heightFromBegin;
1285 pInfo.m_height = static_cast<int>(input->readULong(2));
1286 if (pInfo.m_height > maxHeight) return false;
1287
1288 wInfo.m_pages.push_back(pInfo);
1289 f.str("");
1290 f << "Entries(PageInfo)-"<< page+1 << ":" << pInfo;
1291
1292 ascii().addPos(pos);
1293 ascii().addNote(f.str().c_str());
1294 }
1295
1296 return true;
1297 }
1298
1299 ////////////////////////////////////////////////////////////
1300 // read a windows paragraph info
1301 ////////////////////////////////////////////////////////////
getParagraph(WriterPlsParserInternal::ParagraphData const & data)1302 MWAWParagraph WriterPlsParser::getParagraph(WriterPlsParserInternal::ParagraphData const &data)
1303 {
1304 MWAWParagraph para;
1305
1306 para.m_marginsUnit=librevenge::RVNG_POINT;
1307 // decrease a little left indent to avoid some page width pb
1308 double left=double(data.m_indent[0])-20.-72.*getPageSpan().getMarginLeft();
1309 if (left > 0)
1310 para.m_margins[1]=left;
1311 para.m_margins[0]=double(data.m_indent[1]-data.m_indent[0]);
1312 if (getTextListener() && getTextListener()->getSection().numColumns() > 1)
1313 return para; // too dangerous to set the paragraph width in this case...
1314 double right=getPageWidth()*72.-double(data.m_width);
1315 if (right > 0)
1316 para.m_margins[2]=right;
1317 return para;
1318 }
1319
readParagraphInfo(int zone)1320 bool WriterPlsParser::readParagraphInfo(int zone)
1321 {
1322 if (zone<0 || zone>=3) {
1323 MWAW_DEBUG_MSG(("WriterPlsParser::readParagraphInfo:the zone seems bad\n"));
1324 return false;
1325 }
1326
1327 libmwaw::DebugStream f;
1328
1329 MWAWInputStreamPtr input = getInput();
1330 WriterPlsParserInternal::WindowsInfo &wInfo = m_state->m_windows[zone];
1331 int numPara = wInfo.m_zone[3].m_number;
1332 long endPos = long(input->tell()) + wInfo.m_zone[3].m_size;
1333
1334 int para = 0;
1335 while (para <= numPara) {
1336 long pos = input->tell();
1337 if (pos == endPos) break;
1338 if (pos > endPos) return false;
1339 WriterPlsParserInternal::ParagraphInfo pInfo;
1340
1341 f.str("");
1342 f << "Entries(ParaInfo)-"<< para+1 << ":";
1343 auto wh = static_cast<int>(input->readLong(1));
1344 if ((wh%2) == 0) {
1345 if (wh < 4) return false;
1346 for (int i = 0; i < (wh-4)/2; i++)
1347 pInfo.m_unknowns.push_back(static_cast<int>(input->readULong(2)));
1348 pInfo.m_type = -1;
1349 pInfo.m_numLines = static_cast<int>(input->readULong(1)); // probably numLine
1350 pInfo.m_height = static_cast<int>(input->readULong(2));
1351 f << pInfo;
1352 ascii().addPos(pos);
1353 ascii().addNote(f.str().c_str());
1354 continue;
1355 }
1356 para++;
1357 pInfo.m_flags[0] = (wh>>1);
1358 pInfo.m_flags[1] = static_cast<int>(input->readULong(1)); // almost always 0
1359 pInfo.m_type = static_cast<int>(input->readULong(1));
1360 pInfo.m_numLines = static_cast<int>(input->readULong(1)); // or numColumns if type==5
1361 pInfo.m_height = static_cast<int>(input->readULong(2));
1362 pInfo.m_pos = long(input->readULong(4));
1363 pInfo.m_flags[2] = static_cast<int>(input->readULong(1)); // almost always 0
1364 pInfo.m_width = static_cast<int>(input->readULong(2));
1365 for (int i = 3; i < 5; i++)
1366 pInfo.m_flags[i] = static_cast<int>(input->readULong(1));
1367 if (pInfo.m_numLines!=1) {
1368 for (int i = 0; i < pInfo.m_numLines; i++)
1369 pInfo.m_linesHeight.push_back(static_cast<int>(input->readULong(1)));
1370 }
1371 pInfo.m_height2 = static_cast<int>(input->readULong(1));
1372 wInfo.m_paragraphs.push_back(pInfo);
1373 f << pInfo;
1374
1375 ascii().addPos(pos);
1376 ascii().addNote(f.str().c_str());
1377
1378 }
1379
1380 return true;
1381 }
1382
1383 ////////////////////////////////////////////////////////////
1384 // read all the windows col info ?
1385 ////////////////////////////////////////////////////////////
readColInfo(int zone)1386 bool WriterPlsParser::readColInfo(int zone)
1387 {
1388 if (zone<0 || zone>=3) {
1389 MWAW_DEBUG_MSG(("WriterPlsParser::readColInfo:the zone seems bad\n"));
1390 return false;
1391 }
1392
1393 libmwaw::DebugStream f;
1394
1395 WriterPlsParserInternal::WindowsInfo &wInfo = m_state->m_windows[zone];
1396 int numCols = wInfo.m_zone[2].m_number;
1397 if (wInfo.m_zone[2].m_size != numCols * 16) {
1398 MWAW_DEBUG_MSG(("WriterPlsParser::readColInfo: odd col size\n"));
1399 return false;
1400 }
1401
1402 MWAWInputStreamPtr input = getInput();
1403 for (int col = 0; col < numCols; col++) {
1404 long pos = input->tell();
1405 WriterPlsParserInternal::ColumnInfo cInfo;
1406 cInfo.m_col = static_cast<int>(input->readLong(2));
1407 cInfo.m_unknown[0] = static_cast<int>(input->readLong(2));
1408 cInfo.m_numCol = static_cast<int>(input->readLong(2));
1409 cInfo.m_firstLine = static_cast<int>(input->readLong(2));
1410 for (int i = 1; i < 4; i++)
1411 cInfo.m_unknown[i] = static_cast<int>(input->readLong(2));
1412 cInfo.m_height = static_cast<int>(input->readLong(2));
1413 wInfo.m_columns.push_back(cInfo);
1414
1415 f.str("");
1416 f << "Entries(ColInfo):" << cInfo;
1417 ascii().addPos(pos);
1418 ascii().addNote(f.str().c_str());
1419 }
1420
1421 return true;
1422 }
1423
readText(WriterPlsParserInternal::ParagraphInfo const & info)1424 bool WriterPlsParser::readText(WriterPlsParserInternal::ParagraphInfo const &info)
1425 {
1426 WriterPlsParserInternal::ParagraphData data;
1427 std::vector<WriterPlsParserInternal::Line> lines;
1428 if (!info.m_pos) {
1429 MWAW_DEBUG_MSG(("WriterPlsParser::readText: pb with pos\n"));
1430 return false;
1431 }
1432
1433 if (!readParagraphData(info, true, data))
1434 return false;
1435
1436 libmwaw::DebugStream f;
1437
1438 MWAWInputStreamPtr input = getInput();
1439 long pos = input->tell();
1440 f.str("");
1441 f << "Paragraph" << data.m_type << "(II):";
1442
1443 int numLines = data.m_numData[1];
1444 if (!readLines(info, numLines, lines)) {
1445 MWAW_DEBUG_MSG(("WriterPlsParser::readText: pb with the lines\n"));
1446 lines.resize(0);
1447 input->seek(pos+numLines*16, librevenge::RVNG_SEEK_SET);
1448 f << "###lines,";
1449 }
1450 for (int i = 0; i < numLines; i++)
1451 f << "line" << i << "=[" << lines[size_t(i)] << "],";
1452
1453 if (long(input->tell()) != data.m_endPos) {
1454 ascii().addDelimiter(input->tell(), '|');
1455 input->seek(data.m_endPos, librevenge::RVNG_SEEK_SET);
1456 f << "#endPos,";
1457 }
1458
1459 ascii().addPos(pos);
1460 ascii().addNote(f.str().c_str());
1461
1462 ascii().addPos(input->tell());
1463 ascii().addNote("_");
1464
1465 if (!getTextListener())
1466 return true;
1467 std::string const &text = data.m_text;
1468 auto const &fonts = data.m_fonts;
1469 auto numChars = long(text.length());
1470 size_t actFont = 0, numFonts = fonts.size();
1471 int actLine = 0;
1472 numLines=int(lines.size());
1473 MWAWParagraph para=getParagraph(data);
1474
1475 if (numLines == 0 && info.m_height > 0) {
1476 para.setInterline(info.m_height, librevenge::RVNG_POINT);
1477 getTextListener()->setParagraph(para);
1478 }
1479 for (long c = 0; c < numChars; c++) {
1480 if (actFont < numFonts && c == fonts[actFont].m_firstChar)
1481 getTextListener()->setFont(fonts[actFont++].m_font);
1482 if (actLine < numLines && c == lines[size_t(actLine)].m_firstChar) {
1483 if (actLine) getTextListener()->insertEOL();
1484 if (numLines == 1 && info.m_height > lines[0].m_height) {
1485 para.setInterline(info.m_height, librevenge::RVNG_POINT);
1486 getTextListener()->setParagraph(para);
1487 }
1488 else if (lines[size_t(actLine)].m_height) {
1489 para.setInterline(lines[size_t(actLine)].m_height, librevenge::RVNG_POINT);
1490 getTextListener()->setParagraph(para);
1491 }
1492 actLine++;
1493 }
1494
1495 auto ch = static_cast<unsigned char>(text[size_t(c)]);
1496 if (ch == 0x9)
1497 getTextListener()->insertTab();
1498 else
1499 getTextListener()->insertCharacter(ch);
1500 }
1501 if (info.getType() != 3)
1502 getTextListener()->insertEOL();
1503
1504 return true;
1505 }
1506
readSection(WriterPlsParserInternal::ParagraphInfo const & info,bool mainBlock)1507 bool WriterPlsParser::readSection(WriterPlsParserInternal::ParagraphInfo const &info, bool mainBlock)
1508 {
1509 WriterPlsParserInternal::ParagraphData data;
1510
1511 if (!info.m_pos) {
1512 MWAW_DEBUG_MSG(("WriterPlsParser::readSection: can not find the beginning pos\n"));
1513 return false;
1514 }
1515
1516 if (!readParagraphData(info, true, data))
1517 return false;
1518
1519 libmwaw::DebugStream f;
1520
1521 MWAWInputStreamPtr input = getInput();
1522 long pos = input->tell();
1523 f.str("");
1524 f << "Paragraph" << data.m_type << "(II):";
1525
1526 int numData = data.m_numData[1];
1527 if (numData != 1) {
1528 MWAW_DEBUG_MSG(("WriterPlsParser::readSection: unexpected num of data: %d \n", numData));
1529 }
1530
1531 std::vector<WriterPlsParserInternal::SectionInfo> sections;
1532 for (int i = 0; i < numData; i++) {
1533 WriterPlsParserInternal::SectionInfo section;
1534 for (int j = 0; j < 2; j++)
1535 section.m_flags[j] = static_cast<int>(input->readLong(2));
1536 section.m_numCol = static_cast<int>(input->readLong(2)); // checkme
1537 for (auto &dim : section.m_dim) dim = static_cast<int>(input->readLong(2));
1538 for (int j = 2; j < 4; j++)
1539 section.m_flags[j] = static_cast<int>(input->readLong(2));
1540 sections.push_back(section);
1541 if (!section.empty())
1542 f << "section" << i << "=[" << section << "],";
1543 }
1544
1545 if (long(input->tell()) != data.m_endPos) {
1546 ascii().addDelimiter(input->tell(), '|');
1547 input->seek(data.m_endPos, librevenge::RVNG_SEEK_SET);
1548 f << "#endPos,";
1549 }
1550
1551 if (getTextListener() && mainBlock) {
1552 if (!getTextListener()->isSectionOpened())
1553 getTextListener()->openSection(MWAWSection());
1554 }
1555 ascii().addPos(pos);
1556 ascii().addNote(f.str().c_str());
1557
1558 ascii().addPos(input->tell());
1559 ascii().addNote("_");
1560 return true;
1561 }
1562
readTable(WriterPlsParserInternal::ParagraphInfo const & info)1563 bool WriterPlsParser::readTable(WriterPlsParserInternal::ParagraphInfo const &info)
1564 {
1565 WriterPlsParserInternal::ParagraphData data;
1566
1567 if (!info.m_pos) {
1568 MWAW_DEBUG_MSG(("WriterPlsParser::readTable: can not find the beginning pos\n"));
1569 return false;
1570 }
1571
1572 if (!readParagraphData(info, true, data))
1573 return false;
1574
1575 libmwaw::DebugStream f;
1576
1577 MWAWInputStreamPtr input = getInput();
1578 long pos = input->tell();
1579 f.str("");
1580 f << "Paragraph" << data.m_type << "(II):";
1581
1582 int numData = data.m_numData[1];
1583 if (numData <= 1) {
1584 MWAW_DEBUG_MSG(("WriterPlsParser::readTable: unexpected num of data: %d \n", numData));
1585 }
1586
1587 std::vector<WriterPlsParserInternal::ColumnTableInfo> columns;
1588 for (int i = 0; i < numData; i++) {
1589 WriterPlsParserInternal::ColumnTableInfo cols;
1590 cols.m_height = static_cast<int>(input->readLong(2));
1591 for (auto &colX : cols.m_colX) colX = static_cast<int>(input->readLong(2));
1592 cols.m_numData = static_cast<int>(input->readLong(2));
1593 cols.m_flags = static_cast<int>(input->readLong(2));
1594 for (auto &textX : cols.m_textX) textX = static_cast<int>(input->readLong(2));
1595
1596 columns.push_back(cols);
1597 f << "col" << i << "=[" << cols << "],";
1598 }
1599
1600 if (getTextListener()) {
1601 std::vector<float> colSize(static_cast<size_t>(numData));
1602 for (int i = 0; i < numData; i++) {
1603 auto const &cols = columns[size_t(i)];
1604 colSize[size_t(i)] = float(cols.m_colX[1]-cols.m_colX[0]);
1605 }
1606 MWAWTable table(MWAWTable::TableDimBit);
1607 table.setColsSize(colSize);
1608 // use the same function than getParagraph to respect alignment
1609 int left=columns.empty() ? 0 : columns[0].m_colX[0]-20-int(72.*getPageSpan().getMarginLeft());
1610 if (left)
1611 table.setAlignment(MWAWTable::Left, float(left));
1612 getTextListener()->openTable(table);
1613 }
1614
1615 if (long(input->tell()) != data.m_endPos) {
1616 ascii().addDelimiter(input->tell(), '|');
1617 input->seek(data.m_endPos, librevenge::RVNG_SEEK_SET);
1618 f << "#endPos,";
1619 }
1620
1621 ascii().addPos(pos);
1622 ascii().addNote(f.str().c_str());
1623
1624 ascii().addPos(input->tell());
1625 ascii().addNote("_");
1626
1627 return true;
1628 }
1629
readGraphic(WriterPlsParserInternal::ParagraphInfo const & info)1630 bool WriterPlsParser::readGraphic(WriterPlsParserInternal::ParagraphInfo const &info)
1631 {
1632 WriterPlsParserInternal::ParagraphData data;
1633
1634 if (!info.m_pos) {
1635 MWAW_DEBUG_MSG(("WriterPlsParser::readGraphic: can not find the beginning pos\n"));
1636 return false;
1637 }
1638
1639 if (!readParagraphData(info, true, data))
1640 return false;
1641
1642 libmwaw::DebugStream f;
1643
1644 MWAWInputStreamPtr input = getInput();
1645 long pos = input->tell();
1646 f.str("");
1647 f << "Paragraph" << data.m_type << "(II):";
1648
1649 int numData = data.m_numData[1];
1650 if (numData != 1) {
1651 MWAW_DEBUG_MSG(("WriterPlsParser::readGraphic: unexpected num of data: %d \n", numData));
1652 }
1653
1654 std::vector<WriterPlsParserInternal::GraphicInfo> graphicsInfos;
1655 for (int i = 0; i < numData; i++) {
1656 WriterPlsParserInternal::GraphicInfo gInfo;
1657 gInfo.m_flags[0] = static_cast<int>(input->readLong(1));
1658 gInfo.m_width = static_cast<int>(input->readLong(2));
1659 gInfo.m_flags[1] = static_cast<int>(input->readULong(1)); //
1660 gInfo.m_graphicWidth = static_cast<int>(input->readLong(2)); // total width
1661 for (int j = 2; j < 7; j++)
1662 gInfo.m_flags[j] = static_cast<int>(input->readLong(2));
1663 f << "data" << i << "=[" << gInfo << "],";
1664 graphicsInfos.push_back(gInfo);
1665 }
1666 if (long(input->tell()) != data.m_endPos) {
1667 ascii().addDelimiter(input->tell(), '|');
1668 input->seek(data.m_endPos, librevenge::RVNG_SEEK_SET);
1669 f << "#endPos,";
1670 }
1671
1672 ascii().addPos(pos);
1673 ascii().addNote(f.str().c_str());
1674
1675 // read the graphic:
1676 pos = input->tell();
1677 auto length = long(input->readULong(4));
1678 if (!length) {
1679 MWAW_DEBUG_MSG(("WriterPlsParser::readGraphic: find a zero size graphics\n"));
1680 ascii().addPos(pos);
1681 ascii().addNote("Entries(Graphic):#sz=0");
1682 return true;
1683 }
1684 long endPos = pos+4+length;
1685 input->seek(length, librevenge::RVNG_SEEK_CUR);
1686 if (long(input->tell()) != endPos) {
1687 MWAW_DEBUG_MSG(("WriterPlsParser::readGraphic: file is too short\n"));
1688 input->seek(pos, librevenge::RVNG_SEEK_SET);
1689 return false;
1690 }
1691
1692 f.str("");
1693 f << "Paragraph" << data.m_type << "(III):";
1694
1695 MWAWBox2f box;
1696 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1697 auto res = MWAWPictData::check(input, static_cast<int>(length), box);
1698 if (res == MWAWPict::MWAW_R_BAD) {
1699 MWAW_DEBUG_MSG(("WriterPlsParser::readGraphic: can not find the picture\n"));
1700 input->seek(endPos, librevenge::RVNG_SEEK_SET);
1701 return false;
1702 }
1703
1704 MWAWVec2f actualSize(0,0), naturalSize(actualSize);
1705 if (box.size().x() > 0 && box.size().y() > 0) {
1706 if (actualSize.x() <= 0 || actualSize.y() <= 0) actualSize = box.size();
1707 naturalSize = box.size();
1708 }
1709 else {
1710 MWAW_DEBUG_MSG(("WriterPlsParser::readGraphic: can not find the picture size\n"));
1711 actualSize = MWAWVec2f(100,100);
1712 }
1713
1714 MWAWPosition pictPos=MWAWPosition(MWAWVec2f(0,0),actualSize, librevenge::RVNG_POINT);
1715 pictPos.setRelativePosition(MWAWPosition::Char);
1716 pictPos.setNaturalSize(naturalSize);
1717 f << pictPos;
1718
1719 // get the picture
1720 input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1721 std::shared_ptr<MWAWPict> pict(MWAWPictData::get(input, static_cast<int>(length)));
1722 if (getTextListener()) {
1723 auto para=getTextListener()->getParagraph();
1724 para.setInterline(info.m_height, librevenge::RVNG_POINT);
1725 getTextListener()->setParagraph(para);
1726 MWAWEmbeddedObject picture;
1727 if (pict && pict->getBinary(picture))
1728 getTextListener()->insertPicture(pictPos, picture);
1729 getTextListener()->insertEOL();
1730 para.setInterline(1.0, librevenge::RVNG_PERCENT);
1731 getTextListener()->setParagraph(para);
1732 }
1733 if (pict)
1734 ascii().skipZone(pos+4, pos+4+length-1);
1735
1736 input->seek(endPos, librevenge::RVNG_SEEK_SET);
1737
1738 ascii().addPos(pos);
1739 ascii().addNote(f.str().c_str());
1740
1741 ascii().addPos(endPos);
1742 ascii().addNote("_");
1743
1744 return true;
1745 }
1746
1747 ////////////////////////////////////////////////////////////
1748 // read a paragraph
1749 ////////////////////////////////////////////////////////////
readUnknown(WriterPlsParserInternal::ParagraphInfo const & info)1750 bool WriterPlsParser::readUnknown(WriterPlsParserInternal::ParagraphInfo const &info)
1751 {
1752 WriterPlsParserInternal::ParagraphData data;
1753 if (!readParagraphData(info, true, data))
1754 return false;
1755
1756 libmwaw::DebugStream f;
1757
1758 MWAWInputStreamPtr input = getInput();
1759 long pos = input->tell();
1760 f.str("");
1761 f << "Paragraph" << data.m_type << "(II):";
1762
1763 int numData = data.m_numData[1];
1764 for (int i = 0; i < numData; i++) {
1765 f << "data" << i << "=[";
1766 for (int j = 0; j < 8; j++) {
1767 auto val = static_cast<int>(input->readLong(2));
1768 if (!val) f << "_,";
1769 else f << val << ",";
1770 }
1771 f << "],";
1772 }
1773 if (long(input->tell()) != data.m_endPos) {
1774 ascii().addDelimiter(input->tell(), '|');
1775 input->seek(data.m_endPos, librevenge::RVNG_SEEK_SET);
1776 f << "#";
1777 }
1778
1779 ascii().addPos(pos);
1780 ascii().addNote(f.str().c_str());
1781
1782 ascii().addPos(input->tell());
1783 ascii().addNote("_");
1784
1785 return true;
1786 }
1787
1788 ////////////////////////////////////////////////////////////
1789 // read the beginning of a paragraph data
1790 ////////////////////////////////////////////////////////////
readParagraphData(WriterPlsParserInternal::ParagraphInfo const & info,bool hasFonts,WriterPlsParserInternal::ParagraphData & data)1791 bool WriterPlsParser::readParagraphData(WriterPlsParserInternal::ParagraphInfo const &info, bool hasFonts,
1792 WriterPlsParserInternal::ParagraphData &data)
1793 {
1794 libmwaw::DebugStream f;
1795
1796 MWAWInputStreamPtr input = getInput();
1797 long pos = info.m_pos;
1798 input->seek(pos, librevenge::RVNG_SEEK_SET);
1799
1800 data = WriterPlsParserInternal::ParagraphData();
1801 auto textLength = static_cast<int>(input->readLong(2));
1802 auto length2 = static_cast<int>(input->readLong(2));
1803 data.m_endPos = pos+4+textLength+length2;
1804
1805 if (textLength < 0 || length2 < 0 || !input->checkPosition(data.m_endPos)) {
1806 MWAW_DEBUG_MSG(("WriterPlsParser::readParagraphData: paragraph is too short\n"));
1807 return false;
1808 }
1809 if (textLength) {
1810 std::string &text = data.m_text;
1811 for (int i = 0; i < textLength; i++) {
1812 auto c = char(input->readULong(1));
1813 if (c == '\0') return false;
1814 text += c;
1815 }
1816 }
1817 auto type = static_cast<int>(input->readULong(2));
1818 data.m_type = (type & 7);
1819 data.m_typeFlag = (type & 0xFFF8);
1820
1821 f << "Entries(Paragraph" << data.m_type << "):";
1822
1823 // format type
1824 if (info.m_type != data.m_type + (data.m_typeFlag!=0 ? 8 : 0)) {
1825 MWAW_DEBUG_MSG(("WriterPlsParser::readParagraph: I find an unexpected type\n"));
1826 f << "#diffType=" << info.m_type << ",";
1827 }
1828
1829 data.m_height = static_cast<int>(input->readLong(2));
1830 data.m_indent[0] = static_cast<int>(input->readLong(2)); // left indent ?
1831 data.m_width = static_cast<int>(input->readLong(2));
1832 data.m_indent[1] = static_cast<int>(input->readLong(2)); // first pos indent ?
1833 data.m_unknown = static_cast<int>(input->readLong(2));
1834
1835 for (auto &numData : data.m_numData) numData = static_cast<int>(input->readLong(2));
1836
1837 auto &fonts = data.m_fonts;
1838 if (hasFonts) {
1839 long actPos = input->tell();
1840 if (data.m_numData[0]<0 || !input->checkPosition(actPos+data.m_numData[0]*16)) {
1841 MWAW_DEBUG_MSG(("WriterPlsParser::readParagraph: pb reading the number of fonts\n"));
1842 f << "###numFonts=" << data.m_numData[0] << ",";
1843 }
1844 else if (!readFonts(data.m_numData[0], data.m_type, fonts)) {
1845 MWAW_DEBUG_MSG(("WriterPlsParser::readParagraph: pb with the fonts\n"));
1846 input->seek(actPos+data.m_numData[0]*16, librevenge::RVNG_SEEK_SET);
1847 }
1848 }
1849
1850 f << data;
1851 for (size_t i = 0; i < fonts.size(); i++) {
1852 f << "font" << i << "=[";
1853 #ifdef DEBUG
1854 f << fonts[i].m_font.getDebugString(getFontConverter());
1855 #endif
1856 f << fonts[i] << "],";
1857 }
1858 ascii().addPos(pos);
1859 ascii().addNote(f.str().c_str());
1860
1861 return true;
1862 }
1863
1864 ////////////////////////////////////////////////////////////
1865 // read a series of fonts
1866 ////////////////////////////////////////////////////////////
readFonts(int nFonts,int type,std::vector<WriterPlsParserInternal::Font> & fonts)1867 bool WriterPlsParser::readFonts
1868 (int nFonts, int type, std::vector<WriterPlsParserInternal::Font> &fonts)
1869 {
1870 fonts.resize(0);
1871 MWAWInputStreamPtr input = getInput();
1872 bool hasFontExtra = true;
1873 switch (type) {
1874 case 0: // find in these case junk in the last part of font
1875 case 2:
1876 case 4:
1877 hasFontExtra = false;
1878 break;
1879 default:
1880 break;
1881 }
1882 int actPos = 0;
1883 libmwaw::DebugStream f;
1884 for (int i = 0; i < nFonts; i++) {
1885 if (!input->checkPosition(input->tell()+16)) {
1886 MWAW_DEBUG_MSG(("WriterPlsParser::readFonts: the zone seems too short\n"));
1887 break;
1888 }
1889 WriterPlsParserInternal::Font fInfo;
1890 f.str("");
1891 auto val = static_cast<int>(input->readLong(2)); // 65|315
1892 if (val) f << "dim?=" << val << ",";
1893 for (int j = 0; j < 3; j++) { // always 0: a color ?
1894 val = static_cast<int>(input->readLong(1));
1895 if (val) f << "f" << j << "=" << val << ",";
1896 }
1897 MWAWFont &font = fInfo.m_font;
1898 font.setId(static_cast<int>(input->readULong(1)));
1899 auto flag = static_cast<int>(input->readULong(1));
1900 uint32_t flags = 0;
1901 if (flag&0x1) flags |= MWAWFont::boldBit;
1902 if (flag&0x2) flags |= MWAWFont::italicBit;
1903 if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
1904 if (flag&0x8) flags |= MWAWFont::embossBit;
1905 if (flag&0x10) flags |= MWAWFont::shadowBit;
1906 if (flag&0x60)
1907 f << "#fl=" << std::hex << (flag&0x60) << std::dec << ",";
1908 if (flag&0x80) f << "fl80,"; // frequent, find on complete line,
1909
1910 flag= static_cast<int>(input->readULong(1));
1911 if (flag&2) font.set(MWAWFont::Script::super100());
1912 if (flag&4) font.set(MWAWFont::Script::sub100());
1913 if (flag&0x10) f << "flA10,";// also frequent, find on complete line
1914 if (flag&0xE9) f << "#flA=" << std::hex << (flag&0xE9) << std::dec << ",";
1915 font.setFlags(flags);
1916 val = static_cast<int>(input->readLong(1));// always 0
1917 if (val)
1918 f << "#g0=" << val << ",";
1919 font.setSize(float(input->readLong(1)));
1920 fInfo.m_firstChar = actPos;
1921 auto nChar = static_cast<int>(input->readULong(2));
1922 actPos += nChar;
1923 if (!hasFontExtra)
1924 input->seek(4, librevenge::RVNG_SEEK_CUR);
1925 else { // always 0
1926 for (int j = 0; j < 2; j++) {
1927 val = static_cast<int>(input->readLong(2));
1928 if (val) f << "g" << j+1 << "=" << val << ",";
1929 }
1930 }
1931 font.m_extra+=f.str();
1932 fonts.push_back(fInfo);
1933 }
1934
1935 return true;
1936 }
1937
1938 ////////////////////////////////////////////////////////////
1939 // read a series of lines
1940 ////////////////////////////////////////////////////////////
readLines(WriterPlsParserInternal::ParagraphInfo const &,int nLines,std::vector<WriterPlsParserInternal::Line> & lines)1941 bool WriterPlsParser::readLines
1942 (WriterPlsParserInternal::ParagraphInfo const &/*info*/,
1943 int nLines, std::vector<WriterPlsParserInternal::Line> &lines)
1944 {
1945 lines.resize(0);
1946 MWAWInputStreamPtr input = getInput();
1947
1948 int actPos = 0;
1949 for (int i = 0; i < nLines; i++) {
1950 WriterPlsParserInternal::Line lInfo;
1951 lInfo.m_height = static_cast<int>(input->readLong(2));
1952 lInfo.m_maxFontSize = static_cast<int>(input->readLong(2)); // checkMe
1953 lInfo.m_width = static_cast<int>(input->readLong(2));
1954 auto nChar = static_cast<int>(input->readLong(2));
1955 lInfo.m_firstChar = actPos;
1956 actPos += nChar;
1957 /*
1958 f0 always 0
1959 f1 almost always 0, if not 1
1960 f2 almost always 0, if not 2, 3, 4, c
1961 f3 almost always 0, if not 200, 400, 6465, 7600, dfc, e03, e04, e06 : junk?
1962 */
1963 for (auto &fl : lInfo.m_flags) fl = static_cast<int>(input->readLong(2));
1964 lines.push_back(lInfo);
1965 }
1966 return true;
1967 }
1968
1969 ////////////////////////////////////////////////////////////
1970 // read the print info
1971 ////////////////////////////////////////////////////////////
readPrintInfo()1972 bool WriterPlsParser::readPrintInfo()
1973 {
1974 MWAWInputStreamPtr input = getInput();
1975 long pos = input->tell();
1976 libmwaw::DebugStream f;
1977 // print info
1978 libmwaw::PrinterInfo info;
1979 if (!info.read(input)) return false;
1980 f << "Entries(PrintInfo):"<< info;
1981
1982 MWAWVec2i paperSize = info.paper().size();
1983 MWAWVec2i pageSize = info.page().size();
1984 if (pageSize.x() <= 0 || pageSize.y() <= 0 ||
1985 paperSize.x() <= 0 || paperSize.y() <= 0) return false;
1986
1987 // define margin from print info
1988 MWAWVec2i lTopMargin= -1 * info.paper().pos(0);
1989 MWAWVec2i rBotMargin=info.paper().size() - info.page().size();
1990
1991 // move margin left | top
1992 int decalX = lTopMargin.x() > 14 ? lTopMargin.x()-14 : 0;
1993 int decalY = lTopMargin.y() > 14 ? lTopMargin.y()-14 : 0;
1994 lTopMargin -= MWAWVec2i(decalX, decalY);
1995 rBotMargin += MWAWVec2i(decalX, decalY);
1996
1997 // decrease right | bottom
1998 int rightMarg = rBotMargin.x() -50;
1999 if (rightMarg < 0) rightMarg=0;
2000 int botMarg = rBotMargin.y() -50;
2001 if (botMarg < 0) botMarg=0;
2002
2003 getPageSpan().setMarginTop(lTopMargin.y()/72.0);
2004 getPageSpan().setMarginBottom(botMarg/72.0);
2005 getPageSpan().setMarginLeft(lTopMargin.x()/72.0);
2006 getPageSpan().setMarginRight(rightMarg/72.0);
2007 getPageSpan().setFormLength(paperSize.y()/72.);
2008 getPageSpan().setFormWidth(paperSize.x()/72.);
2009
2010 ascii().addPos(pos);
2011 ascii().addNote(f.str().c_str());
2012
2013 input->seek(pos+0x78, librevenge::RVNG_SEEK_SET);
2014 if (long(input->tell()) != pos+0x78) {
2015 MWAW_DEBUG_MSG(("WriterPlsParser::readPrintInfo: file is too short\n"));
2016 return false;
2017 }
2018
2019 return true;
2020 }
2021
2022 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
2023