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