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