Coverage Report

Created: 2026-04-29 07:28

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