Coverage Report

Created: 2026-09-14 06:37

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