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