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 <cstdlib>
35 #include <cstring>
36 #include <iostream>
37 #include <map>
38 #include <sstream>
39 #include <string>
40 
41 #include <librevenge/librevenge.h>
42 
43 #include "MWAWFontConverter.hxx"
44 #include "MWAWPosition.hxx"
45 #include "MWAWOLEParser.hxx"
46 #include "MWAWPictMac.hxx"
47 
48 //////////////////////////////////////////////////
49 // internal structure
50 //////////////////////////////////////////////////
51 /** Low level: namespace used to define/store the data used by MWAWOLEParser */
52 namespace MWAWOLEParserInternal
53 {
54 /** Internal: internal method to compobj definition */
55 class CompObj
56 {
57 public:
58   //! the constructor
CompObj()59   CompObj()
60     : m_mapCls()
61   {
62     initCLSMap();
63   }
64 
65   /** return the CLS Name corresponding to an identifier */
getCLSName(unsigned long v)66   char const *getCLSName(unsigned long v)
67   {
68     if (m_mapCls.find(v) == m_mapCls.end()) return nullptr;
69     return m_mapCls[v];
70   }
71 
72 protected:
73   /** map CLSId <-> name */
74   std::map<unsigned long, char const *> m_mapCls;
75 
76   /** initialise a map CLSId <-> name */
initCLSMap()77   void initCLSMap()
78   {
79     // source: binfilter/bf_so3/source/inplace/embobj.cxx
80     m_mapCls[0x00000319]="Picture"; // addon Enhanced Metafile ( find in some file)
81 
82     m_mapCls[0x000212F0]="MSWordArt"; // or MSWordArt.2
83     m_mapCls[0x00021302]="MSWorksWPDoc"; // addon
84 
85     // MS Apps
86     m_mapCls[0x00030000]= "ExcelWorksheet";
87     m_mapCls[0x00030001]= "ExcelChart";
88     m_mapCls[0x00030002]= "ExcelMacrosheet";
89     m_mapCls[0x00030003]= "WordDocument";
90     m_mapCls[0x00030004]= "MSPowerPoint";
91     m_mapCls[0x00030005]= "MSPowerPointSho";
92     m_mapCls[0x00030006]= "MSGraph";
93     m_mapCls[0x00030007]= "MSDraw"; // find also with ca003 ?
94     m_mapCls[0x00030008]= "Note-It";
95     m_mapCls[0x00030009]= "WordArt";
96     m_mapCls[0x0003000a]= "PBrush";
97     m_mapCls[0x0003000b]= "Equation"; // "Microsoft Equation Editor"
98     m_mapCls[0x0003000c]= "Package";
99     m_mapCls[0x0003000d]= "SoundRec";
100     m_mapCls[0x0003000e]= "MPlayer";
101     // MS Demos
102     m_mapCls[0x0003000f]= "ServerDemo"; // "OLE 1.0 Server Demo"
103     m_mapCls[0x00030010]= "Srtest"; // "OLE 1.0 Test Demo"
104     m_mapCls[0x00030011]= "SrtInv"; //  "OLE 1.0 Inv Demo"
105     m_mapCls[0x00030012]= "OleDemo"; //"OLE 1.0 Demo"
106 
107     // Coromandel / Dorai Swamy / 718-793-7963
108     m_mapCls[0x00030013]= "CoromandelIntegra";
109     m_mapCls[0x00030014]= "CoromandelObjServer";
110 
111     // 3-d Visions Corp / Peter Hirsch / 310-325-1339
112     m_mapCls[0x00030015]= "StanfordGraphics";
113 
114     // Deltapoint / Nigel Hearne / 408-648-4000
115     m_mapCls[0x00030016]= "DGraphCHART";
116     m_mapCls[0x00030017]= "DGraphDATA";
117 
118     // Corel / Richard V. Woodend / 613-728-8200 x1153
119     m_mapCls[0x00030018]= "PhotoPaint"; // "Corel PhotoPaint"
120     m_mapCls[0x00030019]= "CShow"; // "Corel Show"
121     m_mapCls[0x0003001a]= "CorelChart";
122     m_mapCls[0x0003001b]= "CDraw"; // "Corel Draw"
123 
124     // Inset Systems / Mark Skiba / 203-740-2400
125     m_mapCls[0x0003001c]= "HJWIN1.0"; // "Inset Systems"
126 
127     // Mark V Systems / Mark McGraw / 818-995-7671
128     m_mapCls[0x0003001d]= "ObjMakerOLE"; // "MarkV Systems Object Maker"
129 
130     // IdentiTech / Mike Gilger / 407-951-9503
131     m_mapCls[0x0003001e]= "FYI"; // "IdentiTech FYI"
132     m_mapCls[0x0003001f]= "FYIView"; // "IdentiTech FYI Viewer"
133 
134     // Inventa Corporation / Balaji Varadarajan / 408-987-0220
135     m_mapCls[0x00030020]= "Stickynote";
136 
137     // ShapeWare Corp. / Lori Pearce / 206-467-6723
138     m_mapCls[0x00030021]= "ShapewareVISIO10";
139     m_mapCls[0x00030022]= "ImportServer"; // "Spaheware Import Server"
140 
141     // test app SrTest
142     m_mapCls[0x00030023]= "SrvrTest"; // "OLE 1.0 Server Test"
143 
144     // test app ClTest.  Doesn't really work as a server but is in reg db
145     m_mapCls[0x00030025]= "Cltest"; // "OLE 1.0 Client Test"
146 
147     // Microsoft ClipArt Gallery   Sherry Larsen-Holmes
148     m_mapCls[0x00030026]= "MS_ClipArt_Gallery";
149     // Microsoft Project  Cory Reina
150     m_mapCls[0x00030027]= "MSProject";
151 
152     // Microsoft Works Chart
153     m_mapCls[0x00030028]= "MSWorksChart";
154 
155     // Microsoft Works Spreadsheet
156     m_mapCls[0x00030029]= "MSWorksSpreadsheet";
157 
158     // AFX apps - Dean McCrory
159     m_mapCls[0x0003002A]= "MinSvr"; // "AFX Mini Server"
160     m_mapCls[0x0003002B]= "HierarchyList"; // "AFX Hierarchy List"
161     m_mapCls[0x0003002C]= "BibRef"; // "AFX BibRef"
162     m_mapCls[0x0003002D]= "MinSvrMI"; // "AFX Mini Server MI"
163     m_mapCls[0x0003002E]= "TestServ"; // "AFX Test Server"
164 
165     // Ami Pro
166     m_mapCls[0x0003002F]= "AmiProDocument";
167 
168     // WordPerfect Presentations For Windows
169     m_mapCls[0x00030030]= "WPGraphics";
170     m_mapCls[0x00030031]= "WPCharts";
171 
172     // MicroGrafx Charisma
173     m_mapCls[0x00030032]= "Charisma";
174     m_mapCls[0x00030033]= "Charisma_30"; // v 3.0
175     m_mapCls[0x00030034]= "CharPres_30"; // v 3.0 Pres
176     // MicroGrafx Draw
177     m_mapCls[0x00030035]= "Draw"; //"MicroGrafx Draw"
178     // MicroGrafx Designer
179     m_mapCls[0x00030036]= "Designer_40"; // "MicroGrafx Designer 4.0"
180 
181     // STAR DIVISION
182     //m_mapCls[0x000424CA]= "StarMath"; // "StarMath 1.0"
183     m_mapCls[0x00043AD2]= "FontWork"; // "Star FontWork"
184     //m_mapCls[0x000456EE]= "StarMath2"; // "StarMath 2.0"
185   }
186 };
187 
188 /** Internal: internal method to keep ole definition */
189 struct OleDef {
OleDefMWAWOLEParserInternal::OleDef190   OleDef()
191     : m_id(-1)
192     , m_subId(-1)
193     , m_dir("")
194     , m_base("")
195     , m_name("")
196   {
197   }
198   int m_id /**main id*/, m_subId /**subsversion id */ ;
199   std::string m_dir/**the directory*/, m_base/**the base*/, m_name/**the complete name*/;
200 };
201 
202 /** Internal: internal state of a MWAWOLEParser */
203 struct State {
204   /** constructor */
StateMWAWOLEParserInternal::State205   State(MWAWFontConverterPtr const &fontConverter, int fId)
206     : m_fontConverter(fontConverter)
207     , m_fontId(fId)
208     , m_encoding(-1)
209     , m_metaData()
210     , m_unknownOLEs()
211     , m_objects()
212     , m_objectsPosition()
213     , m_objectsId()
214     , m_objectsType()
215     , m_compObjIdName()
216   {
217   }
218   //! the font converter
219   MWAWFontConverterPtr m_fontConverter;
220   //! the font id used to decode string
221   int m_fontId;
222   //! the font encoding
223   int m_encoding;
224   //! the meta data
225   librevenge::RVNGPropertyList m_metaData;
226   //! list of ole which can not be parsed
227   std::vector<std::string> m_unknownOLEs;
228 
229   //! list of pictures read
230   std::vector<librevenge::RVNGBinaryData> m_objects;
231   //! list of picture size ( if known)
232   std::vector<MWAWPosition> m_objectsPosition;
233   //! list of pictures id
234   std::vector<int> m_objectsId;
235   //! list of picture type
236   std::vector<std::string> m_objectsType;
237 
238   //! a smart ptr used to stored the list of compobj id->name
239   std::shared_ptr<MWAWOLEParserInternal::CompObj> m_compObjIdName;
240 };
241 }
242 
243 // constructor/destructor
MWAWOLEParser(std::string const & mainName,MWAWFontConverterPtr const & fontConverter,int fId)244 MWAWOLEParser::MWAWOLEParser(std::string const &mainName, MWAWFontConverterPtr const &fontConverter, int fId)
245   : m_avoidOLE(mainName)
246   , m_state(new MWAWOLEParserInternal::State(fontConverter, fId))
247 {
248 }
249 
~MWAWOLEParser()250 MWAWOLEParser::~MWAWOLEParser()
251 {
252 }
253 
getFontEncoding() const254 int MWAWOLEParser::getFontEncoding() const
255 {
256   return m_state->m_encoding;
257 }
258 
updateMetaData(librevenge::RVNGPropertyList & metaData) const259 void MWAWOLEParser::updateMetaData(librevenge::RVNGPropertyList &metaData) const
260 {
261   librevenge::RVNGPropertyList::Iter i(m_state->m_metaData);
262   for (i.rewind(); i.next();) {
263     if (!metaData[i.key()])
264       metaData.insert(i.key(),i()->clone());
265   }
266 }
267 
getNotParse() const268 std::vector<std::string> const &MWAWOLEParser::getNotParse() const
269 {
270   return m_state->m_unknownOLEs;
271 }
272 
getObjectsId() const273 std::vector<int> const &MWAWOLEParser::getObjectsId() const
274 {
275   return m_state->m_objectsId;
276 }
277 
getObjectsPosition() const278 std::vector<MWAWPosition> const &MWAWOLEParser::getObjectsPosition() const
279 {
280   return m_state->m_objectsPosition;
281 }
282 
getObjects() const283 std::vector<librevenge::RVNGBinaryData> const &MWAWOLEParser::getObjects() const
284 {
285   return m_state->m_objects;
286 }
287 
getObjectsType() const288 std::vector<std::string> const &MWAWOLEParser::getObjectsType() const
289 {
290   return m_state->m_objectsType;
291 }
292 
getObject(int id,librevenge::RVNGBinaryData & obj,MWAWPosition & pos,std::string & type) const293 bool MWAWOLEParser::getObject(int id, librevenge::RVNGBinaryData &obj, MWAWPosition &pos, std::string &type)  const
294 {
295   for (size_t i = 0; i < m_state->m_objectsId.size(); i++) {
296     if (m_state->m_objectsId[i] != id) continue;
297     obj = m_state->m_objects[i];
298     pos = m_state->m_objectsPosition[i];
299     type = m_state->m_objectsType[i];
300     return true;
301   }
302   obj.clear();
303   return false;
304 }
305 
setObject(int id,librevenge::RVNGBinaryData const & obj,MWAWPosition const & pos,std::string const & type)306 void MWAWOLEParser::setObject(int id, librevenge::RVNGBinaryData const &obj, MWAWPosition const &pos,
307                               std::string const &type)
308 {
309   for (size_t i = 0; i < m_state->m_objectsId.size(); i++) {
310     if (m_state->m_objectsId[i] != id) continue;
311     m_state->m_objects[i] = obj;
312     m_state->m_objectsPosition[i] = pos;
313     m_state->m_objectsType[i] = type;
314     return;
315   }
316   m_state->m_objects.push_back(obj);
317   m_state->m_objectsPosition.push_back(pos);
318   m_state->m_objectsId.push_back(id);
319   m_state->m_objectsType.push_back(type);
320 }
321 
322 // parsing
parse(MWAWInputStreamPtr file)323 bool MWAWOLEParser::parse(MWAWInputStreamPtr file)
324 {
325   if (!m_state->m_compObjIdName)
326     m_state->m_compObjIdName.reset(new MWAWOLEParserInternal::CompObj);
327 
328   m_state->m_unknownOLEs.resize(0);
329   m_state->m_objects.resize(0);
330   m_state->m_objectsId.resize(0);
331   m_state->m_objectsType.resize(0);
332 
333   if (!file.get()) return false;
334 
335   if (!file->isStructured()) return false;
336 
337   unsigned numSubStreams = file->subStreamCount();
338   //
339   // we begin by grouping the Ole by their potential main id
340   //
341   std::multimap<int, MWAWOLEParserInternal::OleDef> listsById;
342   std::vector<int> listIds;
343   for (unsigned i = 0; i < numSubStreams; ++i) {
344     std::string const &name = file->subStreamName(i);
345     if (name.empty() || name[name.length()-1]=='/') continue;
346 
347     // separated the directory and the name
348     //    MatOST/MatadorObject1/Ole10Native
349     //      -> dir="MatOST/MatadorObject1", base="Ole10Native"
350     auto pos = name.find_last_of('/');
351 
352     std::string dir, base;
353     if (pos == std::string::npos) base = name;
354     else if (pos == 0) base = name.substr(1);
355     else {
356       dir = name.substr(0,pos);
357       base = name.substr(pos+1);
358     }
359     if (dir == "" && base == m_avoidOLE) continue;
360 
361 #define PRINT_OLE_NAME
362 #if defined(PRINT_OLE_NAME)
363     MWAW_DEBUG_MSG(("OLEName=%s\n", name.c_str()));
364 #endif
365     MWAWOLEParserInternal::OleDef data;
366     data.m_name = name;
367     data.m_dir = dir;
368     data.m_base = base;
369 
370     // try to retrieve the identificator stored in the directory
371     //  MatOST/MatadorObject1/ -> 1, -1
372     //  Object 2/ -> 2, -1
373     dir+='/';
374     pos = dir.find('/');
375     int id[2] = { -1, -1};
376     while (pos != std::string::npos) {
377       if (pos >= 1 && dir[pos-1] >= '0' && dir[pos-1] <= '9') {
378         auto idP = pos-1;
379         while (idP >=1 && dir[idP-1] >= '0' && dir[idP-1] <= '9')
380           idP--;
381         int val = std::atoi(dir.substr(idP, idP-pos).c_str());
382         if (id[0] == -1) id[0] = val;
383         else {
384           id[1] = val;
385           break;
386         }
387       }
388       pos = dir.find('/', pos+1);
389     }
390     data.m_id = id[0];
391     data.m_subId = id[1];
392     if (listsById.find(data.m_id) == listsById.end())
393       listIds.push_back(data.m_id);
394     listsById.insert(std::multimap<int, MWAWOLEParserInternal::OleDef>::value_type(data.m_id, data));
395   }
396 
397   for (auto id : listIds) {
398     auto pos = listsById.lower_bound(id);
399 
400     // try to find a representation for each id
401     // FIXME: maybe we must also find some for each subid
402     librevenge::RVNGBinaryData pict;
403     int confidence = -1000;
404     MWAWPosition actualPos, potentialSize;
405     bool isPict = false;
406 
407     while (pos != listsById.end()) {
408       auto const &dOle = pos->second;
409       if (pos++->first != id) break;
410 
411       MWAWInputStreamPtr ole = file->getSubStreamByName(dOle.m_name);
412       if (!ole.get()) {
413         MWAW_DEBUG_MSG(("MWAWOLEParser: error: can not find OLE part: \"%s\"\n", dOle.m_name.c_str()));
414         continue;
415       }
416       libmwaw::DebugFile asciiFile(ole);
417       asciiFile.open(dOle.m_name);
418 
419       librevenge::RVNGBinaryData data;
420       bool hasData = false;
421       int newConfidence = -2000;
422       bool ok = true;
423       MWAWPosition pictPos;
424 
425       if (strncmp("Ole", dOle.m_base.c_str(), 3) == 0 ||
426           strncmp("CompObj", dOle.m_base.c_str(), 7) == 0)
427         ole->setReadInverted(true);
428 
429       try {
430         librevenge::RVNGPropertyList pList;
431         int encoding=m_state->m_encoding;
432         bool isMainOle=dOle.m_dir.empty();
433         if (readMM(ole, dOle.m_base, asciiFile));
434         else if (readSummaryInformation(ole, dOle.m_base, isMainOle ? m_state->m_encoding : encoding, isMainOle ? m_state->m_metaData : pList, asciiFile)) {
435           if (isMainOle && m_state->m_encoding!=encoding && m_state->m_fontConverter &&
436               m_state->m_encoding>=1250 && m_state->m_encoding<=1258) {
437             std::stringstream s;
438             s << "CP" << m_state->m_encoding;
439             m_state->m_fontId=m_state->m_fontConverter->getId(s.str().c_str());
440           }
441         }
442         else if (readObjInfo(ole, dOle.m_base, asciiFile));
443         else if (readOle(ole, dOle.m_base, asciiFile));
444         else if (isOlePres(ole, dOle.m_base) &&
445                  readOlePres(ole, data, pictPos, asciiFile)) {
446           hasData = true;
447           newConfidence = 2;
448         }
449         else if (isOle10Native(ole, dOle.m_base) &&
450                  readOle10Native(ole, data, asciiFile)) {
451           hasData = true;
452           // small size can be a symptom that this is a link to a
453           // basic msworks data file, so we reduce confidence
454           newConfidence = data.size() > 1000 ? 4 : 2;
455         }
456         else if (readCompObj(ole, dOle.m_base, asciiFile));
457         else if (readContents(ole, dOle.m_base, data, pictPos, asciiFile)) {
458           hasData = true;
459           newConfidence = 3;
460         }
461         else if (readCONTENTS(ole, dOle.m_base, data, pictPos, asciiFile)) {
462           hasData = true;
463           newConfidence = 3;
464         }
465         else
466           ok = false;
467       }
468       catch (...) {
469         ok = false;
470       }
471       if (!ok) {
472         m_state->m_unknownOLEs.push_back(dOle.m_name);
473         asciiFile.reset();
474         continue;
475       }
476 
477       /** first check if this is a mac pict as other oles
478           may not be understand by openOffice, ... */
479       if (data.size()) {
480         MWAWInputStreamPtr dataInput=MWAWInputStream::get(data, false);
481         if (dataInput) {
482           dataInput->seek(0, librevenge::RVNG_SEEK_SET);
483           MWAWBox2f box;
484           if (MWAWPictData::check(dataInput, static_cast<int>(data.size()), box) != MWAWPict::MWAW_R_BAD) {
485             isPict = true;
486             newConfidence = 100;
487           }
488         }
489       }
490 
491       if (hasData && data.size()) {
492         // probably only a subs data
493         if (dOle.m_subId != -1) newConfidence -= 10;
494 
495         if (newConfidence > confidence ||
496             (newConfidence == confidence && pict.size() < data.size())) {
497           confidence = newConfidence;
498           pict = data;
499           actualPos = pictPos;
500         }
501 
502         if (actualPos.naturalSize().x() > 0 && actualPos.naturalSize().y() > 0)
503           potentialSize = actualPos;
504 #ifdef DEBUG_WITH_FILES
505         libmwaw::Debug::dumpFile(data, dOle.m_name.c_str());
506 #endif
507       }
508 
509       asciiFile.reset();
510 
511 #ifndef DEBUG
512       if (confidence >= 3) break;
513 #endif
514     }
515 
516     if (pict.size()) {
517       m_state->m_objects.push_back(pict);
518       if (actualPos.naturalSize().x() <= 0 || actualPos.naturalSize().y() <= 0) {
519         MWAWVec2f size = potentialSize.naturalSize();
520         if (size.x() > 0 && size.y() > 0)
521           actualPos.setNaturalSize(actualPos.getInvUnitScale(potentialSize.unit())*size);
522       }
523       m_state->m_objectsPosition.push_back(actualPos);
524       m_state->m_objectsId.push_back(id);
525       if (isPict)
526         m_state->m_objectsType.push_back("image/pict");
527       else
528         m_state->m_objectsType.push_back("object/ole");
529     }
530   }
531 
532   return true;
533 }
534 
535 
536 
537 ////////////////////////////////////////
538 //
539 // small structure
540 //
541 ////////////////////////////////////////
readOle(MWAWInputStreamPtr ip,std::string const & oleName,libmwaw::DebugFile & ascii)542 bool MWAWOLEParser::readOle(MWAWInputStreamPtr ip, std::string const &oleName,
543                             libmwaw::DebugFile &ascii)
544 {
545   if (!ip.get()) return false;
546 
547   if (oleName!="Ole") return false;
548 
549   if (ip->seek(20, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 20) return false;
550 
551   ip->seek(0, librevenge::RVNG_SEEK_SET);
552 
553   int val[20];
554   for (int &i : val) {
555     i = static_cast<int>(ip->readLong(1));
556     if (i < -10 || i > 10) return false;
557   }
558 
559   libmwaw::DebugStream f;
560   f << "@@Ole: ";
561   // always 1, 0, 2, 0*
562   for (int i = 0; i < 20; i++)
563     if (val[i]) f << "f" << i << "=" << val[i] << ",";
564   ascii.addPos(0);
565   ascii.addNote(f.str().c_str());
566 
567   if (!ip->isEnd()) {
568     ascii.addPos(20);
569     ascii.addNote("@@Ole:###");
570   }
571 
572   return true;
573 }
574 
readObjInfo(MWAWInputStreamPtr input,std::string const & oleName,libmwaw::DebugFile & ascii)575 bool MWAWOLEParser::readObjInfo(MWAWInputStreamPtr input, std::string const &oleName,
576                                 libmwaw::DebugFile &ascii)
577 {
578   if (oleName!="ObjInfo") return false;
579 
580   input->seek(14, librevenge::RVNG_SEEK_SET);
581   if (input->tell() != 6 || !input->isEnd()) return false;
582 
583   input->seek(0, librevenge::RVNG_SEEK_SET);
584   libmwaw::DebugStream f;
585   f << "@@ObjInfo:";
586 
587   // always 0, 3, 4 ?
588   for (int i = 0; i < 3; i++) f << input->readLong(2) << ",";
589 
590   ascii.addPos(0);
591   ascii.addNote(f.str().c_str());
592 
593   return true;
594 }
595 
readMM(MWAWInputStreamPtr input,std::string const & oleName,libmwaw::DebugFile & ascii)596 bool MWAWOLEParser::readMM(MWAWInputStreamPtr input, std::string const &oleName,
597                            libmwaw::DebugFile &ascii)
598 {
599   if (oleName!="MM") return false;
600 
601   input->seek(14, librevenge::RVNG_SEEK_SET);
602   if (input->tell() != 14 || !input->isEnd()) return false;
603 
604   input->seek(0, librevenge::RVNG_SEEK_SET);
605   auto entete = static_cast<int>(input->readULong(2));
606   if (entete != 0x444e) {
607     if (entete == 0x4e44) {
608       MWAW_DEBUG_MSG(("MWAWOLEParser::readMM: ERROR: endian mode probably bad, potentially bad PC/Mac mode detection.\n"));
609     }
610     return false;
611   }
612   libmwaw::DebugStream f;
613   f << "@@MM:";
614 
615   int val[6];
616   for (auto &v : val) v = static_cast<int>(input->readLong(2));
617 
618   switch (val[5]) {
619   case 0:
620     f << "conversion,";
621     break;
622   case 2:
623     f << "Works3,";
624     break;
625   case 4:
626     f << "Works4,";
627     break;
628   default:
629     f << "version=unknown,";
630     break;
631   }
632 
633   // 1, 0, 0, 0, 0 : Mac file
634   // 0, 1, 0, [0,1,2,4,6], 0 : Pc file
635   // Note: No field seems to code the document type
636   bool macFile = input->readInverted() == false;
637   int normalMod = macFile ? 0:1;
638 
639   for (int i = 0; i < 5; i++) {
640     if ((i%2)!=normalMod && val[i]) f << "###";
641     f << val[i] << ",";
642   }
643 
644   ascii.addPos(0);
645   ascii.addNote(f.str().c_str());
646 
647   if (macFile) input->setReadInverted(true);
648   return true;
649 }
650 
651 
readCompObj(MWAWInputStreamPtr ip,std::string const & oleName,libmwaw::DebugFile & ascii)652 bool MWAWOLEParser::readCompObj(MWAWInputStreamPtr ip, std::string const &oleName, libmwaw::DebugFile &ascii)
653 {
654   if (strncmp(oleName.c_str(), "CompObj", 7) != 0) return false;
655 
656   // check minimal size
657   const int minSize = 12 + 14+ 16 + 12; // size of header, clsid, footer, 3 string size
658   if (ip->seek(minSize,librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != minSize) return false;
659 
660   libmwaw::DebugStream f;
661   f << "@@CompObj(Header): ";
662   ip->seek(0,librevenge::RVNG_SEEK_SET);
663 
664   for (int i = 0; i < 6; i++) {
665     auto val = static_cast<int>(ip->readLong(2));
666     f << val << ", ";
667   }
668 
669   ascii.addPos(0);
670   ascii.addNote(f.str().c_str());
671 
672   ascii.addPos(12);
673   // the clsid
674   unsigned long clsData[4]; // ushort n1, n2, n3, b8, ... b15
675   for (auto &data : clsData) data = ip->readULong(4);
676 
677   f.str("");
678   f << "@@CompObj(CLSID):";
679   if (clsData[1] == 0 && clsData[2] == 0xC0 && clsData[3] == 0x46000000L) {
680     // normally, a referenced object
681     char const *clsName = m_state->m_compObjIdName->getCLSName(clsData[0]);
682     if (clsName)
683       f << "'" << clsName << "'";
684     else {
685       MWAW_DEBUG_MSG(("MWAWOLEParser::readCompObj: unknown clsid=%ld\n", long(clsData[0])));
686       f << "unknCLSID='" << std::hex << clsData[0] << "'";
687     }
688   }
689   else {
690     /* I found:
691       c1dbcd28e20ace11a29a00aa004a1a72     for MSWorks.Table
692       c2dbcd28e20ace11a29a00aa004a1a72     for Microsoft Works/MSWorksWPDoc
693       a3bcb394c2bd1b10a18306357c795b37     for Microsoft Drawing 1.01/MSDraw.1.01
694       b25aa40e0a9ed111a40700c04fb932ba     for Quill96 Story Group Class ( basic MSWorks doc?)
695       796827ed8bc9d111a75f00c04fb9667b     for MSWorks4Sheet
696     */
697     f << "data0=(" << std::hex << clsData[0] << "," << clsData[1] << "), "
698       << "data1=(" << clsData[2] << "," << clsData[3] << ")";
699   }
700   ascii.addNote(f.str().c_str());
701   f << std::dec;
702   for (int ch = 0; ch < 3; ch++) {
703     long actPos = ip->tell();
704     long sz = ip->readLong(4);
705     bool waitNumber = sz == -1;
706     if (waitNumber || sz == -2) sz = 4;
707     if (sz < 0 || !ip->checkPosition(actPos+4+sz)) return false;
708 
709     std::string st;
710     if (waitNumber) {
711       f.str("");
712       f << ip->readLong(4) << "[val*]";
713       st = f.str();
714     }
715     else {
716       for (long i = 0; i < sz; i++)
717         st += char(ip->readULong(1));
718     }
719 
720     f.str("");
721     f << "@@CompObj:";
722     switch (ch) {
723     case 0:
724       f << "UserType=";
725       break;
726     case 1:
727       f << "ClipName=";
728       break;
729     case 2:
730       f << "ProgIdName=";
731       break;
732     default:
733       break;
734     }
735     f << st;
736     ascii.addPos(actPos);
737     ascii.addNote(f.str().c_str());
738   }
739 
740   if (ip->isEnd()) return true;
741 
742   long actPos = ip->tell();
743   long nbElt = 4;
744   if (ip->seek(actPos+16,librevenge::RVNG_SEEK_SET) != 0 ||
745       ip->tell() != actPos+16) {
746     if ((ip->tell()-actPos)%4) {
747       f.str("");
748       f << "@@CompObj(Footer):###";
749       ascii.addPos(actPos);
750       ascii.addNote(f.str().c_str());
751       return true;
752     }
753     nbElt = (ip->tell()-actPos)/4;
754   }
755 
756   f.str("");
757   f << "@@CompObj(Footer): " << std::hex;
758   ip->seek(actPos,librevenge::RVNG_SEEK_SET);
759   for (long i = 0; i < nbElt; i++)
760     f << ip->readULong(4) << ",";
761   ascii.addPos(actPos);
762   ascii.addNote(f.str().c_str());
763 
764   ascii.addPos(ip->tell());
765 
766   return true;
767 }
768 
769 //////////////////////////////////////////////////
770 // summary and doc summary
771 //////////////////////////////////////////////////
readSummaryInformation(MWAWInputStreamPtr input,std::string const & oleName,int & encoding,librevenge::RVNGPropertyList & pList,libmwaw::DebugFile & ascii,long endPos) const772 bool MWAWOLEParser::readSummaryInformation(MWAWInputStreamPtr input, std::string const &oleName,
773     int &encoding, librevenge::RVNGPropertyList &pList, libmwaw::DebugFile &ascii,
774     long endPos) const
775 {
776   if (oleName!="SummaryInformation" && oleName!="DocumentSummaryInformation") return false;
777   if (endPos<0) {
778     input->seek(0, librevenge::RVNG_SEEK_SET);
779     endPos=input->size();
780   }
781   long pos=input->tell();
782   libmwaw::DebugStream f;
783   f << "Entries(SumInfo):";
784   bool isDoc=oleName=="DocumentSummaryInformation";
785   if (isDoc) f << "doc,";
786   auto val=int(input->readULong(2));
787   bool invertOLE=false;
788   if (val==0xfeff) {
789     invertOLE=true;
790     input->setReadInverted(!input->readInverted());
791     val=0xfffe;
792   }
793   if (pos+48>endPos || val!=0xfffe) {
794     MWAW_DEBUG_MSG(("MWAWOLEParser::readSummaryInformation: header seems bad\n"));
795     f << "###";
796     ascii.addPos(pos);
797     ascii.addNote(f.str().c_str());
798     if (invertOLE) input->setReadInverted(!input->readInverted());
799     return true;
800   }
801   for (int i=0; i<11; ++i) { // f1=1, f2=0-2
802     val=int(input->readULong(2));
803     if (val) f << "f" << i << "=" << val << ",";
804   }
805   unsigned long lVal=input->readULong(4);
806   if ((lVal&0xF0FFFFFF)==0) {
807     lVal=(lVal>>24);
808     input->setReadInverted(!input->readInverted());
809   }
810   if (lVal==0 || lVal>15) { // find 1 or 2 sections, unsure about the maximum numbers
811     MWAW_DEBUG_MSG(("MWAWOLEParser::readSummaryInformation: summary info is bad\n"));
812     f << "###sumInfo=" << std::hex << lVal << std::dec << ",";
813     ascii.addPos(pos);
814     ascii.addNote(f.str().c_str());
815     if (invertOLE) input->setReadInverted(!input->readInverted());
816     return true;
817   }
818   auto numSection=int(lVal);
819   if (numSection!=1)
820     f << "num[section]=" << numSection << ",";
821   for (int i=0; i<4; ++i) {
822     val=int(input->readULong(4));
823     static int const expected[]= {int(0xf29f85e0),0x10684ff9,0x891ab,int(0xd9b3272b)};
824     static int const docExpected[]= {int(0xd5cdd502),0x101b2e9c,0x89793,int(0xaef92c2b)};
825     if ((!isDoc && val==expected[i]) || (isDoc && val==docExpected[i])) continue;
826     f << "#fmid" << i << "=" << std::hex << val << std::dec << ",";
827     static bool first=true;
828     if (first) {
829       MWAW_DEBUG_MSG(("MWAWOLEParser::readSummaryInformation: fmid is bad\n"));
830       first=false;
831     }
832   }
833   auto decal=int(input->readULong(4));
834   if (decal<0x30 || pos+decal>endPos) {
835     MWAW_DEBUG_MSG(("MWAWOLEParser::readSummaryInformation: decal is bad\n"));
836     f << "decal=" << val << ",";
837     ascii.addPos(pos);
838     ascii.addNote(f.str().c_str());
839     if (invertOLE) input->setReadInverted(!input->readInverted());
840     return true;
841 
842   }
843   ascii.addPos(pos);
844   ascii.addNote(f.str().c_str());
845   if (decal!=0x30) {
846     ascii.addPos(0x30);
847     ascii.addNote("_");
848     input->seek(pos+decal, librevenge::RVNG_SEEK_SET);
849   }
850 
851   for (int sect=0; sect<numSection; ++sect) {
852     pos=input->tell();
853     f.str("");
854     f << "SumInfo-A:";
855     auto pSectSize=long(input->readULong(4));
856     long endSect=pos+pSectSize;
857     auto N=int(input->readULong(4));
858     f << "N=" << N << ",";
859     if (pSectSize<0 || endPos-pos<pSectSize || (pSectSize-8)/8<N) {
860       MWAW_DEBUG_MSG(("MWAWOLEParser::readSummaryInformation: psetstruct is bad\n"));
861       f << "###";
862       ascii.addPos(pos);
863       ascii.addNote(f.str().c_str());
864       if (invertOLE) input->setReadInverted(!input->readInverted());
865       return true;
866     }
867     f << "[";
868     std::map<long,int> posToTypeMap;
869     for (int i=0; i<N; ++i) {
870       auto type=int(input->readULong(4));
871       auto depl=int(input->readULong(4));
872       if (depl<=0) continue;
873       f << std::hex << depl << std::dec << ":" << type << ",";
874       if ((depl-8)/8<N || depl>pSectSize-4 || posToTypeMap.find(pos+depl)!=posToTypeMap.end()) {
875         f << "###";
876         continue;
877       }
878       posToTypeMap[pos+depl]=type;
879     }
880     f << "],";
881     ascii.addPos(pos);
882     ascii.addNote(f.str().c_str());
883 
884     for (auto it=posToTypeMap.begin(); it!=posToTypeMap.end(); ++it) {
885       pos=it->first;
886       auto nextIt=it;
887       long sEndPos= (++nextIt!=posToTypeMap.end()) ? nextIt->first : endSect;
888       input->seek(pos, librevenge::RVNG_SEEK_SET);
889       f.str("");
890       f << "SumInfo-B" << it->second << ":";
891       auto type=int(input->readULong(4));
892       if (sect==0 && it->second==1 && !isDoc && type==2) {
893         long value=-1;
894         if (readSummaryPropertyLong(input,sEndPos,type,value,f) && value>=0 && value<10000) // 10000 is mac
895           encoding=int(value);
896       }
897       else if (sect==0 && type==0x1e && !isDoc && ((it->second>=2 && it->second<=6) || it->second==8)) {
898         librevenge::RVNGString text;
899         if (readSummaryPropertyString(input, sEndPos, type, text, f) && !text.empty()) {
900           static char const *attribNames[] = {
901             "", "", "dc:title", "dc:subject", "meta:initial-creator",
902             "meta:keywords", "dc:description"/*comment*/, "", "dc:creator"
903           };
904           pList.insert(attribNames[it->second], text);
905         }
906       }
907       else if (!readSummaryProperty(input, sEndPos, type, ascii, f)) {
908         MWAW_DEBUG_MSG(("MWAWOLEParser::readSummaryInformation: find unknown type\n"));
909         f << "##type=" << std::hex << type << std::dec << ",";
910       }
911       if (input->tell()!=sEndPos && input->tell()!=pos)
912         ascii.addDelimiter(input->tell(),'|');
913       ascii.addPos(pos);
914       ascii.addNote(f.str().c_str());
915     }
916     input->seek(endSect, librevenge::RVNG_SEEK_SET);
917   }
918   if (invertOLE) input->setReadInverted(!input->readInverted());
919   return true;
920 }
921 
readSummaryPropertyString(MWAWInputStreamPtr input,long endPos,int type,librevenge::RVNGString & string,libmwaw::DebugStream & f) const922 bool MWAWOLEParser::readSummaryPropertyString(MWAWInputStreamPtr input, long endPos, int type,
923     librevenge::RVNGString &string, libmwaw::DebugStream &f) const
924 {
925   if (!input) return false;
926   long pos=input->tell();
927   string.clear();
928   auto sSz=long(input->readULong(4));
929   if (sSz<0 || (endPos-pos-4)<sSz || pos+4+sSz>endPos) {
930     MWAW_DEBUG_MSG(("MWAWOLEParser::readSummaryPropertyString: string size is bad\n"));
931     f << "##stringSz=" << sSz << ",";
932     return false;
933   }
934   std::string text("");
935   for (long c=0; c < sSz; ++c) {
936     auto ch=char(input->readULong(1));
937     if (ch) {
938       text+=ch;
939       if (m_state->m_fontConverter) {
940         int unicode=m_state->m_fontConverter->unicode(m_state->m_fontId, static_cast<unsigned char>(ch));
941         if (unicode!=-1)
942           libmwaw::appendUnicode(uint32_t(unicode), string);
943       }
944     }
945     else if (c+1!=sSz)
946       text+="##";
947   }
948   f << text;
949   if (type==0x1f && (sSz%4))
950     input->seek(sSz%4, librevenge::RVNG_SEEK_CUR);
951   return true;
952 }
953 
readSummaryPropertyLong(MWAWInputStreamPtr input,long endPos,int type,long & value,libmwaw::DebugStream & f) const954 bool MWAWOLEParser::readSummaryPropertyLong(MWAWInputStreamPtr input, long endPos, int type, long &value,
955     libmwaw::DebugStream &f) const
956 {
957   if (!input) return false;
958   long pos=input->tell();
959   switch (type) {
960   case 2: // int
961   case 0x12: // uint
962     if (pos+2>endPos)
963       return false;
964     value=type==2 ? long(input->readLong(2)) : long(input->readULong(2));
965     break;
966   case 3: // int
967   case 9: // uint
968     if (pos+4>endPos)
969       return false;
970     value=type==3 ? long(input->readLong(4)) : long(input->readULong(4));
971     break;
972   default:
973     return false;
974   }
975   f << "val=" << value << ",";
976   return true;
977 }
978 
readSummaryProperty(MWAWInputStreamPtr input,long endPos,int type,libmwaw::DebugFile & ascii,libmwaw::DebugStream & f) const979 bool MWAWOLEParser::readSummaryProperty(MWAWInputStreamPtr input, long endPos, int type,
980                                         libmwaw::DebugFile &ascii, libmwaw::DebugStream &f) const
981 {
982   if (!input) return false;
983   long pos=input->tell();
984   // see propread.cxx
985   if (type&0x1000) {
986     auto N=int(input->readULong(4));
987     f << "N=" << N << ",";
988     f << "[";
989     for (int n=0; n<N; ++n) {
990       pos=input->tell();
991       f << "[";
992       if (!readSummaryProperty(input, endPos, type&0xFFF, ascii, f)) {
993         input->seek(pos, librevenge::RVNG_SEEK_SET);
994         return false;
995       }
996       f << "],";
997     }
998     f << "],";
999     return true;
1000   }
1001   switch (type) {
1002   case 0x10: // int1
1003   case 0x11: // uint1
1004     if (pos+1>endPos)
1005       return false;
1006     f << "val=" << char(input->readULong(1));
1007     break;
1008   case 2: // int
1009   case 0xb: // bool
1010   case 0x12: // uint
1011     if (pos+2>endPos)
1012       return false;
1013     if (type==2)
1014       f << "val=" << int(input->readLong(2)) << ",";
1015     else if (type==0x12)
1016       f << "val=" << int(input->readULong(2)) << ",";
1017     else if (input->readULong(2))
1018       f << "true,";
1019     break;
1020   case 3: // int
1021   case 4: // float
1022   case 9: // uint
1023     if (pos+4>endPos)
1024       return false;
1025     if (type==3)
1026       f << "val=" << int(input->readLong(4)) << ",";
1027     else if (type==9)
1028       f << "val=" << int(input->readULong(4)) << ",";
1029     else
1030       f << "val[fl4]=" << std::hex << input->readULong(4) << std::dec << ",";
1031     break;
1032   case 5: // double
1033   case 6:
1034   case 7:
1035   case 20:
1036   case 21:
1037   case 0x40:
1038     if (pos+8>endPos)
1039       return false;
1040     ascii.addDelimiter(input->tell(),'|');
1041     if (type==5)
1042       f << "double,";
1043     else if (type==6)
1044       f << "cy,";
1045     else if (type==7)
1046       f << "date,";
1047     else if (type==20)
1048       f << "long,";
1049     else if (type==21)
1050       f << "ulong,";
1051     else
1052       f << "fileTime,"; // readme 8 byte
1053     input->seek(pos+8, librevenge::RVNG_SEEK_SET);
1054     break;
1055   case 0xc: // variant
1056     if (pos+4>endPos)
1057       return false;
1058     type=int(input->readULong(4));
1059     return readSummaryProperty(input, endPos, type, ascii, f);
1060   // case 20: int64
1061   // case 21: uint64
1062   case 8:
1063   case 0x1e:
1064   case 0x1f: {
1065     librevenge::RVNGString string;
1066     if (!readSummaryPropertyString(input, endPos, type, string, f))
1067       return false;
1068     break;
1069   }
1070   case 0x41:
1071   case 0x46:
1072   case 0x47: {
1073     if (pos+4>endPos)
1074       return false;
1075     f << (type==0x41 ? "blob" : type==0x46 ? "blob[object]" : "clipboard") << ",";
1076     auto dSz=long(input->readULong(4));
1077     if (dSz<0 || pos+4+dSz>endPos)
1078       return false;
1079     if (dSz) {
1080       ascii.skipZone(pos+4, pos+4+dSz-1);
1081       input->seek(dSz, librevenge::RVNG_SEEK_CUR);
1082     }
1083     break;
1084   }
1085   /* todo type==0x47, vtcf clipboard */
1086   default:
1087     return false;
1088   }
1089   return true;
1090 }
1091 //////////////////////////////////////////////////
1092 //
1093 // OlePres001 seems to contained standart picture file and size
1094 //    extract the picture if it is possible
1095 //
1096 //////////////////////////////////////////////////
1097 
isOlePres(MWAWInputStreamPtr ip,std::string const & oleName)1098 bool MWAWOLEParser::isOlePres(MWAWInputStreamPtr ip, std::string const &oleName)
1099 {
1100   if (!ip.get()) return false;
1101 
1102   if (strncmp("OlePres",oleName.c_str(),7) != 0) return false;
1103 
1104   if (ip->seek(40, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 40) return false;
1105 
1106   ip->seek(0, librevenge::RVNG_SEEK_SET);
1107   for (int i= 0; i < 2; i++) {
1108     long val = ip->readLong(4);
1109     if (val < -10 || val > 10) {
1110       if (i!=1 && val != 0x50494354)
1111         return false;
1112     }
1113   }
1114 
1115   long actPos = ip->tell();
1116   long hSize = ip->readLong(4);
1117   if (hSize < 4) return false;
1118   if (ip->seek(actPos+hSize+28, librevenge::RVNG_SEEK_SET) != 0
1119       || ip->tell() != actPos+hSize+28)
1120     return false;
1121 
1122   ip->seek(actPos+hSize, librevenge::RVNG_SEEK_SET);
1123   for (int i= 3; i < 7; i++) {
1124     long val = ip->readLong(4);
1125     if (val < -10 || val > 10) {
1126       if (i != 5 || val > 256) return false;
1127     }
1128   }
1129 
1130   ip->seek(8, librevenge::RVNG_SEEK_CUR);
1131   long size = ip->readLong(4);
1132 
1133   if (size <= 0) return ip->isEnd();
1134 
1135   actPos = ip->tell();
1136   if (ip->seek(actPos+size, librevenge::RVNG_SEEK_SET) != 0
1137       || ip->tell() != actPos+size)
1138     return false;
1139 
1140   return true;
1141 }
1142 
readOlePres(MWAWInputStreamPtr ip,librevenge::RVNGBinaryData & data,MWAWPosition & pos,libmwaw::DebugFile & ascii)1143 bool MWAWOLEParser::readOlePres(MWAWInputStreamPtr ip, librevenge::RVNGBinaryData &data, MWAWPosition &pos,
1144                                 libmwaw::DebugFile &ascii)
1145 {
1146   data.clear();
1147   if (!isOlePres(ip, "OlePres")) return false;
1148 
1149   pos = MWAWPosition();
1150   pos.setUnit(librevenge::RVNG_POINT);
1151   pos.setRelativePosition(MWAWPosition::Char);
1152   libmwaw::DebugStream f;
1153   f << "@@OlePress(header): ";
1154   ip->seek(0,librevenge::RVNG_SEEK_SET);
1155   for (int i = 0; i < 2; i++) {
1156     long val = ip->readLong(4);
1157     f << val << ", ";
1158   }
1159 
1160   long actPos = ip->tell();
1161   long hSize = ip->readLong(4);
1162   if (hSize < 4) return false;
1163   f << "hSize = " << hSize;
1164   ascii.addPos(0);
1165   ascii.addNote(f.str().c_str());
1166 
1167   long endHPos = actPos+hSize;
1168   if (!ip->checkPosition(endHPos+28)) return false;
1169   bool ok = true;
1170   f.str("");
1171   f << "@@OlePress(headerA): ";
1172   if (hSize < 14) ok = false;
1173   else {
1174     // 12,21,32|48,0
1175     for (int i = 0; i < 4; i++) f << ip->readLong(2) << ",";
1176     // 3 name of creator
1177     for (int ch=0; ch < 3; ch++) {
1178       std::string str;
1179       bool find = false;
1180       while (ip->tell() < endHPos) {
1181         auto c = static_cast<unsigned char>(ip->readULong(1));
1182         if (c == 0) {
1183           find = true;
1184           break;
1185         }
1186         str += char(c);
1187       }
1188       if (!find) {
1189         ok = false;
1190         break;
1191       }
1192       f << ", name" <<  ch << "=" << str;
1193     }
1194     if (ok) ok = ip->tell() == endHPos;
1195   }
1196   // FIXME, normally they remain only a few bits (size unknown)
1197   if (!ok) f << "###";
1198   ascii.addPos(actPos);
1199   ascii.addNote(f.str().c_str());
1200 
1201   if (ip->seek(endHPos+28, librevenge::RVNG_SEEK_SET) != 0)
1202     return false;
1203 
1204   ip->seek(endHPos, librevenge::RVNG_SEEK_SET);
1205 
1206   actPos = ip->tell();
1207   f.str("");
1208   f << "@@OlePress(headerB): ";
1209   for (int i = 3; i < 7; i++) {
1210     long val = ip->readLong(4);
1211     f << val << ", ";
1212   }
1213   // dim in TWIP ?
1214   auto extendX = long(ip->readULong(4));
1215   auto extendY = long(ip->readULong(4));
1216   if (extendX > 0 && extendY > 0) pos.setNaturalSize(MWAWVec2f(float(extendX)/20.f, float(extendY)/20.f));
1217   long fSize = ip->readLong(4);
1218   f << "extendX="<< extendX << ", extendY=" << extendY << ", fSize=" << fSize;
1219 
1220   ascii.addPos(actPos);
1221   ascii.addNote(f.str().c_str());
1222 
1223   if (fSize == 0) return ip->isEnd();
1224 
1225   data.clear();
1226   if (!ip->readDataBlock(fSize, data)) return false;
1227 
1228   if (!ip->isEnd()) {
1229     ascii.addPos(ip->tell());
1230     ascii.addNote("@@OlePress###");
1231   }
1232 
1233   ascii.skipZone(36+hSize,36+hSize+fSize-1);
1234   return true;
1235 }
1236 
1237 //////////////////////////////////////////////////
1238 //
1239 //  Ole10Native: basic Windows picture, with no size
1240 //          - in general used to store a bitmap
1241 //
1242 //////////////////////////////////////////////////
1243 
isOle10Native(MWAWInputStreamPtr ip,std::string const & oleName)1244 bool MWAWOLEParser::isOle10Native(MWAWInputStreamPtr ip, std::string const &oleName)
1245 {
1246   if (strncmp("Ole10Native",oleName.c_str(),11) != 0) return false;
1247 
1248   if (ip->seek(4, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 4) return false;
1249 
1250   ip->seek(0, librevenge::RVNG_SEEK_SET);
1251   long size = ip->readLong(4);
1252 
1253   if (size <= 0) return false;
1254   if (ip->seek(4+size, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 4+size)
1255     return false;
1256 
1257   return true;
1258 }
1259 
readOle10Native(MWAWInputStreamPtr ip,librevenge::RVNGBinaryData & data,libmwaw::DebugFile & ascii)1260 bool MWAWOLEParser::readOle10Native(MWAWInputStreamPtr ip,
1261                                     librevenge::RVNGBinaryData &data,
1262                                     libmwaw::DebugFile &ascii)
1263 {
1264   if (!isOle10Native(ip, "Ole10Native")) return false;
1265 
1266   libmwaw::DebugStream f;
1267   f << "@@Ole10Native(Header): ";
1268   ip->seek(0,librevenge::RVNG_SEEK_SET);
1269   long fSize = ip->readLong(4);
1270   f << "fSize=" << fSize;
1271 
1272   ascii.addPos(0);
1273   ascii.addNote(f.str().c_str());
1274 
1275   data.clear();
1276   if (!ip->readDataBlock(fSize, data)) return false;
1277 
1278   if (!ip->isEnd()) {
1279     ascii.addPos(ip->tell());
1280     ascii.addNote("@@Ole10Native###");
1281   }
1282   ascii.skipZone(4,4+fSize-1);
1283   return true;
1284 }
1285 
1286 ////////////////////////////////////////////////////////////////
1287 //
1288 // In general a picture : a PNG, an JPEG, a basic metafile,
1289 //    find also a MSDraw.1.01 picture (with first bytes 0x78563412="xV4") or WordArt,
1290 //    ( with first bytes "WordArt" )  which are not sucefull read
1291 //    (can probably contain a list of data, but do not know how to
1292 //     detect that)
1293 //
1294 // To check: does this is related to MSO_BLIPTYPE ?
1295 //        or OO/filter/sources/msfilter/msdffimp.cxx ?
1296 //
1297 ////////////////////////////////////////////////////////////////
readContents(MWAWInputStreamPtr input,std::string const & oleName,librevenge::RVNGBinaryData & pict,MWAWPosition & pos,libmwaw::DebugFile & ascii)1298 bool MWAWOLEParser::readContents(MWAWInputStreamPtr input,
1299                                  std::string const &oleName,
1300                                  librevenge::RVNGBinaryData &pict, MWAWPosition &pos,
1301                                  libmwaw::DebugFile &ascii)
1302 {
1303   pict.clear();
1304   if (oleName!="Contents") return false;
1305 
1306   libmwaw::DebugStream f;
1307   pos = MWAWPosition();
1308   pos.setUnit(librevenge::RVNG_POINT);
1309   pos.setRelativePosition(MWAWPosition::Char);
1310   input->seek(0, librevenge::RVNG_SEEK_SET);
1311   f << "@@Contents:";
1312 
1313   bool ok = true;
1314   // bdbox 0 : size in the file ?
1315   long dim[2];
1316   for (auto &d : dim) d = input->readLong(4);
1317   f << "bdbox0=(" << dim[0] << "," << dim[1]<<"),";
1318   for (int i = 0; i < 3; i++) {
1319     /* 0,{10|21|75|101|116}x2 */
1320     auto val = long(input->readULong(4));
1321     if (val < 1000)
1322       f << val << ",";
1323     else
1324       f << std::hex << "0x" << val << std::dec << ",";
1325     if (val > 0x10000) ok=false;
1326   }
1327   // new bdbox : size of the picture ?
1328   long naturalSize[2];
1329   for (auto &size : naturalSize) size = input->readLong(4);
1330   f << std::dec << "bdbox1=(" << naturalSize[0] << "," << naturalSize[1]<<"),";
1331   f << "unk=" << input->readULong(4) << ","; // 24 or 32
1332   if (input->isEnd()) {
1333     MWAW_DEBUG_MSG(("MWAWOLEParser: warning: Contents header length\n"));
1334     return false;
1335   }
1336   if (dim[0] > 0 && dim[0] < 3000 &&
1337       dim[1] > 0 && dim[1] < 3000)
1338     pos.setSize(MWAWVec2f(float(dim[0]),float(dim[1])));
1339   else {
1340     MWAW_DEBUG_MSG(("MWAWOLEParser: warning: Contents odd size : %ld %ld\n", dim[0], dim[1]));
1341   }
1342   if (naturalSize[0] > 0 && naturalSize[0] < 5000 &&
1343       naturalSize[1] > 0 && naturalSize[1] < 5000)
1344     pos.setNaturalSize(MWAWVec2f(float(naturalSize[0]),float(naturalSize[1])));
1345   else {
1346     MWAW_DEBUG_MSG(("MWAWOLEParser: warning: Contents odd naturalsize : %ld %ld\n", naturalSize[0], naturalSize[1]));
1347   }
1348 
1349   long actPos = input->tell();
1350   auto size = long(input->readULong(4));
1351   if (size <= 0) ok = false;
1352   if (ok) {
1353     input->seek(actPos+size+4, librevenge::RVNG_SEEK_SET);
1354     if (input->tell() != actPos+size+4 || !input->isEnd()) {
1355       ok = false;
1356       MWAW_DEBUG_MSG(("MWAWOLEParser: warning: Contents unexpected file size=%ld\n",
1357                       size));
1358     }
1359   }
1360 
1361   if (!ok) f << "###";
1362   f << "dataSize=" << size;
1363 
1364   ascii.addPos(0);
1365   ascii.addNote(f.str().c_str());
1366 
1367   input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
1368 
1369   if (ok) {
1370     if (input->readDataBlock(size, pict))
1371       ascii.skipZone(actPos+4, actPos+size+4-1);
1372     else {
1373       input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
1374       ok = false;
1375     }
1376   }
1377 
1378   if (!input->isEnd()) {
1379     ascii.addPos(actPos);
1380     ascii.addNote("@@Contents:###");
1381   }
1382 
1383   if (!ok) {
1384     MWAW_DEBUG_MSG(("MWAWOLEParser: warning: read ole Contents: failed\n"));
1385   }
1386   return ok;
1387 }
1388 
1389 ////////////////////////////////////////////////////////////////
1390 //
1391 // Another different type of contents (this time in majuscule)
1392 // we seem to contain the header of a EMF and then the EMF file
1393 //
1394 ////////////////////////////////////////////////////////////////
readCONTENTS(MWAWInputStreamPtr input,std::string const & oleName,librevenge::RVNGBinaryData & pict,MWAWPosition & pos,libmwaw::DebugFile & ascii)1395 bool MWAWOLEParser::readCONTENTS(MWAWInputStreamPtr input,
1396                                  std::string const &oleName,
1397                                  librevenge::RVNGBinaryData &pict, MWAWPosition &pos,
1398                                  libmwaw::DebugFile &ascii)
1399 {
1400   pict.clear();
1401   if (oleName!="CONTENTS") return false;
1402 
1403   libmwaw::DebugStream f;
1404 
1405   pos = MWAWPosition();
1406   pos.setUnit(librevenge::RVNG_POINT);
1407   pos.setRelativePosition(MWAWPosition::Char);
1408   input->seek(0, librevenge::RVNG_SEEK_SET);
1409   f << "@@CONTENTS:";
1410 
1411   auto hSize = long(input->readULong(4));
1412   if (input->isEnd()) return false;
1413   f << "hSize=" << std::hex << hSize << std::dec;
1414 
1415   if (hSize <= 52 || input->seek(hSize+8,librevenge::RVNG_SEEK_SET) != 0
1416       || input->tell() != hSize+8) {
1417     MWAW_DEBUG_MSG(("MWAWOLEParser: warning: CONTENTS headerSize=%ld\n",
1418                     hSize));
1419     return false;
1420   }
1421 
1422   // minimal checking of the "copied" header
1423   input->seek(4, librevenge::RVNG_SEEK_SET);
1424   auto type = long(input->readULong(4));
1425   if (type < 0 || type > 4) return false;
1426   auto newSize = long(input->readULong(4));
1427 
1428   f << ", type=" << type;
1429   if (newSize < 8) return false;
1430 
1431   if (newSize != hSize) // can sometimes happen, pb after a conversion ?
1432     f << ", ###newSize=" << std::hex << newSize << std::dec;
1433 
1434   // checkme: two bdbox, in document then data : units ?
1435   //     Maybe first in POINT, second in TWIP ?
1436   for (int st = 0; st < 2 ; st++) {
1437     long dim[4];
1438     for (auto &d : dim) d = input->readLong(4);
1439 
1440     bool ok = dim[0] >= 0 && dim[2] > dim[0] && dim[1] >= 0 && dim[3] > dim[2];
1441     if (ok && st==0) pos.setNaturalSize(MWAWVec2f(float(dim[2]-dim[0]), float(dim[3]-dim[1])));
1442     if (st==0) f << ", bdbox(Text)";
1443     else f << ", bdbox(Data)";
1444     if (!ok) f << "###";
1445     f << "=(" << dim[0] << "x" << dim[1] << "<->" << dim[2] << "x" << dim[3] << ")";
1446   }
1447   char dataType[5];
1448   for (int i = 0; i < 4; i++) dataType[i] = char(input->readULong(1));
1449   dataType[4] = '\0';
1450   f << ",typ=\""<<dataType<<"\""; // always " EMF" ?
1451 
1452   for (int i = 0; i < 2; i++) { // always id0=0, id1=1 ?
1453     auto val = static_cast<int>(input->readULong(2));
1454     if (val) f << ",id"<< i << "=" << val;
1455   }
1456   auto dataLength = long(input->readULong(4));
1457   f << ",length=" << dataLength+hSize;
1458 
1459   ascii.addPos(0);
1460   ascii.addNote(f.str().c_str());
1461 
1462   ascii.addPos(input->tell());
1463   f.str("");
1464   f << "@@CONTENTS(2)";
1465   for (int i = 0; i < 12 && 4*i+52 < hSize; i++) {
1466     // f0=7,f1=1,f5=500,f6=320,f7=1c4,f8=11a
1467     // or f0=a,f1=1,f2=2,f3=6c,f5=480,f6=360,f7=140,f8=f0
1468     // or f0=61,f1=1,f2=2,f3=58,f5=280,f6=1e0,f7=a9,f8=7f
1469     // f3=some header sub size ? f5/f6 and f7/f8 two other bdbox ?
1470     auto val = long(input->readULong(4));
1471     if (val) f << std::hex << ",f" << i << "=" << val;
1472   }
1473   for (int i = 0; 2*i+100 < hSize; i++) {
1474     // g0=e3e3,g1=6,g2=4e6e,g3=4
1475     // g0=e200,g1=4,g2=a980,g3=3,g4=4c,g5=50
1476     // ---
1477     auto val = long(input->readULong(2));
1478     if (val) f << std::hex << ",g" << i << "=" << val;
1479   }
1480   ascii.addNote(f.str().c_str());
1481 
1482   if (dataLength <= 0 || input->seek(hSize+4+dataLength,librevenge::RVNG_SEEK_SET) != 0
1483       || input->tell() != hSize+4+dataLength || !input->isEnd()) {
1484     MWAW_DEBUG_MSG(("MWAWOLEParser: warning: CONTENTS unexpected file length=%ld\n",
1485                     dataLength));
1486     return false;
1487   }
1488 
1489   input->seek(4+hSize, librevenge::RVNG_SEEK_SET);
1490   if (!input->readEndDataBlock(pict)) return false;
1491 
1492   ascii.skipZone(hSize+4, input->tell()-1);
1493   return true;
1494 }
1495 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
1496