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 <string.h>
35
36 #include <iomanip>
37 #include <iostream>
38 #include <limits>
39 #include <set>
40 #include <sstream>
41
42 #include <librevenge/librevenge.h>
43
44 #include "MWAWTextListener.hxx"
45 #include "MWAWHeader.hxx"
46 #include "MWAWOLEParser.hxx"
47 #include "MWAWParagraph.hxx"
48 #include "MWAWParser.hxx"
49 #include "MWAWPosition.hxx"
50 #include "MWAWPrinter.hxx"
51 #include "MWAWSection.hxx"
52 #include "MWAWSubDocument.hxx"
53 #include "MWAWRSRCParser.hxx"
54
55 #include "MsWksGraph.hxx"
56 #include "MsWks3Text.hxx"
57 #include "MsWks4Text.hxx"
58 #include "MsWks4Zone.hxx"
59
60 #include "MsWksDocument.hxx"
61
62 /** Internal: the structures of a MsWksDocument */
63 namespace MsWksDocumentInternal
64 {
65
66 ////////////////////////////////////////
67 //! Internal: the state of a MsWksDocument
68 struct State {
69 //! constructor
StateMsWksDocumentInternal::State70 State()
71 : m_kind(MWAWDocument::MWAW_K_TEXT)
72 , m_fileHeaderSize(0)
73 , m_freeZoneId(0)
74 , m_typeZoneMap()
75 , m_entryMap()
76 , m_hasHeader(false)
77 , m_hasFooter(false)
78 , m_oleParser()
79 , m_headerParser()
80 , m_footerParser()
81 , m_footnoteParser()
82 , m_frameParserMap()
83 , m_unparsedOlesName()
84 , m_actPage(0)
85 , m_numPages(0)
86 , m_headerHeight(0)
87 , m_footerHeight(0)
88 {
89 }
90 //! returns a color palette
91 std::vector<MWAWColor> const &getPalette(int vers);
92 //! the type of document
93 MWAWDocument::Kind m_kind;
94 //! the file header size
95 long m_fileHeaderSize;
96 //! an id to count the number of free map
97 mutable int m_freeZoneId;
98 //! the list of zone (for v1-v3) document
99 std::multimap<int, MsWksDocument::Zone> m_typeZoneMap;
100 //! the list of entries, name->entry ( for v4 document)
101 std::multimap<std::string, MWAWEntry> m_entryMap;
102 bool m_hasHeader /** true if there is a header v3*/, m_hasFooter /** true if there is a footer v3*/;
103
104 // ole data
105 /** the ole parser */
106 std::shared_ptr<MWAWOLEParser> m_oleParser;
107 std::shared_ptr<MsWks4Zone> m_headerParser /**parser of the header ole*/, m_footerParser /**parser of the footer ole*/,
108 m_footnoteParser /**parser of the footnote ole*/;
109 /**the frame parsers: name-> parser*/
110 std::map<std::string, std::shared_ptr<MsWks4Zone> > m_frameParserMap;
111 //! the list of unparsed OLEs
112 std::vector<std::string> m_unparsedOlesName;
113 //! the color palette
114 std::vector<MWAWColor> m_colorPalette[4];
115 int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
116
117 int m_headerHeight /** the header height if known */,
118 m_footerHeight /** the footer height if known */;
119 };
120
getPalette(int vers)121 std::vector<MWAWColor> const &State::getPalette(int vers)
122 {
123 switch (vers) {
124 case 2: {
125 if (!m_colorPalette[0].empty())
126 return m_colorPalette[0];
127 m_colorPalette[0].resize(9);
128 m_colorPalette[0][0]=MWAWColor(0,0,0); // undef
129 m_colorPalette[0][1]=MWAWColor(0,0,0);
130 m_colorPalette[0][2]=MWAWColor(255,255,255);
131 m_colorPalette[0][3]=MWAWColor(255,0,0);
132 m_colorPalette[0][4]=MWAWColor(0,255,0);
133 m_colorPalette[0][5]=MWAWColor(0,0,255);
134 m_colorPalette[0][6]=MWAWColor(0, 255,255);
135 m_colorPalette[0][7]=MWAWColor(255,0,255);
136 m_colorPalette[0][8]=MWAWColor(255,255,0);
137 return m_colorPalette[0];
138 }
139 case 3: {
140 if (!m_colorPalette[1].empty())
141 return m_colorPalette[1];
142 m_colorPalette[1].resize(256);
143 size_t ind=0;
144 for (int k = 0; k < 6; k++) {
145 for (int j = 0; j < 6; j++) {
146 for (int i = 0; i < 6; i++, ind++) {
147 if (j==5 && i==2) break;
148 m_colorPalette[1][ind]=MWAWColor(static_cast<unsigned char>(255-51*i), static_cast<unsigned char>(255-51*k), static_cast<unsigned char>(255-51*j));
149 }
150 }
151 }
152
153 // the last 2 lines
154 for (int r = 0; r < 2; r++) {
155 // the black, red, green, blue zone of 5*2
156 for (int c = 0; c < 4; c++) {
157 for (int i = 0; i < 5; i++, ind++) {
158 int val = 17*r+51*i;
159 if (c == 0) {
160 m_colorPalette[1][ind]=MWAWColor(static_cast<unsigned char>(val), static_cast<unsigned char>(val), static_cast<unsigned char>(val));
161 continue;
162 }
163 int color[3]= {0,0,0};
164 color[c-1]=val;
165 m_colorPalette[1][ind]=MWAWColor(static_cast<unsigned char>(color[0]),static_cast<unsigned char>(color[1]),static_cast<unsigned char>(color[2]));
166 }
167 }
168 // last part of j==5, i=2..5
169 for (int k = r; k < 6; k+=2) {
170 for (int i = 2; i < 6; i++, ind++)
171 m_colorPalette[1][ind]=MWAWColor(static_cast<unsigned char>(255-51*i), static_cast<unsigned char>(255-51*k), static_cast<unsigned char>(255-51*5));
172 }
173 }
174 return m_colorPalette[1];
175 }
176 case 4: {
177 if (!m_colorPalette[2].empty())
178 return m_colorPalette[2];
179 m_colorPalette[2].resize(256);
180 size_t ind=0;
181 for (int k = 0; k < 6; k++) {
182 for (int j = 0; j < 6; j++) {
183 for (int i = 0; i < 6; i++, ind++) {
184 m_colorPalette[2][ind]=
185 MWAWColor(static_cast<unsigned char>(255-51*k), static_cast<unsigned char>(255-51*j),
186 static_cast<unsigned char>(255-51*i));
187 }
188 }
189 }
190 ind--; // remove the black color
191 for (int c = 0; c < 4; c++) {
192 unsigned char color[3] = {0,0,0};
193 auto val=static_cast<unsigned char>(251);
194 for (int i = 0; i < 10; i++) {
195 val = static_cast<unsigned char>(val-17);
196 if (c == 3) m_colorPalette[2][ind++]=MWAWColor(val, val, val);
197 else {
198 color[c] = val;
199 m_colorPalette[2][ind++]=MWAWColor(color[0],color[1],color[2]);
200 }
201 if ((i%2)==1) val = static_cast<unsigned char>(val-17);
202 }
203 }
204
205 // last is black
206 m_colorPalette[2][ind++]=MWAWColor(0,0,0);
207 return m_colorPalette[2];
208 }
209 default:
210 break;
211 }
212 MWAW_DEBUG_MSG(("MsWksDocumentInternal::State::getPalette: can not find palette for version %d\n", vers));
213 return m_colorPalette[3];
214 }
215
216 ////////////////////////////////////////
217 //! Internal: the subdocument of a MsWksDocument
218 class SubDocument final : public MWAWSubDocument
219 {
220 public:
221 enum Type { Note, OLE, Zone, Text };
SubDocument(MsWksDocument & document,MWAWInputStreamPtr const & input,Type type,int zoneId)222 SubDocument(MsWksDocument &document, MWAWInputStreamPtr const &input, Type type, int zoneId)
223 : MWAWSubDocument(document.m_parser, input, MWAWEntry())
224 , m_document(document)
225 , m_type(type)
226 , m_id(zoneId)
227 {
228 }
229
230 //! destructor
~SubDocument()231 ~SubDocument() final {}
232
233 //! operator!=
234 bool operator!=(MWAWSubDocument const &doc) const final;
235
236 //! the parser function
237 void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
238
239 protected:
240 /** the main document */
241 MsWksDocument &m_document;
242 /** the type */
243 Type m_type;
244 /** the subdocument id*/
245 int m_id;
246 };
247
parse(MWAWListenerPtr & listener,libmwaw::SubDocumentType)248 void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
249 {
250 if (!listener.get()) {
251 MWAW_DEBUG_MSG(("MsWksDocument::SubDocument::parse: no listener\n"));
252 return;
253 }
254
255 long pos = m_input->tell();
256 switch (m_type) {
257 case Note:
258 m_document.sendFootnoteContent(m_id);
259 break;
260 case OLE:
261 if (m_id==MsWksDocument::Z_HEADER) m_document.sendTextbox(MWAWEntry(),"QHdr");
262 else if (m_id==MsWksDocument::Z_FOOTER) m_document.sendTextbox(MWAWEntry(),"QFtr");
263 else {
264 MWAW_DEBUG_MSG(("MsWksDocument::SubDocument::parse: unexpected ole zone %d\n", m_id));
265 }
266 break;
267 case Text:
268 m_document.sendText(m_id);
269 break;
270 case Zone:
271 m_document.sendZone(m_id);
272 break;
273 #if !defined(__clang__)
274 default:
275 MWAW_DEBUG_MSG(("MsWksDocument::SubDocument::parse: unexpected zone type\n"));
276 break;
277 #endif
278 }
279 m_input->seek(pos, librevenge::RVNG_SEEK_SET);
280 }
281
operator !=(MWAWSubDocument const & doc) const282 bool SubDocument::operator!=(MWAWSubDocument const &doc) const
283 {
284 if (MWAWSubDocument::operator!=(doc)) return true;
285 auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
286 if (!sDoc) return true;
287 if (&m_document != &sDoc->m_document) return true;
288 if (m_id != sDoc->m_id) return true;
289 if (m_type != sDoc->m_type) return true;
290 return false;
291 }
292 }
293
294 ////////////////////////////////////////////////////////////
295 // constructor/destructor, accessor functions...
296 ////////////////////////////////////////////////////////////
MsWksDocument(MWAWInputStreamPtr const & input,MWAWParser & parser)297 MsWksDocument::MsWksDocument(MWAWInputStreamPtr const &input, MWAWParser &parser)
298 : m_state()
299 , m_parserState(parser.getParserState())
300 , m_parser(&parser)
301 , m_parentDocument(nullptr)
302 , m_input(input)
303 , m_asciiFile(MWAWInputStreamPtr())
304 , m_graphParser()
305 , m_textParser3()
306 , m_textParser4()
307 , m_newPage(nullptr)
308 {
309 m_state.reset(new MsWksDocumentInternal::State());
310 m_graphParser.reset(new MsWksGraph(*this));
311 }
312
~MsWksDocument()313 MsWksDocument::~MsWksDocument()
314 {
315 }
316
initAsciiFile(std::string const & name)317 void MsWksDocument::initAsciiFile(std::string const &name)
318 {
319 m_asciiFile.setStream(m_input);
320 m_asciiFile.open(name);
321 }
322
getTextParser3()323 std::shared_ptr<MsWks3Text> MsWksDocument::getTextParser3()
324 {
325 if (!m_textParser3) m_textParser3.reset(new MsWks3Text(*this));
326 return m_textParser3;
327 }
328
getTextParser4()329 std::shared_ptr<MsWks4Text> MsWksDocument::getTextParser4()
330 {
331 if (!m_textParser4) m_textParser4.reset(new MsWks4Text(*this));
332 return m_textParser4;
333 }
334
setVersion(int vers)335 void MsWksDocument::setVersion(int vers)
336 {
337 m_parserState->m_version=vers;
338 }
339
version() const340 int MsWksDocument::version() const
341 {
342 return m_parserState->m_version;
343 }
344
getKind() const345 MWAWDocument::Kind MsWksDocument::getKind() const
346 {
347 return m_state->m_kind;
348 }
349
setKind(MWAWDocument::Kind kind)350 void MsWksDocument::setKind(MWAWDocument::Kind kind)
351 {
352 m_state->m_kind=kind;
353 }
354
getNewZoneId() const355 int MsWksDocument::getNewZoneId() const
356 {
357 return MsWksDocument::Z_NONE + ++m_state->m_freeZoneId;
358 }
359
getZone(MsWksDocument::ZoneType type) const360 MsWksDocument::Zone MsWksDocument::getZone(MsWksDocument::ZoneType type) const
361 {
362 Zone res;
363 if (m_state->m_typeZoneMap.find(int(type)) != m_state->m_typeZoneMap.end())
364 res = m_state->m_typeZoneMap.find(int(type))->second;
365 return res;
366 }
367
getTypeZoneMap()368 std::multimap<int, MsWksDocument::Zone> &MsWksDocument::getTypeZoneMap()
369 {
370 return m_state->m_typeZoneMap;
371 }
372
getEntryMap()373 std::multimap<std::string, MWAWEntry> &MsWksDocument::getEntryMap()
374 {
375 return m_state->m_entryMap;
376 }
377
getLengthOfFileHeader3() const378 long MsWksDocument::getLengthOfFileHeader3() const
379 {
380 return m_state->m_fileHeaderSize;
381 }
382
sendText(int id)383 void MsWksDocument::sendText(int id)
384 {
385 if (m_textParser3) m_textParser3->sendZone(id);
386 }
387
sendZone(int zoneType)388 void MsWksDocument::sendZone(int zoneType)
389 {
390 Zone zone=getZone(ZoneType(zoneType));
391 if (zone.m_zoneId >= 0)
392 m_graphParser->sendAll(zone.m_zoneId, zoneType==MsWksDocument::Z_MAIN);
393 if (zone.m_textId >= 0) sendText(zone.m_textId);
394 }
395
sendFootnoteContent(int noteId)396 void MsWksDocument::sendFootnoteContent(int noteId)
397 {
398 if (!m_parserState->getMainListener()) return;
399 if (!m_state->m_footnoteParser) {
400 MWAW_DEBUG_MSG(("MsWksDocument::sendFootnoteContent: can not found the footnote parser\n"));
401 m_parserState->getMainListener()->insertChar(' ');
402 return;
403 }
404 m_state->m_footnoteParser->createZones(false);
405 m_state->m_footnoteParser->readFootNote(noteId);
406 }
407
408 ////////////////////////////////////////////////////////////
409 // page span function
410 ////////////////////////////////////////////////////////////
hasHeader() const411 bool MsWksDocument::hasHeader() const
412 {
413 return m_state->m_hasHeader;
414 }
415
hasFooter() const416 bool MsWksDocument::hasFooter() const
417 {
418 return m_state->m_hasFooter;
419 }
420
getHeaderFooterHeight(bool header) const421 float MsWksDocument::getHeaderFooterHeight(bool header) const
422 {
423 return header ? float(m_state->m_headerHeight) : float(m_state->m_footerHeight);
424 }
425
getPageSpanList(std::vector<MWAWPageSpan> & pagesList,int & numPages)426 void MsWksDocument::getPageSpanList(std::vector<MWAWPageSpan> &pagesList, int &numPages)
427 {
428 Zone mainZone=getZone(Z_MAIN);
429 // first count the page
430 numPages = 1;
431 if (m_textParser3 && mainZone.m_textId >= 0 && m_textParser3->numPages(mainZone.m_textId) > numPages)
432 numPages = m_textParser3->numPages(mainZone.m_textId);
433 if (m_textParser4 && m_textParser4->numPages() > numPages)
434 numPages = m_textParser4->numPages();
435 if (m_graphParser->numPages(mainZone.m_zoneId) > numPages)
436 numPages = m_graphParser->numPages(mainZone.m_zoneId);
437 // now update the page list
438 MWAWPageSpan ps(m_parserState->m_pageSpan);
439 int id = m_textParser3 ? m_textParser3->getHeader() : -1;
440 if (id >= 0) {
441 m_state->m_headerHeight=12;
442 MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
443 header.m_subDocument.reset
444 (new MsWksDocumentInternal::SubDocument
445 (*this, getInput(), MsWksDocumentInternal::SubDocument::Text, id));
446 ps.setHeaderFooter(header);
447 }
448 else if (getZone(MsWksDocument::Z_HEADER).m_zoneId >= 0) {
449 MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
450 header.m_subDocument.reset
451 (new MsWksDocumentInternal::SubDocument
452 (*this, getInput(), MsWksDocumentInternal::SubDocument::Zone, int(MsWksDocument::Z_HEADER)));
453 ps.setHeaderFooter(header);
454 }
455 else if (m_state->m_headerParser) {
456 MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
457 header.m_subDocument.reset
458 (new MsWksDocumentInternal::SubDocument
459 (*this, getInput(), MsWksDocumentInternal::SubDocument::OLE, int(MsWksDocument::Z_HEADER)));
460 ps.setHeaderFooter(header);
461 }
462 id = m_textParser3 ? m_textParser3->getFooter() : -1;
463 if (id >= 0) {
464 m_state->m_footerHeight=12;
465 MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
466 footer.m_subDocument.reset
467 (new MsWksDocumentInternal::SubDocument
468 (*this, getInput(), MsWksDocumentInternal::SubDocument::Text, id));
469 ps.setHeaderFooter(footer);
470 }
471 else if (getZone(MsWksDocument::Z_FOOTER).m_zoneId >= 0) {
472 MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
473 footer.m_subDocument.reset
474 (new MsWksDocumentInternal::SubDocument
475 (*this, getInput(), MsWksDocumentInternal::SubDocument::Zone, int(MsWksDocument::Z_FOOTER)));
476 ps.setHeaderFooter(footer);
477 }
478 else if (m_state->m_footerParser) {
479 MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
480 footer.m_subDocument.reset
481 (new MsWksDocumentInternal::SubDocument
482 (*this, getInput(), MsWksDocumentInternal::SubDocument::OLE, int(MsWksDocument::Z_FOOTER)));
483 ps.setHeaderFooter(footer);
484 }
485 ps.setPageSpan(numPages+1);
486 pagesList=std::vector<MWAWPageSpan>(1,ps);
487 }
488
489 ////////////////////////////////////////////////////////////
490 // interface via callback
491 ////////////////////////////////////////////////////////////
newPage(int page,bool softBreak)492 void MsWksDocument::newPage(int page, bool softBreak)
493 {
494 if (m_parentDocument)
495 return m_parentDocument->newPage(page, softBreak);
496 if (!m_newPage) {
497 MWAW_DEBUG_MSG(("MsWksDocument::newPage: can not find the newPage callback\n"));
498 return;
499 }
500 (m_parser->*m_newPage)(page, softBreak);
501 }
502
sendFootnote(int id)503 void MsWksDocument::sendFootnote(int id)
504 {
505 if (m_parentDocument)
506 return m_parentDocument->sendFootnote(id);
507 if (!m_parserState->getMainListener()) return;
508
509 MWAWSubDocumentPtr subdoc(new MsWksDocumentInternal::SubDocument
510 (*this, getInput(), MsWksDocumentInternal::SubDocument::Note, id));
511 m_parserState->getMainListener()->insertNote(MWAWNote(MWAWNote::FootNote), subdoc);
512 }
513
sendOLE(int id,MWAWPosition const & pictPos,MWAWGraphicStyle const & style)514 void MsWksDocument::sendOLE(int id, MWAWPosition const &pictPos, MWAWGraphicStyle const &style)
515 {
516 if (m_parentDocument)
517 return m_parentDocument->sendOLE(id, pictPos, style);
518
519 if (!m_parserState->getMainListener()) return;
520 librevenge::RVNGBinaryData data;
521 MWAWPosition pos;
522 std::string type;
523 if (!m_state->m_oleParser || !m_state->m_oleParser->getObject(id, data, pos, type)) {
524 MWAW_DEBUG_MSG(("MsWksDocument::sendOLE: can not find OLE%d\n", id));
525 return;
526 }
527 m_parserState->getMainListener()->insertPicture(pictPos, MWAWEmbeddedObject(data, type), style);
528 }
529
sendRBIL(int id,MWAWVec2i const & sz)530 void MsWksDocument::sendRBIL(int id, MWAWVec2i const &sz)
531 {
532 MsWksGraph::SendData sendData;
533 sendData.m_type = MsWksGraph::SendData::RBIL;
534 sendData.m_id = id;
535 sendData.m_anchor = MWAWPosition::Char;
536 sendData.m_size = sz;
537 m_graphParser->sendObjects(sendData);
538 }
539
sendTextbox(MWAWEntry const & entry,std::string const & frame)540 void MsWksDocument::sendTextbox(MWAWEntry const &entry, std::string const &frame)
541 {
542 if (m_parentDocument)
543 return m_parentDocument->sendTextbox(entry,frame);
544 MWAWListenerPtr listener=m_parserState->getMainListener();
545 if (!listener) return;
546
547 if (entry.length()==0) {
548 listener->insertChar(' ');
549 return;
550 }
551
552 MsWks4Zone *parser = nullptr;
553 if (frame == "QHdr") parser=m_state->m_headerParser.get();
554 else if (frame == "QFtr") parser=m_state->m_footerParser.get();
555 else {
556 auto frameIt = m_state->m_frameParserMap.find(frame);
557 if (frameIt != m_state->m_frameParserMap.end())
558 parser = frameIt->second.get();
559 }
560 if (!parser || (entry.length() && parser->getTextPosition().length() < entry.end())) {
561 MWAW_DEBUG_MSG(("MsWksDocument::sendTextbox: can not find frame ole: %s\n", frame.c_str()));
562 listener->insertChar(' ');
563 return;
564 }
565
566 // ok, create the entry
567 MWAWEntry ent(entry);
568 ent.setBegin(entry.begin()+parser->getTextPosition().begin());
569 parser->createZones(false);
570 parser->readContentZones(ent, false);
571 }
572
573 ////////////////////////////////////////////////////////////
574 //
575 // Intermediate level
576 //
577 ////////////////////////////////////////////////////////////
getPalette(int vers)578 std::vector<MWAWColor> const &MsWksDocument::getPalette(int vers)
579 {
580 return m_state->getPalette(vers);
581 }
582
getColor(int id,MWAWColor & col,int vers)583 bool MsWksDocument::getColor(int id, MWAWColor &col, int vers)
584 {
585 std::vector<MWAWColor> const &palette = getPalette(vers);
586 if (palette.size()==0 || id < 0 || id >= int(palette.size()) ||
587 (vers==2 && id==0)) {
588 static bool first = true;
589 if (first) {
590 MWAW_DEBUG_MSG(("MsWksDocument::getColor: unknown color=%d\n", id));
591 first = false;
592 }
593 return false;
594 }
595 col = palette[size_t(id)];
596 return true;
597 }
598
599 ////////////////////////////////////////////////////////////
600 //
601 // Low level
602 //
603 ////////////////////////////////////////////////////////////
604
605 ////////////////////////////////////////////////////////////
606 // create the ole structures or get list of unparsed zones
607 ////////////////////////////////////////////////////////////
getUnparsedOLEZones() const608 std::vector<std::string> const &MsWksDocument::getUnparsedOLEZones() const
609 {
610 return m_state->m_unparsedOlesName;
611 }
612
createOLEZones(MWAWInputStreamPtr input)613 bool MsWksDocument::createOLEZones(MWAWInputStreamPtr input)
614 {
615 if (!input || !input->isStructured()) return false;
616 m_state->m_oleParser.reset(new MWAWOLEParser("MN0", m_parserState->m_fontConverter, 3));
617
618 if (!m_state->m_oleParser->parse(input)) return false;
619
620 // normally,
621 // MacWorks/QHdr, MacWorks/QFtr, MacWorks/QFootnotes, MacWorks/QFrm<number>
622 // MN0 (the main header), MM (almost empty)
623 // MatOST/MatadorObject?/*: the picture
624 // but no metadata
625 auto unparsed = m_state->m_oleParser->getNotParse();
626
627 unparsed.push_back("MN0");
628
629 for (auto const &name : unparsed) {
630 // separated the directory and the name
631 // MatOST/MatadorObject1/Ole10Native
632 // -> dir="MatOST/MatadorObject1", base="Ole10Native"
633 auto pos = name.find_last_of('/');
634 std::string dir, base;
635 if (pos == std::string::npos) base = name;
636 else if (pos == 0) base = name.substr(1);
637 else {
638 dir = name.substr(0,pos);
639 base = name.substr(pos+1);
640 }
641
642 if (dir == "" && base == "MN0") continue;
643 bool ok = false;
644 bool isFrame = false;
645 if (dir == "MacWorks") {
646 ok = (base == "QHdr" || base == "QFtr" || base == "QFootnotes");
647 if (!ok && strncmp(base.c_str(),"QFrm",4)==0)
648 ok = isFrame = true;
649 }
650 if (!ok) {
651 m_state->m_unparsedOlesName.push_back(name);
652 continue;
653 }
654
655 MWAWInputStreamPtr ole = input->getSubStreamByName(name.c_str());
656 if (!ole.get()) {
657 MWAW_DEBUG_MSG(("MsWksDocument::createOLEZones: error: can not find OLE part: \"%s\"\n", name.c_str()));
658 continue;
659 }
660
661 std::shared_ptr<MsWks4Zone> newParser(new MsWks4Zone(ole, m_parserState, *m_parser, name));
662 try {
663 ok = newParser->createZones(false);
664 }
665 catch (...) {
666 ok = false;
667 }
668
669 if (!ok) {
670 MWAW_DEBUG_MSG(("MsWksDocument::createOLEZones: error: can not parse OLE: \"%s\"\n", name.c_str()));
671 continue;
672 }
673
674 // first update the parent document
675 newParser->m_document->m_parentDocument=this;
676 if (base == "QHdr") m_state->m_headerParser = newParser;
677 else if (base == "QFtr") m_state->m_footerParser = newParser;
678 else if (isFrame) {
679 auto frameIt = m_state->m_frameParserMap.find(base);
680 if (frameIt != m_state->m_frameParserMap.end()) {
681 MWAW_DEBUG_MSG(("MsWksDocument::createOLEZones: error: oops, I already find a frame zone %s\n", base.c_str()));
682 }
683 else
684 m_state->m_frameParserMap[base] = newParser;
685 }
686 else if (base == "QFootnotes") m_state->m_footnoteParser = newParser;
687 }
688
689 return true;
690 }
691
692 ////////////////////////////////////////////////////////////
693 // read the print info
694 ////////////////////////////////////////////////////////////
readPrintInfo()695 bool MsWksDocument::readPrintInfo()
696 {
697 MWAWInputStreamPtr input = getInput();
698 long pos = input->tell();
699 libmwaw::DebugStream f;
700 // print info
701 libmwaw::PrinterInfo info;
702 if (!input->checkPosition(pos+0x78+8) || !info.read(input)) return false;
703 f << "Entries(PrintInfo):"<< info;
704
705 MWAWVec2i paperSize = info.paper().size();
706 MWAWVec2i pageSize = info.page().size();
707 if (pageSize.x() <= 0 || pageSize.y() <= 0 ||
708 paperSize.x() <= 0 || paperSize.y() <= 0) return false;
709
710 // now read the margin
711 int margin[4];
712 int maxSize = paperSize.x() > paperSize.y() ? paperSize.x() : paperSize.y();
713 f << ", margin=(";
714 for (int i = 0; i < 4; i++) {
715 margin[i] = int(72.f/120.f*float(input->readLong(2)));
716 if (margin[i] < -maxSize || margin[i] > maxSize) return false;
717 f << margin[i];
718 if (i != 3) f << ", ";
719 }
720 f << ")";
721
722 // fixme: compute the real page length here...
723 // define margin from print info
724 MWAWVec2i lTopMargin(margin[0],margin[1]), rBotMargin(margin[2],margin[3]);
725 lTopMargin += paperSize - pageSize;
726
727 int leftMargin = lTopMargin.x();
728 int topMargin = lTopMargin.y();
729
730 // decrease a little right and bottom margins Margin
731 int rightMarg = rBotMargin.x()-50;
732 if (rightMarg < 0) {
733 leftMargin -= (-rightMarg);
734 if (leftMargin < 0) leftMargin=0;
735 rightMarg=0;
736 }
737 int botMarg = rBotMargin.y()-50;
738 if (botMarg < 0) {
739 topMargin -= (-botMarg);
740 if (topMargin < 0) topMargin=0;
741 botMarg=0;
742 }
743
744 m_parserState->m_pageSpan.setMarginTop(topMargin/72.0);
745 m_parserState->m_pageSpan.setMarginBottom(botMarg/72.0);
746 m_parserState->m_pageSpan.setMarginLeft(leftMargin/72.0);
747 m_parserState->m_pageSpan.setMarginRight(rightMarg/72.0);
748 m_parserState->m_pageSpan.setFormLength(paperSize.y()/72.);
749 m_parserState->m_pageSpan.setFormWidth(paperSize.x()/72.);
750
751 ascii().addPos(pos);
752 ascii().addNote(f.str().c_str());
753 input->seek(pos+0x78+8, librevenge::RVNG_SEEK_SET);
754
755 return true;
756 }
757
758 ////////////////////////////////////////////////////////////
759 // read the document info
760 ////////////////////////////////////////////////////////////
readDocumentInfo(long sz)761 bool MsWksDocument::readDocumentInfo(long sz)
762 {
763 MWAWInputStreamPtr input = getInput();
764 long pos = input->tell();
765 libmwaw::DebugStream f;
766
767 int vers = m_parserState->m_kind==MWAWDocument::MWAW_K_DATABASE ? 2 : version();
768 int docId = 0;
769 int docExtra = 0;
770 int flag = 0;
771 int expectedSz = 0x80;
772 if (sz<=0) {
773 if (input->readLong(1) != 2)
774 return false;
775 docId = static_cast<int>(input->readULong(1));
776 docExtra = static_cast<int>(input->readULong(1));
777 flag = static_cast<int>(input->readULong(1));
778 sz = long(input->readULong(2));
779 expectedSz = vers<=2 ? 0x15e : 0x9a;
780 }
781 long endPos = input->tell()+sz;
782 if (!input->checkPosition(endPos))
783 return false;
784
785 if (sz < expectedSz) {
786 if (sz < 0x78+8) {
787 MWAW_DEBUG_MSG(("MsWksDocument::readDocumentInfo: size is too short\n"));
788 return false;
789 }
790 MWAW_DEBUG_MSG(("MsWksDocument::readDocumentInfo: size is too short: try to continue\n"));
791 }
792
793 f << "Entries(DocInfo):";
794 if (docId) f << "id=0x"<< std::hex << docId << ",";
795 if (docExtra) f << "unk=" << docExtra << ","; // in v3: find 3, 7, 1x
796 if (flag) f << "fl=" << flag << ","; // in v3: find 80, 84, e0
797 ascii().addPos(pos);
798 ascii().addNote(f.str().c_str());
799
800 if (!readPrintInfo()) {
801 input->seek(pos, librevenge::RVNG_SEEK_SET);
802 return true;
803 }
804
805 if (sz < 0x9a) {
806 input->seek(endPos, librevenge::RVNG_SEEK_SET);
807 return true;
808 }
809 pos = input->tell();
810 f.str("");
811 f << "DocInfo-1:";
812 auto val = static_cast<int>(input->readLong(2));
813 if ((val & 0x0400) && vers >= 3) {
814 f << "titlepage,";
815 val &= 0xFBFF;
816 }
817 if (val) f << "unkn=" << val << ",";
818 if (vers <= 2) {
819 for (int wh = 0; wh < 2; wh++) {
820 long debPos = input->tell();
821 std::string name(wh==0 ? "header" : "footer");
822 std::string text = getTextParser3()->readHeaderFooterString(wh==0);
823 if (text.size()) f << name << "="<< text << ",";
824
825 long remain = debPos+100 - input->tell();
826 for (long i = 0; i < remain; i++) {
827 auto c = static_cast<unsigned char>(input->readULong(1));
828 if (c == 0) continue;
829 f << std::dec << "f"<< i << "=" << static_cast<int>(c) << ",";
830 }
831 }
832 f << "defFid=" << input->readULong(2) << ",";
833 f << "defFsz=" << input->readULong(2)/2 << ",";
834 val = static_cast<int>(input->readULong(2)); // 0 or 8
835 if (val) f << "#unkn=" << val << ",";
836 int dim[2];
837 for (auto &d : dim) d = static_cast<int>(input->readULong(2));
838 f << "dim=" << dim[0] << "x" << dim[1] << ",";
839 /* followed by 0 (v1) or 0|0x21|0* (v2)*/
840 ascii().addPos(pos);
841 ascii().addNote(f.str().c_str());
842 pos = input->tell();
843 f.str("");
844 f << "DocInfo-2:";
845 }
846
847 // last data ( normally 26)
848 auto numData = int((endPos - input->tell())/2);
849 for (int i = 0; i < numData; i++) {
850 val = static_cast<int>(input->readLong(2));
851 switch (i) {
852 case 2:
853 if (val!=1) f << "firstPageNumber=" << val << ",";
854 break;
855 case 3:
856 if (val!=1) f << "firstNoteNumber=" << val << ",";
857 break;
858 default:
859 if (val)
860 f << "g" << i << "=" << val << ",";
861 break;
862 }
863 }
864 ascii().addPos(pos);
865 ascii().addNote(f.str().c_str());
866
867 input->seek(endPos, librevenge::RVNG_SEEK_SET);
868
869 return true;
870 }
871
872 ////////////////////////////////////////////////////////////
873 // read a header/footer zone info
874 ////////////////////////////////////////////////////////////
readGroupHeaderFooter(bool header,int check)875 bool MsWksDocument::readGroupHeaderFooter(bool header, int check)
876 {
877 if (version() < 3) return false;
878
879 MWAWInputStreamPtr input=getInput();
880 long debPos = input->tell();
881
882 auto ptr = long(input->readULong(2));
883 if (input->isEnd()) return false;
884 if (ptr) {
885 if (check == 49) return false;
886 if (check == 99) {
887 MWAW_DEBUG_MSG(("MsWksDocument::readGroupHeaderFooter: find ptr=0x%lx\n", static_cast<long unsigned int>(ptr)));
888 }
889 }
890
891 libmwaw::DebugStream f;
892
893 int size = static_cast<int>(input->readLong(2))+4;
894 int realSize = 0x11;
895 if (size < realSize) return false;
896 if (input->readLong(2) != 0) return false;
897 f << "Entries(GroupHInfo)";
898 if (header)
899 f << "[header]";
900 else
901 f << "[footer]";
902 f << ": size=" << std::hex << size << std::dec << " BTXT";
903
904 if (!input->checkPosition(debPos+size)) return false;
905
906 input->seek(debPos+6, librevenge::RVNG_SEEK_SET);
907 auto N=static_cast<int>(input->readLong(2));
908 f << ", N=" << N;
909 int dim[4];
910 for (auto &d : dim) d = static_cast<int>(input->readLong(2));
911
912 MWAWBox2i box(MWAWVec2i(dim[1], dim[0]), MWAWVec2i(dim[3], dim[2]));
913 if (box.size().x() < -2000 || box.size().y() < -2000 ||
914 box.size().x() > 2000 || box.size().y() > 2000 ||
915 box.min().x() < -200 || box.min().y() < -200) return false;
916 if (check == 49 && box.size().x() == 0 && box.size().y() == 0) return false;
917 f << ", BDBox =" << box;
918 auto val = static_cast<int>(input->readULong(1));
919 if (val) f << ", flag=" << val;
920
921 input->seek(debPos+size, librevenge::RVNG_SEEK_SET);
922 if (check < 99) return true;
923 if (header) m_state->m_headerHeight = box.size().y();
924 else m_state->m_footerHeight = box.size().y();
925 std::multimap<int, MsWksDocument::Zone> &typeZoneMap=getTypeZoneMap();
926 MsWksDocument::ZoneType type=
927 header ? MsWksDocument::Z_HEADER : MsWksDocument::Z_FOOTER;
928 MsWksDocument::Zone zone(type, int(typeZoneMap.size()));
929
930 ascii().addPos(debPos);
931 ascii().addNote(f.str().c_str());
932 ascii().addPos(input->tell());
933
934 input->seek(debPos+realSize, librevenge::RVNG_SEEK_SET);
935 input->pushLimit(debPos+size);
936 bool limitSet = true;
937 for (int i = 0; i < N; i++) {
938 long pos = input->tell();
939 if (limitSet && pos==debPos+size) {
940 limitSet = false;
941 input->popLimit();
942 }
943 if (readZone(zone)) continue;
944 input->seek(pos, librevenge::RVNG_SEEK_SET);
945 zone.m_textId = getTextParser3()->createZones(N-i, false);
946 if (zone.m_textId >= 0)
947 break;
948 MWAW_DEBUG_MSG(("MsWksDocument::readGroupHeaderFooter: can not find end of group\n"));
949 input->seek(pos, librevenge::RVNG_SEEK_SET);
950 }
951 if (limitSet) input->popLimit();
952 if (long(input->tell()) < debPos+size) {
953 ascii().addPos(input->tell());
954 ascii().addNote("GroupHInfo-II");
955
956 input->seek(debPos+size, librevenge::RVNG_SEEK_SET);
957
958 ascii().addPos(debPos + size);
959 ascii().addNote("_");
960 }
961 // getGraphParser()->addDeltaToPositions(zone.m_zoneId, -1*box[0]);
962 if (typeZoneMap.find(int(type)) != typeZoneMap.end()) {
963 MWAW_DEBUG_MSG(("MsWksDocument::readGroupHeaderFooter: the zone already exists\n"));
964 }
965 else
966 typeZoneMap.insert(std::multimap<int,MsWksDocument::Zone>::value_type(int(type),zone));
967 return true;
968 }
969
970 ////////////////////////////////////////////////////////////
971 // read a generic zone
972 ////////////////////////////////////////////////////////////
readZone(MsWksDocument::Zone & zone)973 bool MsWksDocument::readZone(MsWksDocument::Zone &zone)
974 {
975 MWAWInputStreamPtr input = getInput();
976 if (input->isEnd()) return false;
977 long pos = input->tell();
978 MWAWEntry pict;
979 auto val = static_cast<int>(input->readLong(1));
980 input->seek(-1, librevenge::RVNG_SEEK_CUR);
981 switch (val) {
982 case 0: {
983 if (m_graphParser->getEntryPicture(zone.m_zoneId, pict)>=0) {
984 input->seek(pict.end(), librevenge::RVNG_SEEK_SET);
985 return true;
986 }
987 break;
988 }
989 case 1: {
990 if (m_graphParser->getEntryPictureV1(zone.m_zoneId, pict)>=0) {
991 input->seek(pict.end(), librevenge::RVNG_SEEK_SET);
992 return true;
993 }
994 break;
995 }
996 case 2:
997 if (readDocumentInfo())
998 return true;
999 break;
1000 case 3: {
1001 // checkme, ok for text but is it also ok for other ?
1002 MWAWEntry group;
1003 group.setId(zone.m_zoneId);
1004 group.setName("RBDR");
1005 if (m_graphParser->readRB(input, group, 2))
1006 return true;
1007 break;
1008 }
1009 default:
1010 break;
1011 }
1012
1013 input->seek(pos, librevenge::RVNG_SEEK_SET);
1014 return false;
1015 }
1016
1017 ////////////////////////////////////////////////////////////
1018 // read the header
1019 ////////////////////////////////////////////////////////////
checkHeader3(MWAWHeader * header,bool strict)1020 bool MsWksDocument::checkHeader3(MWAWHeader *header, bool strict)
1021 {
1022 *m_state = MsWksDocumentInternal::State();
1023 MWAWInputStreamPtr input = getInput();
1024 if (!input || !input->hasDataFork())
1025 return false;
1026
1027 int numError = 0, val;
1028
1029 const int headerSize = 0x20;
1030
1031 libmwaw::DebugStream f;
1032
1033 input->seek(0,librevenge::RVNG_SEEK_SET);
1034
1035 m_state->m_hasHeader = m_state->m_hasFooter = false;
1036 auto vers = static_cast<int>(input->readULong(4));
1037 switch (vers) {
1038 case 11:
1039 setVersion(4);
1040 break;
1041 case 9:
1042 setVersion(3);
1043 break;
1044 case 8:
1045 setVersion(2);
1046 break;
1047 case 4:
1048 setVersion(1);
1049 break;
1050 default:
1051 if (strict) return false;
1052
1053 MWAW_DEBUG_MSG(("MsWksDocument::checkHeader3: find unknown version 0x%x\n", static_cast<unsigned int>(vers)));
1054 // must we stop in this case, or can we continue ?
1055 if (vers < 0 || vers > 14) {
1056 MWAW_DEBUG_MSG(("MsWksDocument::checkHeader3: version too big, we stop\n"));
1057 return false;
1058 }
1059 setVersion((vers < 4) ? 1 : (vers < 8) ? 2 : (vers < 11) ? 3 : 4);
1060 }
1061 if (input->seek(headerSize,librevenge::RVNG_SEEK_SET) != 0 || input->isEnd())
1062 return false;
1063
1064 if (input->seek(12,librevenge::RVNG_SEEK_SET) != 0) return false;
1065
1066 for (int i = 0; i < 3; i++) {
1067 val = static_cast<int>(input->readLong(1));
1068 if (val < -10 || val > 10) {
1069 MWAW_DEBUG_MSG(("MsWksDocument::checkHeader3: find odd val%d=0x%x: not implemented\n", i, static_cast<unsigned int>(val)));
1070 numError++;
1071 }
1072 }
1073 input->seek(1,librevenge::RVNG_SEEK_CUR);
1074 auto type = static_cast<int>(input->readLong(2));
1075 switch (type) {
1076 // Text document
1077 case 1:
1078 break;
1079 case 2:
1080 m_state->m_kind = MWAWDocument::MWAW_K_DATABASE;
1081 break;
1082 case 3:
1083 m_state->m_kind = MWAWDocument::MWAW_K_SPREADSHEET;
1084 break;
1085 case 12:
1086 m_state->m_kind = MWAWDocument::MWAW_K_DRAW;
1087 break;
1088 default:
1089 MWAW_DEBUG_MSG(("MsWksDocument::checkHeader3: find odd type=%d: not implemented\n", type));
1090 return false;
1091 }
1092
1093 if (version() < 1 || version() > 4)
1094 return false;
1095
1096 //
1097 input->seek(0,librevenge::RVNG_SEEK_SET);
1098 f << "FileHeader: ";
1099 f << "version= " << input->readULong(4);
1100 long dim[4];
1101 for (auto &d : dim) d = input->readLong(2);
1102 bool checkDatabaseFileHSize=false;
1103 if (dim[2] <= dim[0] || dim[3] <= dim[1]) {
1104 // bdbox(0,0,0,0) can appear if there is no document info, so do not consider this an error for database file
1105 if (dim[0]==0 && dim[1]==0 && dim[2]==0 && dim[3]==0 && m_state->m_kind == MWAWDocument::MWAW_K_DATABASE) {
1106 checkDatabaseFileHSize=true;
1107 }
1108 else {
1109 MWAW_DEBUG_MSG(("MsWksDocument::checkHeader3: find odd bdbox\n"));
1110 numError++;
1111 }
1112 }
1113 f << ", windowdbdbox?=(";
1114 for (auto d : dim) f << d <<",";
1115 f << "),";
1116 auto fileHeaderSize=long(input->readULong(4));
1117 if (checkDatabaseFileHSize && strict) {
1118 if ((version()==2 && fileHeaderSize!=0x516) || (version()==3&&fileHeaderSize!=0x50))
1119 return false;
1120 }
1121 if (fileHeaderSize)
1122 f << "headerSize=" << std::hex << fileHeaderSize << std::dec << ",";
1123
1124 if (m_state->m_kind==MWAWDocument::MWAW_K_SPREADSHEET) {
1125 /* CHECKME: normally 0x56c, but find one time 0x56a in a v2 file
1126 which seems to imply that the spreadsheet begins earlier */
1127 if (fileHeaderSize!=0x56c && fileHeaderSize > long(0x550) && input->checkPosition(fileHeaderSize+0x20))
1128 m_state->m_fileHeaderSize=fileHeaderSize;
1129 else
1130 m_state->m_fileHeaderSize=0x56c;
1131 }
1132 type = static_cast<int>(input->readULong(2));
1133 f << std::dec;
1134 switch (type) {
1135 case 1:
1136 f << "doc,";
1137 break;
1138 case 2:
1139 f << "database,";
1140 break; // with ##v3=50
1141 case 3:
1142 f << "spreadsheet,";
1143 break; // with ##v2=5,##v3=6c
1144 case 12:
1145 f << "draw,";
1146 break;
1147 default:
1148 f << "###type=" << type << ",";
1149 break;
1150 }
1151 f << "numlines?=" << input->readLong(2) << ",";
1152 val = static_cast<int>(input->readLong(1)); // 0, v2: 0, 4 or -4
1153 if (val) f << "f0=" << val << ",";
1154 val = static_cast<int>(input->readLong(1)); // almost always 1
1155 if (val != 1) f << "f1=" << val << ",";
1156 for (int i = 11; i < headerSize/2; i++) { // v1: 0, 0, v2: 0, 0|1
1157 val = static_cast<int>(input->readULong(2));
1158 if (!val) continue;
1159 f << "f" << i << "=" << std::hex << val << std::dec;
1160 if (version() >= 3 && i == 12) {
1161 if (val & 0x100) {
1162 m_state->m_hasHeader = true;
1163 f << "(Head)";
1164 }
1165 if (val & 0x200) {
1166 m_state->m_hasFooter = true;
1167 f << "(Foot)";
1168 }
1169 }
1170 f << ",";
1171 }
1172
1173 if (header)
1174 header->reset(MWAWDocument::MWAW_T_MICROSOFTWORKS, version(), m_state->m_kind);
1175
1176 ascii().addPos(0);
1177 ascii().addNote(f.str().c_str());
1178 ascii().addPos(headerSize);
1179
1180 input->seek(headerSize,librevenge::RVNG_SEEK_SET);
1181 return strict ? (numError==0) : (numError < 3);
1182 }
1183
1184 ////////////////////////////////////////////////////////////
1185 // spreadsheet/database function
1186 ////////////////////////////////////////////////////////////
readDBString(long endPos,std::string & res)1187 bool MsWksDocument::readDBString(long endPos, std::string &res)
1188 {
1189 MWAWInputStreamPtr input=getInput();
1190 res = "";
1191 int error = 0;
1192 int ok = 0;
1193 while (!input->isEnd() && input->tell() < endPos) {
1194 auto c = char(input->readLong(1));
1195 if (c < 27 && c != '\t' && c != '\n') error++;
1196 else ok++;
1197 res += c;
1198 }
1199 return ok >= error;
1200 }
1201
readDBNumber(long endPos,double & res,bool & isNan,std::string & str)1202 bool MsWksDocument::readDBNumber(long endPos, double &res, bool &isNan, std::string &str)
1203 {
1204 MWAWInputStreamPtr input=getInput();
1205 res = 0;
1206 str="";
1207 long pos = input->tell();
1208 if (endPos > pos+10 && !readDBString(endPos-10,str)) return false;
1209 return input->tell() == endPos-10 && input->readDouble10(res,isNan);
1210 }
1211
readCellInFormula(MWAWCellContent::FormulaInstruction & instr,bool is2D)1212 bool MsWksDocument::readCellInFormula(MWAWCellContent::FormulaInstruction &instr, bool is2D)
1213 {
1214 MWAWInputStreamPtr input=getInput();
1215 instr=MWAWCellContent::FormulaInstruction();
1216 instr.m_type=MWAWCellContent::FormulaInstruction::F_Cell;
1217 bool ok = true;
1218 if (is2D) {
1219 bool absolute[2] = { false, false};
1220 auto type = static_cast<int>(input->readULong(1));
1221 if (type & 0x80) {
1222 absolute[0] = true;
1223 type &= 0x7F;
1224 }
1225 if (type & 0x40) {
1226 absolute[1] = true;
1227 type &= 0xBF;
1228 }
1229 if (type) {
1230 MWAW_DEBUG_MSG(("MSWksSSParser::readCellInFormula:Pb find fl=%d when reading a cell\n", type));
1231 ok = false;
1232 }
1233 int pos[2]= {1,0};
1234 for (auto &p : pos) p = static_cast<int>(input->readULong(1));
1235
1236 if (pos[0] < 1 || pos[1] < 0) {
1237 if (ok) {
1238 MWAW_DEBUG_MSG(("MSWksSSParser::readCellInFormula: can not read cell position\n"));
1239 }
1240 return false;
1241 }
1242 instr.m_position[0]=MWAWVec2i(pos[1],pos[0]-1);
1243 instr.m_positionRelative[0]=MWAWVec2b(!absolute[1],!absolute[0]);
1244 }
1245 else
1246 instr.m_position[0]=MWAWVec2i(static_cast<int>(input->readULong(1)),0);
1247 return ok;
1248 }
1249
readFormula(long endPos,MWAWCellContent & content,std::string & extra)1250 bool MsWksDocument::readFormula(long endPos, MWAWCellContent &content, std::string &extra)
1251 {
1252 MWAWInputStreamPtr input=getInput();
1253 long pos = input->tell();
1254 extra="";
1255 if (pos == endPos) return false;
1256 if (!input->checkPosition(endPos)) {
1257 MWAW_DEBUG_MSG(("MsWksDocument::readFormula: bad endPos\n"));
1258 extra="###endPos";
1259 return false;
1260 }
1261
1262 std::stringstream f;
1263 auto &formula=content.m_formula;
1264 bool is2D=m_parserState->m_kind!=MWAWDocument::MWAW_K_DATABASE;
1265 bool ok=true;
1266 while (input->tell() < endPos && !input->isEnd()) {
1267 pos = input->tell();
1268 auto code = static_cast<int>(input->readLong(1));
1269 MWAWCellContent::FormulaInstruction instr;
1270 bool findEnd=false;
1271 switch (code) {
1272 case 0x0:
1273 case 0x2:
1274 case 0x4:
1275 case 0x6: {
1276 static char const *wh[]= {"+","-","*","/"};
1277 instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
1278 instr.m_content=wh[code/2];
1279 break;
1280 }
1281 case 0x8: { // a number
1282 auto sz = static_cast<int>(input->readULong(1));
1283 std::string s;
1284 double val;
1285 bool isNan;
1286 if (pos+sz+12 <= endPos && readDBNumber((pos+2)+sz+10, val, isNan, s)) {
1287 instr.m_type=MWAWCellContent::FormulaInstruction::F_Double;
1288 instr.m_doubleValue=val;
1289 break;
1290 }
1291 f << "###number" << s;
1292 ok=false;
1293 break;
1294 }
1295 case 0x0a: { // a cell
1296 if (!readCellInFormula(instr, is2D))
1297 f << "#";
1298 break;
1299 }
1300 case 0x0c: { // function
1301 auto v = static_cast<int>(input->readULong(1));
1302 static char const *(listFunc) [0x41] = {
1303 "Abs", "Sum", "Na", "Error", "ACos", "And", "ASin", "ATan",
1304 "ATan2", "Average", "Choose", "Cos", "Count", "Exp", "False", "FV",
1305 "HLookup", "If", "Index", "Int", "IRR", "IsBlank", "IsError", "IsNa",
1306 "##Funct[30]", "Ln", "Lookup", "Log10", "Max", "Min", "Mod", "Not",
1307 "NPer", "NPV", "Or", "Pi", "Pmt", "PV", "Rand", "Round",
1308 "Sign", "Sin", "Sqrt", "StDev", "Tan", "True", "Var", "VLookup",
1309 "Match", "MIRR", "Rate", "Type", "Radians", "Degrees", "Sum" /*"SSum: checkme"*/, "Date",
1310 "Day", "Hour", "Minute", "Month", "Now", "Second", "Time", "Weekday",
1311 "Year"
1312 };
1313 instr.m_type=MWAWCellContent::FormulaInstruction::F_Function;
1314 if ((v%2) == 0 && v >= 0 && v/2 <= 0x40)
1315 instr.m_content=listFunc[v/2];
1316 else {
1317 f << "###";
1318 MWAW_DEBUG_MSG(("MSWksSSParser::readFormula: find unknown function %x\n", static_cast<unsigned int>(v)));
1319 std::stringstream s;
1320 s << "Funct" << std::hex << v << std::dec;
1321 instr.m_content=s.str();
1322 }
1323 break;
1324 }
1325 case 0x0e: { // list of cell
1326 MWAWCellContent::FormulaInstruction instr2;
1327 if (endPos-pos< (is2D ? 9 : 5)) {
1328 f << "###list cell short";
1329 ok = false;
1330 break;
1331 }
1332 if (!readCellInFormula(instr, is2D) || !readCellInFormula(instr2, is2D))
1333 f << "#";
1334 instr.m_type=MWAWCellContent::FormulaInstruction::F_CellList;
1335 instr.m_position[1]=instr2.m_position[0];
1336 instr.m_positionRelative[1]=instr2.m_positionRelative[0];
1337 break;
1338 }
1339 case 0x16:
1340 findEnd=true;
1341 input->seek(-1, librevenge::RVNG_SEEK_CUR);
1342 f << ",";
1343 break;
1344 default:
1345 if ((code%2)==0 && code>=0x10 && code<=0x22) {
1346 static char const *wh[]= {"(", ")", ";", "end", "<", ">", "=", "<=", ">=", "<>" };
1347 instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
1348 instr.m_content=wh[(code-0x10)/2];
1349 break;
1350 }
1351 f << "###" << std::hex << code << std::dec;
1352 ok = false;
1353 break;
1354 }
1355 if (!ok || findEnd)
1356 break;
1357 f << instr;
1358 formula.push_back(instr);
1359 }
1360 extra=f.str();
1361 pos = input->tell();
1362 if (endPos - pos < 21)
1363 return ok;
1364 // test if we have the value
1365 if (input->readLong(1) != 0x16) {
1366 input->seek(-1, librevenge::RVNG_SEEK_CUR);
1367 return true;
1368 }
1369
1370 f.str("");
1371 f << std::dec << "unk1=[";
1372 // looks a little as a zone of cell ?? but this seems eroneous
1373 for (int i = 0; i < 2; i++) {
1374 auto v = static_cast<int>(input->readULong(1));
1375
1376 auto n0 = static_cast<int>(input->readULong(1));
1377 auto n1 = static_cast<int>(input->readULong(1));
1378 if (i == 1) f << ":";
1379 f << n0 << "x" << n1;
1380 if (v) f << "##v";
1381 }
1382 f << std::hex << "],unk2=["; // 0, followed by a small number between 1 and 140
1383 for (int i = 0; i < 2; i++)
1384 f << input->readULong(2) << ",";
1385 f << "]";
1386
1387 // the value
1388 double value;
1389 bool isNan;
1390 std::string res;
1391 if (!readDBNumber(endPos, value, isNan, res)) {
1392 MWAW_DEBUG_MSG(("MsWksDocument::readFormula: can not read val number\n"));
1393 input->seek(pos, librevenge::RVNG_SEEK_SET);
1394 f << ",###";
1395 }
1396 else {
1397 content.setValue(value);
1398 f << ":" << value << ",";
1399 }
1400 extra += f.str();
1401 return true;
1402 }
1403
1404 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
1405