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 <iomanip>
35 #include <iostream>
36 #include <limits>
37 #include <set>
38 #include <sstream>
39 
40 #include <librevenge/librevenge.h>
41 
42 #include "MWAWFont.hxx"
43 #include "MWAWFontConverter.hxx"
44 #include "MWAWGraphicStyle.hxx"
45 #include "MWAWHeader.hxx"
46 #include "MWAWParagraph.hxx"
47 #include "MWAWPosition.hxx"
48 #include "MWAWPrinter.hxx"
49 #include "MWAWRSRCParser.hxx"
50 #include "MWAWSection.hxx"
51 #include "MWAWSpreadsheetListener.hxx"
52 #include "MWAWSubDocument.hxx"
53 
54 #include "GreatWksDocument.hxx"
55 #include "GreatWksGraph.hxx"
56 #include "GreatWksText.hxx"
57 
58 #include "GreatWksSSParser.hxx"
59 
60 /** Internal: the structures of a GreatWksSSParser */
61 namespace GreatWksSSParserInternal
62 {
63 /**a style of a GreatWksSSParser */
64 class Style
65 {
66 public:
67   /** constructor */
Style()68   Style()
69     : m_font(3,10)
70     , m_backgroundColor(MWAWColor::white())
71   {
72   }
73   /** the font style */
74   MWAWFont m_font;
75   /** the cell background color */
76   MWAWColor m_backgroundColor;
77 };
78 
79 /** a cell of a GreatWksSSParser */
80 class Cell final : public MWAWCell
81 {
82 public:
83   /// constructor
Cell()84   Cell() : m_content(), m_style(-1) { }
85   Cell(Cell const &)=default;
86   Cell &operator=(Cell &&)=default;
87   Cell &operator=(Cell const &)=default;
88   /// destructor
89   ~Cell() final;
90   //! returns true if the cell do contain any content
isEmpty() const91   bool isEmpty() const
92   {
93     return m_content.empty() && !hasBorders();
94   }
95 
96   //! the cell content
97   MWAWCellContent m_content;
98   /** the cell style */
99   int m_style;
100 };
101 
~Cell()102 Cell::~Cell()
103 {
104 }
105 
106 /** the spreadsheet of a of a MsWksSSParser */
107 class Spreadsheet
108 {
109 public:
110   //! constructor
Spreadsheet()111   Spreadsheet()
112     : m_widthDefault(75)
113     , m_widthCols()
114     , m_heightDefault(13)
115     , m_heightRows()
116     , m_cells()
117     , m_name("Sheet0")
118   {
119   }
120   //! returns the row size in point
getRowHeight(int row) const121   int getRowHeight(int row) const
122   {
123     if (row>=0&&row<static_cast<int>(m_heightRows.size()))
124       return m_heightRows[size_t(row)];
125     return m_heightDefault;
126   }
127   //! returns the height of a row in point and updated repeated row
getRowHeight(int row,int & numRepeated) const128   double getRowHeight(int row, int &numRepeated) const
129   {
130     double res=getRowHeight(row);
131     numRepeated=1;
132     if (row<0 || row>=static_cast<int>(m_heightRows.size()))
133       numRepeated=1000;
134     else {
135       for (auto r=size_t(row+1); r<m_heightRows.size(); ++r) {
136         double nextH=getRowHeight(row+1);
137         if (nextH<res || nextH>res)
138           break;
139         ++numRepeated;
140       }
141     }
142     return res;
143   }
144   //! convert the m_widthCols in a vector of of point size
convertInPoint(std::vector<int> const & list) const145   std::vector<float> convertInPoint(std::vector<int> const &list) const
146   {
147     auto numCols=size_t(getRightBottomPosition()[0]+1);
148     std::vector<float> res;
149     res.resize(numCols);
150     for (size_t i = 0; i < numCols; i++) {
151       if (i>=list.size() || list[i] < 0) res[i] = float(m_widthDefault);
152       else res[i] = float(list[i]);
153     }
154     return res;
155   }
156   /** the default column width */
157   int m_widthDefault;
158   /** the column size in points */
159   std::vector<int> m_widthCols;
160   /** the default row height */
161   int m_heightDefault;
162   /** the row height in points */
163   std::vector<int> m_heightRows;
164   /** the list of not empty cells */
165   std::vector<Cell> m_cells;
166   /** the spreadsheet name */
167   std::string m_name;
168 protected:
169   /** returns the last Right Bottom cell position */
getRightBottomPosition() const170   MWAWVec2i getRightBottomPosition() const
171   {
172     int maxX = 0, maxY = 0;
173     for (auto const &cell : m_cells) {
174       MWAWVec2i const &p = cell.position();
175       if (p[0] > maxX) maxX = p[0];
176       if (p[1] > maxY) maxY = p[1];
177     }
178     return MWAWVec2i(maxX, maxY);
179   }
180 };
181 
182 ////////////////////////////////////////
183 //! Internal: the state of a GreatWksSSParser
184 struct State {
185   //! constructor
StateGreatWksSSParserInternal::State186   State()
187     : m_spreadsheet()
188     , m_styleList()
189     , m_actPage(0)
190     , m_numPages(0)
191     , m_headerEntry()
192     , m_footerEntry()
193     , m_headerPrint(false)
194     , m_footerPrint(false)
195     , m_headerHeight(0)
196     , m_footerHeight(0)
197   {
198   }
199   //! returns the style corresponding to an id
getStyleGreatWksSSParserInternal::State200   Style getStyle(int id) const
201   {
202     if (id<0 || id>=int(m_styleList.size())) {
203       MWAW_DEBUG_MSG(("GreatWksSSParserInternal::State: can not find the style %d\n", id));
204       return Style();
205     }
206     return m_styleList[size_t(id)];
207   }
208   /** the spreadsheet */
209   Spreadsheet m_spreadsheet;
210   /** the list of style */
211   std::vector<Style> m_styleList;
212   int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
213 
214   MWAWEntry m_headerEntry /** the header entry (in v2)*/, m_footerEntry/**the footer entry (in v2)*/;
215   bool m_headerPrint /** the header is printed */, m_footerPrint /* the footer is printed */;
216   int m_headerHeight /** the header height if known */,
217       m_footerHeight /** the footer height if known */;
218 };
219 
220 ////////////////////////////////////////
221 //! Internal: the subdocument of a GreatWksSSParser
222 class SubDocument final : public MWAWSubDocument
223 {
224 public:
SubDocument(GreatWksSSParser & pars,MWAWInputStreamPtr const & input,int zoneId)225   SubDocument(GreatWksSSParser &pars, MWAWInputStreamPtr const &input, int zoneId) :
226     MWAWSubDocument(&pars, input, MWAWEntry()), m_id(zoneId) {}
227 
228   //! destructor
~SubDocument()229   ~SubDocument() final {}
230 
231   //! operator!=
232   bool operator!=(MWAWSubDocument const &doc) const final;
233 
234   //! the parser function
235   void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
236 
237 protected:
238   //! the subdocument id
239   int m_id;
240 };
241 
parse(MWAWListenerPtr & listener,libmwaw::SubDocumentType type)242 void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type)
243 {
244   if (!listener.get()) {
245     MWAW_DEBUG_MSG(("GreatWksSSParserInternal::SubDocument::parse: no listener\n"));
246     return;
247   }
248   if (type!=libmwaw::DOC_HEADER_FOOTER) {
249     MWAW_DEBUG_MSG(("GreatWksSSParserInternal::SubDocument::parse: unknown type\n"));
250     return;
251   }
252   auto *parser=dynamic_cast<GreatWksSSParser *>(m_parser);
253   if (!parser) {
254     MWAW_DEBUG_MSG(("GreatWksSSParserInternal::SubDocument::parse: no parser\n"));
255     return;
256   }
257   long pos = m_input->tell();
258   parser->sendHF(m_id);
259   m_input->seek(pos, librevenge::RVNG_SEEK_SET);
260 }
261 
operator !=(MWAWSubDocument const & doc) const262 bool SubDocument::operator!=(MWAWSubDocument const &doc) const
263 {
264   if (MWAWSubDocument::operator!=(doc)) return true;
265   auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
266   if (!sDoc) return true;
267   if (m_id != sDoc->m_id) return true;
268   return false;
269 }
270 }
271 
272 ////////////////////////////////////////////////////////////
273 // constructor/destructor, ...
274 ////////////////////////////////////////////////////////////
GreatWksSSParser(MWAWInputStreamPtr const & input,MWAWRSRCParserPtr const & rsrcParser,MWAWHeader * header)275 GreatWksSSParser::GreatWksSSParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
276   : MWAWSpreadsheetParser(input, rsrcParser, header)
277   , m_state()
278   , m_document()
279 {
280   init();
281 }
282 
~GreatWksSSParser()283 GreatWksSSParser::~GreatWksSSParser()
284 {
285 }
286 
init()287 void GreatWksSSParser::init()
288 {
289   resetSpreadsheetListener();
290   setAsciiName("main-1");
291 
292   m_state.reset(new GreatWksSSParserInternal::State);
293 
294   // reduce the margin (in case, the page is not defined)
295   getPageSpan().setMargins(0.1);
296 
297   m_document.reset(new GreatWksDocument(*this));
298 }
299 
300 ////////////////////////////////////////////////////////////
301 // interface with the text parser
302 ////////////////////////////////////////////////////////////
sendHF(int id)303 bool GreatWksSSParser::sendHF(int id)
304 {
305   return m_document->getTextParser()->sendTextbox(id==0 ? m_state->m_headerEntry : m_state->m_footerEntry);
306 }
307 
308 ////////////////////////////////////////////////////////////
309 // the parser
310 ////////////////////////////////////////////////////////////
parse(librevenge::RVNGSpreadsheetInterface * docInterface)311 void GreatWksSSParser::parse(librevenge::RVNGSpreadsheetInterface *docInterface)
312 {
313   if (!getInput().get() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
314   bool ok = false;
315   try {
316     // create the asciiFile
317     ascii().setStream(getInput());
318     ascii().open(asciiName());
319 
320     checkHeader(nullptr);
321     ok = createZones();
322     if (ok) {
323       createDocument(docInterface);
324       sendSpreadsheet();
325     }
326     ascii().reset();
327   }
328   catch (...) {
329     MWAW_DEBUG_MSG(("GreatWksSSParser::parse: exception catched when parsing\n"));
330     ok = false;
331   }
332 
333   resetSpreadsheetListener();
334   if (!ok) throw(libmwaw::ParseException());
335 }
336 
337 ////////////////////////////////////////////////////////////
338 // create the document
339 ////////////////////////////////////////////////////////////
createDocument(librevenge::RVNGSpreadsheetInterface * documentInterface)340 void GreatWksSSParser::createDocument(librevenge::RVNGSpreadsheetInterface *documentInterface)
341 {
342   if (!documentInterface) return;
343   if (getSpreadsheetListener()) {
344     MWAW_DEBUG_MSG(("GreatWksSSParser::createDocument: listener already exist\n"));
345     return;
346   }
347 
348   // update the page
349   m_state->m_actPage = 0;
350 
351   // create the page list
352   int numPages = 1;
353   if (m_document->getGraphParser()->numPages() > numPages)
354     numPages = m_document->getGraphParser()->numPages();
355   if (m_document->getTextParser()->numPages() > numPages)
356     numPages = m_document->getTextParser()->numPages();
357   m_state->m_numPages = numPages;
358 
359   MWAWPageSpan ps(getPageSpan());
360   std::vector<MWAWPageSpan> pageList;
361   if (m_state->m_headerEntry.valid()) {
362     MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
363     header.m_subDocument.reset(new GreatWksSSParserInternal::SubDocument(*this, getInput(), 0));
364     ps.setHeaderFooter(header);
365   }
366   if (m_state->m_footerEntry.valid()) {
367     MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
368     footer.m_subDocument.reset(new GreatWksSSParserInternal::SubDocument(*this, getInput(), 1));
369     ps.setHeaderFooter(footer);
370   }
371   ps.setPageSpan(numPages);
372   pageList.push_back(ps);
373   MWAWSpreadsheetListenerPtr listen(new MWAWSpreadsheetListener(*getParserState(), pageList, documentInterface));
374   setSpreadsheetListener(listen);
375   listen->startDocument();
376 }
377 
378 ////////////////////////////////////////////////////////////
379 //
380 // Intermediate level
381 //
382 ////////////////////////////////////////////////////////////
createZones()383 bool GreatWksSSParser::createZones()
384 {
385   m_document->readRSRCZones();
386 
387   MWAWInputStreamPtr input = getInput();
388   long pos=16;
389   input->seek(pos, librevenge::RVNG_SEEK_SET);
390   if (!readSpreadsheet())
391     return false;
392 
393   if (!input->isEnd()) {
394     pos = input->tell();
395     MWAW_DEBUG_MSG(("GreatWksSSParser::createZones: find some extra data\n"));
396     ascii().addPos(pos);
397     ascii().addNote("Entries(Loose):");
398     ascii().addPos(pos+100);
399     ascii().addNote("_");
400   }
401 
402   return true;
403 }
404 
405 ////////////////////////////////////////////////////////////
406 //
407 // Low level
408 //
409 ////////////////////////////////////////////////////////////
410 
411 ////////////////////////////////////////////////////////////
412 // read a spreadsheet
413 ////////////////////////////////////////////////////////////
readSpreadsheet()414 bool GreatWksSSParser::readSpreadsheet()
415 {
416   MWAWInputStreamPtr input = getInput();
417   int const vers=version();
418   libmwaw::DebugStream f;
419 
420   while (!input->isEnd()) {
421     bool ok=true, printDone=false;
422     long pos = input->tell();
423     f.str("");
424     auto type=static_cast<int>(input->readLong(2));
425     if (type==0x10) {
426       if (vers==1)
427         f << "Entries(DocInfo):";
428       else // some patterns?
429         f << "Entries(Zone10):";
430     }
431     else if (type>=0 && type < 0x1a) {
432       static char const *wh[0x1a] = {
433         "_", "FontNames", "Style", "Column", "Column", "Row", "Zone6", "Row",
434         "Column", "Zone9", "Zonea", "Zoneb", "Row", "Screen", "DocOptions", "Selection",
435         "DocInfo", "CalcMode", "Zone12", "Zone13", "Zone14", "GridOptions", "DocInfo", "Header",
436         "Footer", "Chart"
437       };
438       f << "Entries(" << wh[type] << "):";
439     }
440     else if (type==0x1c)
441       f << "Entries(Graphic):";
442     else
443       f << "Entries(Zone" << std::hex << type << std::dec << "):";
444     long endPos=0;
445     switch (type) {
446     case 1:
447       if (!m_document->getTextParser()->readFontNames()) {
448         ok=false;
449         break;
450       }
451       printDone = true;
452       endPos=input->tell();
453       break;
454     case 2:
455       if (!readStyles()) {
456         ok=false;
457         break;
458       }
459       printDone = true;
460       endPos=input->tell();
461       break;
462     case 3: // column
463     case 5: { // row :
464       auto sz=long(input->readULong(2));
465       endPos=pos+4+long(sz);
466       ok = (sz>=4 && input->checkPosition(endPos));
467       if (!ok) break;
468       auto id=static_cast<int>(input->readLong(2));
469       auto length=static_cast<int>(input->readLong(2));
470       if (id<1 || length < 0) {
471         MWAW_DEBUG_MSG(("GreatWksSSParser::readSpreadsheet: the row/col definition seems bad\n"));
472         f << "###";
473       }
474       else {
475         std::vector<int> &lengths=type==3 ? m_state->m_spreadsheet.m_widthCols : m_state->m_spreadsheet.m_heightRows;
476         if (id-1 >= static_cast<int>(lengths.size()))
477           lengths.resize(size_t(id), -1);
478         lengths[size_t(id-1)]=length;
479       }
480       f << "pos=" << id << ",";
481       f << "size=" << length << ",";
482       // readULong(1)&1 -> page break?
483       break;
484     }
485     case 4:
486       f << "end";
487       endPos=pos+2;
488       break;
489     case 6:
490       endPos=pos+10;
491       if (!input->checkPosition(endPos)) {
492         ok=false;
493         break;
494       }
495       for (int i=0; i < 4; ++i) {
496         auto val=static_cast<int>(input->readLong(2));
497         static int const expected[]= {9,0,2,0x2e};
498         if (val!=expected[i])
499           f << "f" << i << "=" << val << ",";
500       }
501       break;
502     // case 0x10: dim ? + string + select cell...
503 
504     case 7: // d default row size
505     case 8: // 4b default column size
506     case 0xb: // 0|4
507     case 0xc: // d
508     case 0x11: // 0
509     case 0x1a: { // 1
510       auto sz=long(input->readULong(4));
511       endPos=pos+6+sz;
512       if (!input->checkPosition(endPos)) {
513         ok=false;
514         break;
515       }
516       if (sz!=2) {
517         f << "###";
518         MWAW_DEBUG_MSG(("GreatWksSSParser::readSpreadsheet: size of zone %d seems odd\n", type));
519         break;
520       }
521       auto val=static_cast<int>(input->readULong(2));
522       switch (type) {
523       case 7:
524         m_state->m_spreadsheet.m_heightDefault=val;
525         f << "height[def]=" << val << ",";
526         break;
527       case 8:
528         m_state->m_spreadsheet.m_widthDefault=val;
529         f << "width[def]=" << val << ",";
530         break;
531       case 0xc: // similar to 7 related to printing?
532         f << "height[def2]=" << val << ",";
533         break;
534       case 0x11:
535         if (val==1) f << "manual,";
536         else if (val) f << "##val=" << val << ",";
537         break;
538       default:
539         f << "f0=" << val << ",";
540         break;
541       }
542       break;
543     }
544     case 0xd: {
545       auto sz=long(input->readULong(4));
546       endPos=pos+6+sz;
547       if (!input->checkPosition(endPos)) {
548         ok=false;
549         break;
550       }
551       if ((vers==1 && sz!=0x8) || (vers==2 && sz!=0x18)) {
552         f << "###";
553         MWAW_DEBUG_MSG(("GreatWksSSParser::readSpreadsheet: size of screen zone seems odd\n"));
554         break;
555       }
556       int dim[4];
557       for (auto &d : dim) d=static_cast<int>(input->readLong(2));
558       f << dim[0] << "x" << dim[1] << "<->" << dim[2] << "x" << dim[3] << ",";
559       if (vers==1) break;
560       for (auto &d : dim) d=static_cast<int>(input->readLong(2));
561       f << "screen2?=" << dim[0] << "x" << dim[1] << "<->" << dim[1] << "x" << dim[2] << ",";
562       // now find 00000003cccc3333
563       break;
564     }
565     case 0xe: {
566       auto sz=long(input->readULong(4));
567       endPos=pos+6+sz;
568       if (!input->checkPosition(endPos)) {
569         ok=false;
570         break;
571       }
572       if (sz!=0xa) {
573         f << "###";
574         MWAW_DEBUG_MSG(("GreatWksSSParser::readSpreadsheet: size of selection zone seems odd\n"));
575         break;
576       }
577       int val;
578       for (int i=0; i<9; ++i) {
579         val=static_cast<int>(input->readULong(1));
580         if (!val) continue;
581         // checkme: same value in v2 ?
582         static char const *what[]= {"grid[display]", "headerRC[display]", "zero[doc]", "formula[doc]", "grid[print]", "headerRC[print]", "header[print]", "footer[print]", "protected" };
583         if (val==1) {
584           if (i==6)
585             m_state->m_headerPrint=true;
586           else if (i==7)
587             m_state->m_footerPrint=true;
588           f << what[i] << ",";
589         }
590         else
591           f << "###" << what[i] << "=" << val << ",";
592       }
593       val=static_cast<int>(input->readULong(1));
594       if (val!=1) f << "first[page]=" << val << ",";
595       break;
596     }
597     case 0xf: {
598       auto sz=long(input->readULong(4));
599       endPos=pos+6+sz;
600       if (sz<0 || !input->checkPosition(endPos)) {
601         ok=false;
602         break;
603       }
604       if (sz!=0xe) {
605         f << "###";
606         MWAW_DEBUG_MSG(("GreatWksSSParser::readSpreadsheet: size of selection zone seems odd\n"));
607         break;
608       }
609       auto val=static_cast<int>(input->readULong(2));
610       if (val != 4) f << "f0=" << val << ",";
611       f << "cells=[";
612       for (int i=0; i<3; ++i) { // probably selected cell, followed by a cell range
613         f << input->readLong(2);
614         f << "x" << input->readLong(2) << ",";
615       }
616       f << "],";
617       break;
618     }
619     case 0x10: {
620       auto sz=long(input->readULong(4));
621       endPos=pos+6+sz;
622       if (!input->checkPosition(endPos)) {
623         ok=false;
624         break;
625       }
626       if (vers==2) break;
627       if (sz!=0x12) {
628         f << "###";
629         MWAW_DEBUG_MSG(("GreatWksSSParser::readSpreadsheet: size of docinfo zone seems odd\n"));
630         break;
631       }
632       int dim[4];
633       for (auto &d : dim) d=static_cast<int>(input->readLong(2));
634       f << "page=" << dim[0] << "x" << dim[1] << "<->" << dim[2] << "x" << dim[3] << ",";
635       if (input->readLong(1)!=1) break;
636       input->seek(1,librevenge::RVNG_SEEK_CUR);
637       for (auto &d : dim) d=static_cast<int>(input->readLong(2));
638       f << "select=" << dim[0] << "x" << dim[1] << "<->" << dim[2] << "x" << dim[3] << ",";
639       break;
640     }
641     case 0x13: {
642       GreatWksSSParserInternal::Cell cell;
643       if (!readCell(cell)) {
644         ok=false;
645         break;
646       }
647       if (!cell.isEmpty() || !m_state->getStyle(cell.m_style).m_backgroundColor.isWhite())
648         m_state->m_spreadsheet.m_cells.push_back(cell);
649       printDone = true;
650       endPos=input->tell();
651       break;
652     }
653     case 0x14:
654       ascii().addPos(pos);
655       ascii().addNote("_");
656       return true;
657     case 0x15: {
658       auto sz=long(input->readULong(4));
659       endPos=pos+6+sz;
660       if (!input->checkPosition(endPos)) {
661         ok=false;
662         break;
663       }
664       if (sz!=0xa || vers!=2) {
665         f << "###";
666         MWAW_DEBUG_MSG(("GreatWksSSParser::readSpreadsheet: size of grid option zone seems odd\n"));
667         break;
668       }
669       auto val=static_cast<int>(input->readLong(2));
670       if (val!=1) f << "unit=" << val << ",";
671       val=static_cast<int>(input->readLong(2));
672       if (val!=5) f << "subdiv=" << val << ",";
673       break;
674     }
675     case 0x16: {
676       auto sz=long(input->readULong(4));
677       endPos=pos+6+sz;
678       if (!input->checkPosition(endPos)) {
679         ok=false;
680         break;
681       }
682       if (sz!=0x30 || vers!=2) {
683         f << "###";
684         MWAW_DEBUG_MSG(("GreatWksSSParser::readSpreadsheet: size of doc info zone seems odd\n"));
685         break;
686       }
687       f << "margins=[";
688       for (int j=0; j<4; ++j)
689         f << double(input->readLong(4))/65536. << ",";
690       f << "],";
691       for (int c=0; c<2; ++c) {
692         // two times the same values: related to header/footer ?
693         f << "unkn" << c << "=[";
694         for (int j=0; j<8; ++j) {
695           auto val=static_cast<int>(input->readLong(2));
696           if (val)
697             f << val << ",";
698           else
699             f << "_,";
700         }
701         f << "],";
702       }
703       break;
704     }
705     case 0x17:
706     case 0x18: {
707       auto sz=long(input->readULong(4));
708       endPos=pos+6+sz;
709       if (!input->checkPosition(endPos)) {
710         ok=false;
711         break;
712       }
713       MWAWEntry entry;
714       entry.setBegin(pos+6);
715       entry.setEnd(endPos);
716       if (type==0x17)
717         m_state->m_headerEntry=entry;
718       else
719         m_state->m_footerEntry=entry;
720       break;
721     }
722     case 0x19:
723       if (!readChart()) {
724         ok=false;
725         break;
726       }
727       printDone = true;
728       endPos=input->tell();
729       break;
730     case 0x1c:
731       input->seek(-2, librevenge::RVNG_SEEK_CUR);
732       if (!m_document->getGraphParser()->readPageFrames()) {
733         ok=false;
734         break;
735       }
736       printDone = true;
737       endPos=input->tell();
738       break;
739     default: {
740       endPos=pos+6+long(input->readULong(4));
741       if (type <= 0 || !input->checkPosition(endPos)) {
742         ok=false;
743         break;
744       }
745       break;
746     }
747     }
748     if (!ok && type>=7) {
749       // try to use the default value
750       input->seek(pos+2, librevenge::RVNG_SEEK_SET);
751       auto sz= long(input->readULong(4));
752       endPos=pos+6+sz;
753       ok=input->checkPosition(endPos);
754     }
755     if (!ok) {
756       input->seek(pos, librevenge::RVNG_SEEK_SET);
757       break;
758     }
759     if (input->tell()!=endPos)
760       ascii().addDelimiter(input->tell(),'|');
761     input->seek(endPos, librevenge::RVNG_SEEK_SET);
762     if (printDone)
763       continue;
764     ascii().addPos(pos);
765     ascii().addNote(f.str().c_str());
766   }
767   return true;
768 }
769 
770 ////////////////////////////////////////////////////////////
771 ////////////////////////////////////////////////////////////
sendSpreadsheet()772 bool GreatWksSSParser::sendSpreadsheet()
773 {
774   MWAWSpreadsheetListenerPtr listener=getSpreadsheetListener();
775   MWAWInputStreamPtr &input= getInput();
776   if (!listener) {
777     MWAW_DEBUG_MSG(("GreatWksSSParser::sendSpreadsheet: I can not find the listener\n"));
778     return false;
779   }
780   auto &sheet = m_state->m_spreadsheet;
781 
782   listener->openSheet(sheet.convertInPoint(sheet.m_widthCols), librevenge::RVNG_POINT, std::vector<int>(), sheet.m_name);
783   m_document->getGraphParser()->sendPageGraphics();
784 
785   int prevRow = -1;
786   for (auto cell : sheet.m_cells) {
787     if (cell.position()[1]>prevRow+1) {
788       while (cell.position()[1] > prevRow+1) {
789         if (prevRow != -1) listener->closeSheetRow();
790         int numRepeat;
791         double h=sheet.getRowHeight(prevRow+1, numRepeat);
792         if (cell.position()[1]<prevRow+1+numRepeat)
793           numRepeat=cell.position()[1]-1-prevRow;
794         listener->openSheetRow(float(h), librevenge::RVNG_POINT, numRepeat);
795         prevRow+=numRepeat;
796       }
797     }
798     if (cell.position()[1] > prevRow) {
799       if (prevRow != -1) listener->closeSheetRow();
800       listener->openSheetRow(float(sheet.getRowHeight(++prevRow)), librevenge::RVNG_POINT);
801     }
802 
803     auto style=m_state->getStyle(cell.m_style);
804     cell.setFont(style.m_font);
805     if (!style.m_backgroundColor.isWhite())
806       cell.setBackgroundColor(style.m_backgroundColor);
807     listener->openSheetCell(cell, cell.m_content);
808     if (cell.m_content.m_textEntry.valid()) {
809       listener->setFont(style.m_font);
810       input->seek(cell.m_content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
811       while (!input->isEnd() && input->tell()<cell.m_content.m_textEntry.end()) {
812         auto c=static_cast<unsigned char>(input->readULong(1));
813         if (c==0xd)
814           listener->insertEOL();
815         else
816           listener->insertCharacter(c);
817       }
818     }
819     listener->closeSheetCell();
820   }
821   if (prevRow!=-1) listener->closeSheetRow();
822   listener->closeSheet();
823   return true;
824 }
825 
826 ////////////////////////////////////////////////////////////
827 // read the chart zone
828 ////////////////////////////////////////////////////////////
readChart()829 bool GreatWksSSParser::readChart()
830 {
831   MWAWInputStreamPtr input = getInput();
832   long pos = input->tell();
833   libmwaw::DebugStream f;
834 
835   f << "Entries(Chart):";
836   auto sz=long(input->readULong(4));
837   long endPos=pos+4+sz;
838   if (sz < 4 || !input->checkPosition(endPos)) {
839     MWAW_DEBUG_MSG(("GreatWksSSParser::readChart: can not find the chart zone size\n"));
840     f << "###";
841     ascii().addPos(pos-2);
842     ascii().addNote(f.str().c_str());
843     return false;
844   }
845   auto N=static_cast<int>(input->readULong(2));
846   auto fSz=static_cast<int>(input->readULong(2));
847   if (N) f << "N=" << N << ",";
848   if ((N && fSz!=0x14) || long(4+N*fSz)!=sz) {
849     MWAW_DEBUG_MSG(("GreatWksSSParser::readChart: can not find the number of chart\n"));
850     f << "###";
851     ascii().addPos(pos-2);
852     ascii().addNote(f.str().c_str());
853     input->seek(endPos, librevenge::RVNG_SEEK_SET);
854     return true;
855   }
856   ascii().addPos(pos-2);
857   ascii().addNote(f.str().c_str());
858   for (int i=0; i<N; ++i) {
859     pos=input->tell();
860     f.str("");
861     f << "Chart-" << i << ":";
862     ascii().addPos(pos);
863     ascii().addNote(f.str().c_str());
864     input->seek(pos+20, librevenge::RVNG_SEEK_SET);
865   }
866   input->seek(endPos, librevenge::RVNG_SEEK_SET);
867   return true;
868 }
869 
870 ////////////////////////////////////////////////////////////
871 // read a cell
872 ////////////////////////////////////////////////////////////
readCell(GreatWksSSParserInternal::Cell & cell)873 bool GreatWksSSParser::readCell(GreatWksSSParserInternal::Cell &cell)
874 {
875   cell=GreatWksSSParserInternal::Cell();
876 
877   MWAWInputStreamPtr input = getInput();
878   long pos = input->tell();
879   libmwaw::DebugStream f;
880 
881   f << "Entries(Cell):";
882   auto sz=long(input->readULong(4));
883   long endPos=pos+4+sz;
884   if (sz < 0x12 || !input->checkPosition(endPos)) {
885     MWAW_DEBUG_MSG(("GreatWksSSParser::readCell: can not find a cell\n"));
886     f << "###";
887     ascii().addPos(pos-2);
888     ascii().addNote(f.str().c_str());
889     return false;
890   }
891   int cPos[2];
892   for (auto &c : cPos) c=static_cast<int>(input->readLong(2));
893   MWAWVec2i cellPos(cPos[0]-1,cPos[1]-1);
894   cell.setPosition(cellPos);
895   f << "cell=" << cellPos << ",";
896   cell.m_style=static_cast<int>(input->readLong(2));
897   f << "style" << cell.m_style << ",";
898   auto val=static_cast<int>(input->readULong(2));
899   int form=(val>>8)&0x1f;
900   MWAWCell::Format format;
901   format.m_format=MWAWCell::F_TEXT;
902   switch (form) {
903   case 0:
904     format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC;
905     break;
906   case 1:
907     format.m_format=MWAWCell::F_NUMBER;
908     format.m_numberFormat=MWAWCell::F_NUMBER_CURRENCY;
909     f << "currency,";
910     break;
911   case 2:
912     format.m_format=MWAWCell::F_NUMBER;
913     format.m_numberFormat=MWAWCell::F_NUMBER_PERCENT;
914     f << "percent,";
915     break;
916   case 3:
917     format.m_format=MWAWCell::F_NUMBER;
918     format.m_numberFormat=MWAWCell::F_NUMBER_DECIMAL;
919     f << "fixed,";
920     break;
921   case 4:
922     format.m_format=MWAWCell::F_NUMBER;
923     format.m_numberFormat=MWAWCell::F_NUMBER_SCIENTIFIC;
924     f << "scientific,";
925     break;
926   case 5:
927     format.m_format=MWAWCell::F_BOOLEAN;
928     f << "bool,";
929     break;
930   case 10:
931   case 11:
932   case 12:
933   case 13:
934   case 14:
935   case 15:
936   case 16:
937   case 17:
938   case 18: {
939     static char const *wh[] = {
940       "%m/%d/%y", "%b %d, %Y", "%b %Y", "%b %d", "%B %d, %Y", "%B %Y", "%B %d", "%a, %b %d, %Y", "%A, %B %d, %Y"
941     };
942     format.m_format=MWAWCell::F_DATE;
943     format.m_DTFormat=wh[form-10];
944     f << wh[form-10] << ",";
945     break;
946   }
947   case 20:
948   case 21:
949   case 22:
950   case 23: {
951     static char const *wh[] = {
952       "%H:%M", "%H:%M:%S", "%I:%M %p", "%I:%M:%S %p"
953     };
954     format.m_format=MWAWCell::F_TIME;
955     format.m_DTFormat=wh[form-20];
956     f << wh[form-20] << ",";
957     break;
958   }
959   default:
960     f << "#format=" <<form << ",";
961     break;
962   }
963   format.m_digits=(val & 0xF);
964   if ((val & 0xF)!=2)
965     f << "decimal=" << format.m_digits << ",";
966   if (val & 0x4000) f << "parenthesis,";
967   if (val & 0x8000) f << "commas,";
968   val &=0x20F0;
969   if (val)
970     f << "fl0=" << std::hex << val << std::dec << ",";
971   val=static_cast<int>(input->readULong(2));
972   if (val&0xf) {
973     f << "bord=";
974     int wh=0;
975     if (val&1) {
976       wh|=libmwaw::TopBit;
977       f << "T";
978     }
979     if (val&2) {
980       wh|=libmwaw::LeftBit;
981       f << "L";
982     }
983     if (val&4) {
984       wh|=libmwaw::BottomBit;
985       f << "B";
986     }
987     if (val&8) {
988       wh|=libmwaw::RightBit;
989       f << "R";
990     }
991     f << ",";
992     cell.setBorders(wh, MWAWBorder());
993   }
994   switch ((val>>8)&0x7) {
995   case 0: // none
996     break;
997   case 1:
998     cell.setHAlignment(MWAWCell::HALIGN_LEFT);
999     f << "align=left,";
1000     break;
1001   case 2:
1002     cell.setHAlignment(MWAWCell::HALIGN_CENTER);
1003     f << "align=center,";
1004     break;
1005   case 3:
1006     cell.setHAlignment(MWAWCell::HALIGN_RIGHT);
1007     f << "align=right,";
1008     break;
1009   case 4:
1010     f << "align=repeat,";
1011     break;
1012   default:
1013     f << "#align=" << ((val>>8)&0x7) << ",";
1014   }
1015   if (val&0x8000) {
1016     cell.setProtected(true);
1017     f << "protected,";
1018   }
1019   val &= 0x78F0;
1020   if (val)
1021     f << "fl1=" << std::hex << val << std::dec << ",";
1022   val=static_cast<int>(input->readULong(2)); // small number between 0 and 255
1023   if (val)
1024     f << "f0=" << val << ",";
1025   auto formSz=static_cast<int>(input->readULong(2));
1026   if (formSz&0x8000) { // appear if now, rand, ... is used
1027     f << "formula[update],";
1028     formSz &= 0x7FFF;
1029   }
1030   bool ok=true;
1031   MWAWCellContent &content=cell.m_content;
1032   content.m_contentType=MWAWCellContent::C_NONE;
1033   if (formSz) {
1034     long formulaPos=input->tell();
1035     f << "formula,";
1036     if (formulaPos+formSz+4>=endPos) {
1037       MWAW_DEBUG_MSG(("GreatWksSSParser::readCell: formula size seems bad\n"));
1038       f << "##formula[size]=" << formSz << ",";
1039       ok=false;
1040     }
1041     else {
1042       ascii().addDelimiter(formulaPos,'|');
1043       ascii().addDelimiter(formulaPos+formSz,'|');
1044       std::vector<MWAWCellContent::FormulaInstruction> formula;
1045       std::string error("");
1046       if (m_document->readFormula(cellPos,formulaPos+formSz,formula, error)) {
1047         content.m_contentType=MWAWCellContent::C_FORMULA;
1048         content.m_formula=formula;
1049       }
1050       f << "[";
1051       for (auto const &fo : formula)
1052         f << fo;
1053       f << "]" << error << ",";
1054       if (formSz&1) formSz+=1;
1055       input->seek(formulaPos+formSz, librevenge::RVNG_SEEK_SET);
1056     }
1057   }
1058 
1059   if (ok && input->tell()+4<=endPos) {
1060     auto typeVal=static_cast<int>(input->readLong(2));
1061     long valuePos=input->tell();
1062     switch (typeVal) {
1063     case 1: // no value
1064       break;
1065     case 5: {
1066       if (format.m_format==MWAWCell::F_TEXT)
1067         format.m_format=MWAWCell::F_NUMBER;
1068       if (content.m_contentType!=MWAWCellContent::C_FORMULA)
1069         content.m_contentType=MWAWCellContent::C_NUMBER;
1070       if (valuePos+10+2>endPos) {
1071         MWAW_DEBUG_MSG(("GreatWksSSParser::readCell: can not read a float value\n"));
1072         f << "###";
1073         ok=false;
1074         break;
1075       }
1076       double value=0;
1077       bool isNAN=false;
1078       if (!input->readDouble10(value, isNAN)) {
1079         MWAW_DEBUG_MSG(("GreatWksSSParser::readCell: can not read a float value(II)\n"));
1080         f << "###";
1081       }
1082       else if (format.m_format==MWAWCell::F_DATE) // change the reference date from 1/1/1904 to 1/1/1900
1083         content.setValue(value+1460.);
1084       else
1085         content.setValue(value);
1086       f << value << ",";
1087       break;
1088     }
1089     case 7: {
1090       if (content.m_contentType!=MWAWCellContent::C_FORMULA)
1091         content.m_contentType=MWAWCellContent::C_TEXT;
1092       auto sSz=static_cast<int>(input->readULong(1));
1093       if (valuePos+1+sSz+2>endPos) {
1094         MWAW_DEBUG_MSG(("GreatWksSSParser::readCell: can not read a string value\n"));
1095         f << "###";
1096         ok=false;
1097         break;
1098       }
1099       content.m_textEntry.setBegin(valuePos+1);
1100       content.m_textEntry.setLength(sSz);
1101       std::string text("");
1102       for (int i=0; i<sSz; ++i)
1103         text += char(input->readULong(1));
1104       f << "\"" << text << "\",";
1105       if ((sSz%2)==0)
1106         input->seek(1, librevenge::RVNG_SEEK_CUR);
1107       break;
1108     }
1109     case 8: { // often a boolean value
1110       if (format.m_format==MWAWCell::F_TEXT)
1111         format.m_format=MWAWCell::F_BOOLEAN;
1112       if (content.m_contentType!=MWAWCellContent::C_FORMULA)
1113         content.m_contentType=MWAWCellContent::C_NUMBER;
1114       if (valuePos+4+2>endPos) {
1115         MWAW_DEBUG_MSG(("GreatWksSSParser::readCell: can not read a long value\n"));
1116         ok = false;
1117         f << "###";
1118         break;
1119       }
1120       long value=input->readLong(4);
1121       if (format.m_format==MWAWCell::F_DATE) // change the reference date from 1/1/1904 to 1/1/1900
1122         content.setValue(double(value)+1460.);
1123       else
1124         content.setValue(double(value));
1125       f << value << ",";
1126       break;
1127     }
1128     case 15: {
1129       if (format.m_format==MWAWCell::F_TEXT)
1130         format.m_format=MWAWCell::F_NUMBER;
1131       if (content.m_contentType!=MWAWCellContent::C_FORMULA)
1132         content.m_contentType=MWAWCellContent::C_NUMBER;
1133       if (valuePos+4+2>endPos) {
1134         MWAW_DEBUG_MSG(("GreatWksSSParser::readCell: can not read a nan value\n"));
1135         ok = false;
1136         f << "###";
1137         break;
1138       }
1139       input->seek(4, librevenge::RVNG_SEEK_CUR); // nan type
1140       double value=std::numeric_limits<double>::quiet_NaN();
1141       content.setValue(value);
1142       f << value << ",";
1143       break;
1144     }
1145     default:
1146       ok=false;
1147       MWAW_DEBUG_MSG(("GreatWksSSParser::readCell: find unexpected type value\n"));
1148       f << "#type[val]=" << typeVal << ",";
1149     }
1150   }
1151   if (ok && input->tell()+2<=endPos) {
1152     val =static_cast<int>(input->readULong(2));
1153     if (!input->checkPosition(input->tell()+4*val)) {
1154       MWAW_DEBUG_MSG(("GreatWksSSParser::readCell: can not read the number of childs\n"));
1155       f << "###";
1156     }
1157     else if (val) {
1158       f << "childs=[";
1159       for (int i=0; i<val; ++i) {
1160         f << input->readULong(2);
1161         f << "x" << input->readULong(2) << ",";
1162       }
1163       f << "],";
1164     }
1165   }
1166   cell.setFormat(format);
1167   if (input->tell()!=endPos) {
1168     ascii().addDelimiter(input->tell(),'|');
1169     input->seek(endPos, librevenge::RVNG_SEEK_SET);
1170   }
1171   ascii().addPos(pos-2);
1172   ascii().addNote(f.str().c_str());
1173   return true;
1174 }
1175 
1176 ////////////////////////////////////////////////////////////
1177 // read the fonts
1178 ////////////////////////////////////////////////////////////
readStyles()1179 bool GreatWksSSParser::readStyles()
1180 {
1181   MWAWInputStreamPtr input = getInput();
1182   long pos = input->tell();
1183   int const vers=version();
1184   libmwaw::DebugStream f;
1185 
1186   f << "Entries(Style):";
1187   auto sz=long(input->readULong(4));
1188   long endPos=pos+4+sz;
1189   int expectedSize=vers==1 ? 18 : 40;
1190   if (!input->checkPosition(endPos) || (sz%expectedSize)) {
1191     MWAW_DEBUG_MSG(("GreatWksSSParser::readStyles: can not find the font defs zone\n"));
1192     f << "###";
1193     ascii().addPos(pos-2);
1194     ascii().addNote(f.str().c_str());
1195     return false;
1196   }
1197   ascii().addPos(pos-2);
1198   ascii().addNote(f.str().c_str());
1199   auto numFonts=int(sz/expectedSize);
1200   for (int i=0; i <numFonts; ++i) {
1201     pos=input->tell();
1202     f.str("");
1203     f << "Style-" << i << ":";
1204     GreatWksSSParserInternal::Style style;
1205     MWAWFont &font=style.m_font;
1206     auto val=static_cast<int>(input->readLong(2)); // always 0 ?
1207     if (val) f << "#unkn=" << val << ",";
1208     val=static_cast<int>(input->readLong(2));
1209     if (val!=1) f << "used?=" << val << ",";
1210 
1211     font.setId(m_document->getTextParser()->getFontId(static_cast<int>(input->readULong(2))));
1212     auto flag =static_cast<int>(input->readULong(2));
1213     uint32_t flags=0;
1214     if (flag&0x1) flags |= MWAWFont::boldBit;
1215     if (flag&0x2) flags |= MWAWFont::italicBit;
1216     if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
1217     if (flag&0x8) flags |= MWAWFont::embossBit;
1218     if (flag&0x10) flags |= MWAWFont::shadowBit;
1219     if (flag&0x20) font.setDeltaLetterSpacing(-1);
1220     if (flag&0x40) font.setDeltaLetterSpacing(1);
1221     if (flag&0x100) font.set(MWAWFont::Script::super100());
1222     if (flag&0x200) font.set(MWAWFont::Script::sub100());
1223     if (flag&0x800) font.setStrikeOutStyle(MWAWFont::Line::Simple);
1224     if (flag&0x2000) {
1225       font.setUnderlineStyle(MWAWFont::Line::Simple);
1226       font.setUnderlineType(MWAWFont::Line::Double);
1227     }
1228     flag &=0xD480;
1229     if (flag) f << "#fl=" << std::hex << flag << std::dec << ",";
1230     font.setFlags(flags);
1231     font.setSize(float(input->readULong(2)));
1232     unsigned char color[3];
1233     for (auto &c : color) c= static_cast<unsigned char>(input->readULong(2)>>8);
1234     font.setColor(MWAWColor(color[0],color[1],color[2]));
1235     f << font.getDebugString(getParserState()->m_fontConverter) << ",";
1236     f << "h[line]?=" << input->readULong(2) << ",";
1237     if (vers==1) {
1238       m_state->m_styleList.push_back(style);
1239       ascii().addPos(pos);
1240       ascii().addNote(f.str().c_str());
1241       continue;
1242     }
1243     MWAWColor bfColors[2];
1244     for (int j=0; j<2; ++j) {  // front/back color?
1245       for (auto &c : color) c= static_cast<unsigned char>(input->readULong(2)>>8);
1246       MWAWColor col(color[0],color[1],color[2]);
1247       bfColors[j]=col;
1248       if ((j==0 && col.isBlack()) || (j==1 && col.isWhite())) continue;
1249       if (j==0) f << "col[front]=" << MWAWColor(color[0],color[1],color[2]) << ",";
1250       else f << "col[back]=" << MWAWColor(color[0],color[1],color[2]) << ",";
1251     }
1252     auto patId=static_cast<int>(input->readLong(2));
1253     if (!patId) {
1254       style.m_backgroundColor=bfColors[1];
1255       input->seek(8, librevenge::RVNG_SEEK_CUR);
1256     }
1257     else {
1258       f << "pattern[id]=" << patId << ",";
1259       MWAWGraphicStyle::Pattern pattern;
1260       pattern.m_dim=MWAWVec2i(8,8);
1261       pattern.m_data.resize(8);
1262       for (auto &data : pattern.m_data) data=static_cast<unsigned char>(input->readULong(1));
1263       pattern.m_colors[0]=bfColors[1];
1264       pattern.m_colors[1]=bfColors[0];
1265       pattern.getAverageColor(style.m_backgroundColor);
1266       f << "pat=[" << pattern << "],";
1267     }
1268     m_state->m_styleList.push_back(style);
1269     ascii().addDelimiter(input->tell(), '|');
1270     ascii().addPos(pos);
1271     ascii().addNote(f.str().c_str());
1272     input->seek(pos+expectedSize, librevenge::RVNG_SEEK_SET);
1273   }
1274   return true;
1275 }
1276 
1277 ////////////////////////////////////////////////////////////
1278 // read the header
1279 ////////////////////////////////////////////////////////////
checkHeader(MWAWHeader * header,bool strict)1280 bool GreatWksSSParser::checkHeader(MWAWHeader *header, bool strict)
1281 {
1282   *m_state = GreatWksSSParserInternal::State();
1283   if (!m_document->checkHeader(header, strict)) return false;
1284   return getParserState()->m_kind==MWAWDocument::MWAW_K_SPREADSHEET;
1285 }
1286 
1287 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
1288