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