Coverage Report

Created: 2026-09-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libstaroffice/src/lib/STOFFOLEParser.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
/* libstaroffice
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
/*
35
 *  freely inspired from istorage :
36
 * ------------------------------------------------------------
37
 *      Generic OLE Zones furnished with a copy of the file header
38
 *
39
 * Compound Storage (32 bit version)
40
 * Storage implementation
41
 *
42
 * This file contains the compound file implementation
43
 * of the storage interface.
44
 *
45
 * Copyright 1999 Francis Beaudet
46
 * Copyright 1999 Sylvain St-Germain
47
 * Copyright 1999 Thuy Nguyen
48
 * Copyright 2005 Mike McCormack
49
 *
50
 * This library is free software; you can redistribute it and/or
51
 * modify it under the terms of the GNU Lesser General Public
52
 * License as published by the Free Software Foundation; either
53
 * version 2.1 of the License, or (at your option) any later version.
54
 *
55
 * This library is distributed in the hope that it will be useful,
56
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
57
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
58
 * Lesser General Public License for more details.
59
 *
60
 * You should have received a copy of the GNU Lesser General Public
61
 * License along with this library; if not, write to the Free Software
62
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
63
 *
64
 */
65
66
/*
67
 * NOTES
68
 *  The compound file implementation of IStorage used for create
69
 *  and manage substorages and streams within a storage object
70
 *  residing in a compound file object.
71
 *
72
 * MSDN
73
 *  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
74
 * ------------------------------------------------------------
75
 */
76
77
#include <cstdlib>
78
#include <cstring>
79
#include <iostream>
80
#include <map>
81
#include <sstream>
82
#include <string>
83
84
#include <librevenge/librevenge.h>
85
86
#include "STOFFPosition.hxx"
87
#include "STOFFOLEParser.hxx"
88
89
//////////////////////////////////////////////////
90
// internal structure
91
//////////////////////////////////////////////////
92
/** Low level: namespace used to define/store the data used by STOFFOLEParser */
93
namespace STOFFOLEParserInternal
94
{
95
//! the state of a STOFFOLEParser
96
struct State {
97
  //! constructor
98
  State()
99
284k
    : m_oleList()
100
284k
    , m_unknownOLEs()
101
284k
    , m_mapCls()
102
284k
  {
103
284k
  }
104
  //! returns a CLSName if knwon
105
  std::string getCLSName(unsigned long id) const
106
138
  {
107
138
    if (m_mapCls.empty())
108
138
      const_cast<State *>(this)->initCLSMap();
109
138
    if (m_mapCls.find(id) == m_mapCls.end()) return "";
110
45
    return m_mapCls.find(id)->second;
111
138
  }
112
  //! the ole list
113
  std::vector<std::shared_ptr<STOFFOLEParser::OleDirectory> > m_oleList;
114
  //! list of ole which can not be parsed
115
  std::vector<std::string> m_unknownOLEs;
116
protected:
117
  /** initialise a map CLSId <-> name */
118
  void initCLSMap();
119
  /** map CLSId <-> name */
120
  std::map<unsigned long, char const *> m_mapCls;
121
};
122
123
void State::initCLSMap()
124
138
{
125
  // source: binfilter/bf_so3/source/inplace/embobj.cxx
126
138
  m_mapCls[0x00000319]="Picture"; // addon Enhanced Metafile ( find in some file)
127
128
138
  m_mapCls[0x000212F0]="MSWordArt"; // or MSWordArt.2
129
138
  m_mapCls[0x00021302]="MSWorksWPDoc"; // addon
130
131
  // MS Apps
132
138
  m_mapCls[0x00030000]= "ExcelWorksheet";
133
138
  m_mapCls[0x00030001]= "ExcelChart";
134
138
  m_mapCls[0x00030002]= "ExcelMacrosheet";
135
138
  m_mapCls[0x00030003]= "WordDocument";
136
138
  m_mapCls[0x00030004]= "MSPowerPoint";
137
138
  m_mapCls[0x00030005]= "MSPowerPointSho";
138
138
  m_mapCls[0x00030006]= "MSGraph";
139
138
  m_mapCls[0x00030007]= "MSDraw"; // find also with ca003 ?
140
138
  m_mapCls[0x00030008]= "Note-It";
141
138
  m_mapCls[0x00030009]= "WordArt";
142
138
  m_mapCls[0x0003000a]= "PBrush";
143
138
  m_mapCls[0x0003000b]= "Equation"; // "Microsoft Equation Editor"
144
138
  m_mapCls[0x0003000c]= "Package";
145
138
  m_mapCls[0x0003000d]= "SoundRec";
146
138
  m_mapCls[0x0003000e]= "MPlayer";
147
  // MS Demos
148
138
  m_mapCls[0x0003000f]= "ServerDemo"; // "OLE 1.0 Server Demo"
149
138
  m_mapCls[0x00030010]= "Srtest"; // "OLE 1.0 Test Demo"
150
138
  m_mapCls[0x00030011]= "SrtInv"; //  "OLE 1.0 Inv Demo"
151
138
  m_mapCls[0x00030012]= "OleDemo"; //"OLE 1.0 Demo"
152
153
  // Coromandel / Dorai Swamy / 718-793-7963
154
138
  m_mapCls[0x00030013]= "CoromandelIntegra";
155
138
  m_mapCls[0x00030014]= "CoromandelObjServer";
156
157
  // 3-d Visions Corp / Peter Hirsch / 310-325-1339
158
138
  m_mapCls[0x00030015]= "StanfordGraphics";
159
160
  // Deltapoint / Nigel Hearne / 408-648-4000
161
138
  m_mapCls[0x00030016]= "DGraphCHART";
162
138
  m_mapCls[0x00030017]= "DGraphDATA";
163
164
  // Corel / Richard V. Woodend / 613-728-8200 x1153
165
138
  m_mapCls[0x00030018]= "PhotoPaint"; // "Corel PhotoPaint"
166
138
  m_mapCls[0x00030019]= "CShow"; // "Corel Show"
167
138
  m_mapCls[0x0003001a]= "CorelChart";
168
138
  m_mapCls[0x0003001b]= "CDraw"; // "Corel Draw"
169
170
  // Inset Systems / Mark Skiba / 203-740-2400
171
138
  m_mapCls[0x0003001c]= "HJWIN1.0"; // "Inset Systems"
172
173
  // Mark V Systems / Mark McGraw / 818-995-7671
174
138
  m_mapCls[0x0003001d]= "ObjMakerOLE"; // "MarkV Systems Object Maker"
175
176
  // IdentiTech / Mike Gilger / 407-951-9503
177
138
  m_mapCls[0x0003001e]= "FYI"; // "IdentiTech FYI"
178
138
  m_mapCls[0x0003001f]= "FYIView"; // "IdentiTech FYI Viewer"
179
180
  // Inventa Corporation / Balaji Varadarajan / 408-987-0220
181
138
  m_mapCls[0x00030020]= "Stickynote";
182
183
  // ShapeWare Corp. / Lori Pearce / 206-467-6723
184
138
  m_mapCls[0x00030021]= "ShapewareVISIO10";
185
138
  m_mapCls[0x00030022]= "ImportServer"; // "Spaheware Import Server"
186
187
  // test app SrTest
188
138
  m_mapCls[0x00030023]= "SrvrTest"; // "OLE 1.0 Server Test"
189
190
  // test app ClTest.  Doesn't really work as a server but is in reg db
191
138
  m_mapCls[0x00030025]= "Cltest"; // "OLE 1.0 Client Test"
192
193
  // Microsoft ClipArt Gallery   Sherry Larsen-Holmes
194
138
  m_mapCls[0x00030026]= "MS_ClipArt_Gallery";
195
  // Microsoft Project  Cory Reina
196
138
  m_mapCls[0x00030027]= "MSProject";
197
198
  // Microsoft Works Chart
199
138
  m_mapCls[0x00030028]= "MSWorksChart";
200
201
  // Microsoft Works Spreadsheet
202
138
  m_mapCls[0x00030029]= "MSWorksSpreadsheet";
203
204
  // AFX apps - Dean McCrory
205
138
  m_mapCls[0x0003002A]= "MinSvr"; // "AFX Mini Server"
206
138
  m_mapCls[0x0003002B]= "HierarchyList"; // "AFX Hierarchy List"
207
138
  m_mapCls[0x0003002C]= "BibRef"; // "AFX BibRef"
208
138
  m_mapCls[0x0003002D]= "MinSvrMI"; // "AFX Mini Server MI"
209
138
  m_mapCls[0x0003002E]= "TestServ"; // "AFX Test Server"
210
211
  // Ami Pro
212
138
  m_mapCls[0x0003002F]= "AmiProDocument";
213
214
  // WordPerfect Presentations For Windows
215
138
  m_mapCls[0x00030030]= "WPGraphics";
216
138
  m_mapCls[0x00030031]= "WPCharts";
217
218
  // MicroGrafx Charisma
219
138
  m_mapCls[0x00030032]= "Charisma";
220
138
  m_mapCls[0x00030033]= "Charisma_30"; // v 3.0
221
138
  m_mapCls[0x00030034]= "CharPres_30"; // v 3.0 Pres
222
  // MicroGrafx Draw
223
138
  m_mapCls[0x00030035]= "Draw"; //"MicroGrafx Draw"
224
  // MicroGrafx Designer
225
138
  m_mapCls[0x00030036]= "Designer_40"; // "MicroGrafx Designer 4.0"
226
227
  // STAR DIVISION
228
  //m_mapCls[0x000424CA]= "StarMath"; // "StarMath 1.0"
229
138
  m_mapCls[0x00043AD2]= "FontWork"; // "Star FontWork"
230
  //m_mapCls[0x000456EE]= "StarMath2"; // "StarMath 2.0"
231
138
}
232
233
}
234
235
// constructor/destructor
236
STOFFOLEParser::STOFFOLEParser()
237
207k
  : m_state(new STOFFOLEParserInternal::State)
238
207k
{
239
207k
}
240
241
STOFFOLEParser::~STOFFOLEParser()
242
207k
{
243
207k
}
244
245
std::vector<std::shared_ptr<STOFFOLEParser::OleDirectory> > &STOFFOLEParser::getDirectoryList()
246
0
{
247
0
  return m_state->m_oleList;
248
0
}
249
250
std::shared_ptr<STOFFOLEParser::OleDirectory> STOFFOLEParser::getDirectory(std::string const &dir)
251
85.8k
{
252
85.8k
  std::string dirName(dir);
253
85.8k
  if (!dir.empty() && *dir.rbegin()=='/')
254
76.5k
    dirName=dir.substr(0, dir.size()-1);
255
100k
  for (auto &ole : m_state->m_oleList) {
256
100k
    if (ole && ole->m_dir==dirName)
257
83.4k
      return ole;
258
100k
  }
259
2.41k
  return std::shared_ptr<STOFFOLEParser::OleDirectory>();
260
85.8k
}
261
262
// parsing
263
bool STOFFOLEParser::parse(STOFFInputStreamPtr file)
264
76.4k
{
265
76.4k
  m_state.reset(new STOFFOLEParserInternal::State);
266
267
76.4k
  if (!file.get()) return false;
268
269
76.4k
  if (!file->isStructured()) return false;
270
271
76.4k
  unsigned numSubStreams = file->subStreamCount();
272
  //
273
  // we begin by grouping the Ole by their potential main id
274
  //
275
76.4k
  std::map<std::string, std::shared_ptr<STOFFOLEParser::OleDirectory> > listsByDir;
276
571k
  for (unsigned i = 0; i < numSubStreams; ++i) {
277
494k
    std::string const &name = file->subStreamName(i);
278
494k
    if (name.empty() || name[name.length()-1]=='/') continue;
279
280
    // separated the directory and the name
281
    //    MatOST/MatadorObject1/Ole10Native
282
    //      -> dir="MatOST/MatadorObject1", base="Ole10Native"
283
409k
    auto pos = name.find_last_of('/');
284
285
409k
    std::string dir(""), base;
286
409k
    if (pos == std::string::npos) base = name;
287
69.3k
    else if (pos == 0) base = name.substr(1);
288
67.9k
    else {
289
67.9k
      dir = name.substr(0,pos);
290
67.9k
      base = name.substr(pos+1);
291
67.9k
    }
292
293
409k
#define PRINT_OLE_NAME
294
409k
#if defined(PRINT_OLE_NAME)
295
409k
    STOFF_DEBUG_MSG(("OLEName=%s\n", name.c_str()));
296
409k
#endif
297
409k
    if (listsByDir.find(dir)==listsByDir.end() || !listsByDir.find(dir)->second) {
298
92.2k
      std::shared_ptr<STOFFOLEParser::OleDirectory> newDir(new STOFFOLEParser::OleDirectory(file, dir));
299
92.2k
      listsByDir[dir]=newDir;
300
92.2k
      m_state->m_oleList.push_back(newDir);
301
92.2k
    }
302
409k
    listsByDir.find(dir)->second->addNewBase(base);
303
409k
  }
304
92.2k
  for (auto &dOle : m_state->m_oleList) {
305
92.2k
    if (!dOle) continue;
306
92.2k
    if (dOle->m_hasCompObj) {
307
36.5k
      auto ole = file->getSubStreamByName(dOle->m_dir+"/CompObj");
308
36.5k
      if (!ole.get()) {
309
1.94k
        STOFF_DEBUG_MSG(("STOFFOLEParser::parse: error: can not find CompObj in directory: \"%s\"\n", dOle->m_dir.c_str()));
310
1.94k
      }
311
34.5k
      else
312
34.5k
        readCompObj(ole, *dOle);
313
36.5k
    }
314
372k
    for (auto &content : dOle->m_contentList) {
315
372k
      std::string base=content.getBaseName();
316
372k
      std::string oleName=content.getOleName();
317
372k
      auto ole = file->getSubStreamByName(oleName);
318
372k
      if (!ole.get()) {
319
64.0k
        STOFF_DEBUG_MSG(("STOFFOLEParser::parse: error: can not find OLE part: \"%s\"\n", oleName.c_str()));
320
64.0k
        continue;
321
64.0k
      }
322
323
308k
      ole->setReadInverted(true);
324
308k
      if (isOlePres(ole, base) && readOlePres(ole, content))
325
1.37k
        continue;
326
306k
      if (isOle10Native(ole, base) && readOle10Native(ole, content))
327
        // small size can be a symptom that this is a link
328
37
        continue;
329
306k
      if (readContents(ole, content) || readCONTENTS(ole, content))
330
4.04k
        continue;
331
302k
      libstoff::DebugFile asciiFile(ole);
332
302k
      asciiFile.open(oleName);
333
334
302k
      bool ok = true;
335
302k
      try {
336
302k
        if (readObjInfo(ole, base, asciiFile));
337
302k
        else if (readOle(ole, base, asciiFile));
338
286k
        else if (readSummaryInformation(ole, base, asciiFile));
339
266k
        else
340
266k
          ok = false;
341
302k
      }
342
302k
      catch (...) {
343
0
        ok = false;
344
0
      }
345
302k
      content.setParsed(ok);
346
302k
      asciiFile.reset();
347
302k
      if (ok) continue;
348
349
266k
      m_state->m_unknownOLEs.push_back(oleName);
350
266k
      if (base.compare(0,4,"Star")==0) {
351
        // times to update the
352
89.9k
        if (base=="StarCalcDocument")
353
33.6k
          dOle->m_kind=STOFFDocument::STOFF_K_SPREADSHEET;
354
56.3k
        else if (base=="StarChartDocument")
355
2.73k
          dOle->m_kind=STOFFDocument::STOFF_K_CHART;
356
53.5k
        else if (base=="StarDrawDocument" || base=="StarDrawDocument3")
357
46.6k
          dOle->m_kind=STOFFDocument::STOFF_K_DRAW;
358
6.91k
        else if (base=="StarImageDocument")
359
82
          dOle->m_kind=STOFFDocument::STOFF_K_BITMAP;
360
6.83k
        else if (base=="StarMathDocument")
361
2.15k
          dOle->m_kind=STOFFDocument::STOFF_K_MATH;
362
4.68k
        else if (base=="StarWriterDocument")
363
727
          dOle->m_kind=STOFFDocument::STOFF_K_TEXT;
364
89.9k
      }
365
266k
    }
366
92.2k
  }
367
368
76.4k
  return true;
369
76.4k
}
370
371
372
bool STOFFOLEParser::getCompObjName(STOFFInputStreamPtr file, std::string &programName)
373
131k
{
374
131k
  if (!file.get() || !file->isStructured()) return false;
375
131k
  auto ole = file->getSubStreamByName("/CompObj");
376
131k
  if (!ole) return false;
377
81.1k
  STOFFOLEParser::OleDirectory oleDir(file,"");
378
81.1k
  if (!readCompObj(ole, oleDir) || oleDir.m_clipName.empty()) return false;
379
69.6k
  programName=oleDir.m_clipName;
380
69.6k
  return true;
381
81.1k
}
382
383
////////////////////////////////////////
384
//
385
// small structure
386
//
387
////////////////////////////////////////
388
bool STOFFOLEParser::readOle(STOFFInputStreamPtr ip, std::string const &oleName,
389
                             libstoff::DebugFile &ascii)
390
302k
{
391
302k
  if (!ip.get()) return false;
392
393
302k
  if (oleName!="Ole") return false;
394
395
21.5k
  if (ip->seek(20, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 20) return false;
396
397
21.3k
  ip->seek(0, librevenge::RVNG_SEEK_SET);
398
399
21.3k
  int val[20];
400
364k
  for (int &i : val) {
401
364k
    i = int(ip->readLong(1));
402
364k
    if (i < -10 || i > 10) return false;
403
364k
  }
404
405
16.7k
  libstoff::DebugStream f;
406
16.7k
  f << "Entries(OleSimp): ";
407
  // always 1, 0, 2, 0*
408
352k
  for (int i = 0; i < 20; i++)
409
335k
    if (val[i]) f << "f" << i << "=" << val[i] << ",";
410
16.7k
  ascii.addPos(0);
411
16.7k
  ascii.addNote(f.str().c_str());
412
413
16.7k
  if (!ip->isEnd()) {
414
144
    ascii.addPos(20);
415
144
    ascii.addNote("@@OleSimp:###");
416
144
  }
417
418
16.7k
  return true;
419
21.3k
}
420
421
bool STOFFOLEParser::readSummaryInformation(STOFFInputStreamPtr input, std::string const &oleName,
422
    libstoff::DebugFile &ascii)
423
286k
{
424
286k
  if (oleName!="SummaryInformation") return false;
425
19.4k
  input->seek(0, librevenge::RVNG_SEEK_SET);
426
19.4k
  libstoff::DebugStream f;
427
19.4k
  f << "Entries(SumInfo):";
428
19.4k
  auto val=int(input->readULong(2));
429
19.4k
  if (val==0xfeff) {
430
30
    input->setReadInverted(false);
431
30
    val=0xfffe;
432
30
  }
433
19.4k
  if (input->size()<48 || val!=0xfffe) {
434
7.55k
    STOFF_DEBUG_MSG(("STOFFOLEParser::readSummaryInformation: header seems bad\n"));
435
7.55k
    f << "###";
436
7.55k
    ascii.addPos(0);
437
7.55k
    ascii.addNote(f.str().c_str());
438
7.55k
    return true;
439
7.55k
  }
440
143k
  for (int i=0; i<11; ++i) { // f1=1, f2=0-2
441
131k
    val=int(input->readULong(2));
442
131k
    if (val) f << "f" << i << "=" << val << ",";
443
131k
  }
444
11.9k
  val=int(input->readULong(4));
445
11.9k
  if (val!=1) {
446
653
    STOFF_DEBUG_MSG(("STOFFOLEParser::readSummaryInformation: summary info is bad\n"));
447
653
    f << "###sumInfo=" << val << ",";
448
653
    ascii.addPos(0);
449
653
    ascii.addNote(f.str().c_str());
450
653
    return true;
451
653
  }
452
54.9k
  for (int i=0; i<4; ++i) {
453
44.2k
    val=int(input->readULong(4));
454
44.2k
    static int const expected[]= {int(0xf29f85e0),0x10684ff9,0x891ab,int(0xd9b3272b)};
455
44.2k
    if (val==expected[i]) continue;
456
591
    STOFF_DEBUG_MSG(("STOFFOLEParser::readSummaryInformation: fmid is bad\n"));
457
591
    f << "###fmid" << i << "=" << std::hex << val << std::dec << ",";
458
591
    ascii.addPos(0);
459
591
    ascii.addNote(f.str().c_str());
460
591
    return true;
461
44.2k
  }
462
10.6k
  auto decal=int(input->readULong(4));
463
10.6k
  if (decal<0x30 || input->size()<decal) {
464
127
    STOFF_DEBUG_MSG(("STOFFOLEParser::readSummaryInformation: decal is bad\n"));
465
127
    f << "decal=" << val << ",";
466
127
    ascii.addPos(0);
467
127
    ascii.addNote(f.str().c_str());
468
127
    return true;
469
470
127
  }
471
10.5k
  ascii.addPos(0);
472
10.5k
  ascii.addNote(f.str().c_str());
473
10.5k
  if (decal!=0x30) {
474
398
    ascii.addPos(0x30);
475
398
    ascii.addNote("_");
476
398
    input->seek(decal, librevenge::RVNG_SEEK_SET);
477
398
  }
478
479
10.5k
  long pos=input->tell();
480
10.5k
  f.str("");
481
10.5k
  f << "SumInfo-A:";
482
10.5k
  auto pSetSize=long(input->readULong(4));
483
10.5k
  auto N=int(input->readULong(4));
484
10.5k
  f << "N=" << N << ",";
485
10.5k
  if (pSetSize<0 || input->size()-pos<pSetSize || (pSetSize-8)/8<N) {
486
1.95k
    STOFF_DEBUG_MSG(("STOFFOLEParser::readSummaryInformation: psetstruct is bad\n"));
487
1.95k
    f << "###";
488
1.95k
    ascii.addPos(pos);
489
1.95k
    ascii.addNote(f.str().c_str());
490
1.95k
    return true;
491
1.95k
  }
492
8.59k
  f << "[";
493
8.59k
  std::map<long,int> posToTypeMap;
494
86.8k
  for (int i=0; i<N; ++i) {
495
78.2k
    auto type=int(input->readULong(4));
496
78.2k
    auto depl=int(input->readULong(4));
497
78.2k
    if (depl==0) continue;
498
77.8k
    f << std::hex << depl << std::dec << ":" << type << ",";
499
77.8k
    if (depl<8+8*N || depl>pSetSize-4 || posToTypeMap.find(pos+depl)!=posToTypeMap.end()) {
500
5.01k
      f << "###";
501
5.01k
      continue;
502
5.01k
    }
503
72.8k
    posToTypeMap[pos+depl]=type;
504
72.8k
  }
505
8.59k
  f << "],";
506
8.59k
  ascii.addPos(pos);
507
8.59k
  ascii.addNote(f.str().c_str());
508
509
72.8k
  for (auto const &posToType : posToTypeMap) {
510
72.8k
    pos=posToType.first;
511
72.8k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
512
72.8k
    f.str("");
513
72.8k
    f << "SumInfo-B" << posToType.second << ":";
514
72.8k
    auto type=int(input->readULong(4));
515
72.8k
    if (type==0x1e) {
516
55.0k
      auto sSz=long(input->readULong(4));
517
55.0k
      if (sSz<0 || (input->size()-pos-8)<sSz || pos+8+sSz>input->size()) {
518
526
        STOFF_DEBUG_MSG(("STOFFOLEParser::readSummaryInformation: string size is bad\n"));
519
526
        f << "##stringSz=" << sSz << ",";
520
526
        ascii.addPos(pos);
521
526
        ascii.addNote(f.str().c_str());
522
526
        continue;
523
526
      }
524
54.5k
      std::string text("");
525
320k
      for (long c=0; c < sSz; ++c) text+=char(input->readULong(1));
526
54.5k
      f << text;
527
54.5k
    }
528
17.7k
    else if (type==0x40) {
529
13.3k
      f << "dateTime,"; // readme 8 byte
530
13.3k
    }
531
4.47k
    else {
532
4.47k
      STOFF_DEBUG_MSG(("STOFFOLEParser::readSummaryInformation: find unknown type\n"));
533
4.47k
      f << "#type=" << std::hex << type << std::dec << ",";
534
4.47k
    }
535
72.3k
    ascii.addPos(pos);
536
72.3k
    ascii.addNote(f.str().c_str());
537
72.3k
  }
538
8.59k
  return true;
539
10.5k
}
540
541
bool STOFFOLEParser::readObjInfo(STOFFInputStreamPtr input, std::string const &oleName,
542
                                 libstoff::DebugFile &ascii)
543
302k
{
544
302k
  if (oleName!="ObjInfo") return false;
545
546
9
  if (input->size()!=6) return false;
547
548
3
  input->seek(0, librevenge::RVNG_SEEK_SET);
549
3
  libstoff::DebugStream f;
550
3
  f << "Entries(ObjInfo):";
551
552
  // always 0, 3, 4 ?
553
12
  for (int i = 0; i < 3; i++) f << input->readLong(2) << ",";
554
555
3
  ascii.addPos(0);
556
3
  ascii.addNote(f.str().c_str());
557
558
3
  return true;
559
9
}
560
561
bool STOFFOLEParser::readCompObj(STOFFInputStreamPtr ip, STOFFOLEParser::OleDirectory &directory)
562
115k
{
563
115k
  libstoff::DebugFile ascii(ip);
564
115k
  if (directory.m_dir.empty())
565
107k
    ascii.open("CompObj");
566
7.83k
  else
567
7.83k
    ascii.open(directory.m_dir+"/CompObj");
568
569
  // check minimal size
570
115k
  ip->setReadInverted(true);
571
115k
  const int minSize = 12 + 14+ 16 + 12; // size of header, clsid, footer, 3 string size
572
115k
  if (ip->seek(minSize,librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != minSize) return false;
573
574
115k
  libstoff::DebugStream f;
575
115k
  f << "Entries(CompObj)(Header): ";
576
115k
  ip->seek(0,librevenge::RVNG_SEEK_SET);
577
578
805k
  for (int i = 0; i < 6; i++) {
579
690k
    auto val = int(ip->readLong(2));
580
690k
    f << val << ", ";
581
690k
  }
582
583
115k
  ascii.addPos(0);
584
115k
  ascii.addNote(f.str().c_str());
585
586
115k
  ascii.addPos(12);
587
  // the clsid
588
115k
  unsigned long clsData[4]; // ushort n1, n2, n3, b8, ... b15
589
115k
  for (unsigned long &i : clsData)
590
460k
    i = ip->readULong(4);
591
592
115k
  f.str("");
593
115k
  f << "@@CompObj(CLSID):";
594
115k
  if (clsData[1] == 0 && clsData[2] == 0xC0 && clsData[3] == 0x46000000L) {
595
    // normally, a referenced object
596
138
    directory.m_clsName = m_state->getCLSName(clsData[0]);
597
138
    if (!directory.m_clsName.empty())
598
45
      f << "'" << directory.m_clsName << "'";
599
93
    else {
600
93
      STOFF_DEBUG_MSG(("STOFFOLEParser::readCompObj: unknown clsid=%ld\n", long(clsData[0])));
601
93
      f << "unknCLSID='" << std::hex << clsData[0] << "'";
602
93
    }
603
138
  }
604
114k
  else {
605
    /* I found:
606
      c1dbcd28e20ace11a29a00aa004a1a72     for MSWorks.Table
607
      c2dbcd28e20ace11a29a00aa004a1a72     for Microsoft Works/MSWorksWPDoc
608
      a3bcb394c2bd1b10a18306357c795b37     for Microsoft Drawing 1.01/MSDraw.1.01
609
      b25aa40e0a9ed111a40700c04fb932ba     for Quill96 Story Group Class ( basic MSWorks doc?)
610
      796827ed8bc9d111a75f00c04fb9667b     for MSWorks4Sheet
611
    */
612
114k
    f << "data0=(" << std::hex << clsData[0] << "," << clsData[1] << "), "
613
114k
      << "data1=(" << clsData[2] << "," << clsData[3] << ")";
614
114k
  }
615
115k
  ascii.addNote(f.str().c_str());
616
115k
  f << std::dec;
617
441k
  for (int ch = 0; ch < 3; ch++) {
618
335k
    long actPos = ip->tell();
619
335k
    long sz = ip->readLong(4);
620
335k
    bool waitNumber = sz == -1;
621
335k
    if (waitNumber || sz == -2) sz = 4;
622
335k
    if (sz < 0 || !ip->checkPosition(actPos+4+sz)) return false;
623
624
326k
    std::string st;
625
326k
    if (waitNumber) {
626
2.74k
      f.str("");
627
2.74k
      f << ip->readLong(4) << "[val*]";
628
2.74k
      st = f.str();
629
2.74k
    }
630
324k
    else {
631
5.93M
      for (long i = 0; i < sz; i++)
632
5.61M
        st += char(ip->readULong(1));
633
324k
    }
634
635
326k
    f.str("");
636
326k
    f << "@@CompObj:";
637
326k
    switch (ch) {
638
110k
    case 0:
639
110k
      f << "UserType=";
640
110k
      break;
641
109k
    case 1:
642
109k
      f << "ClipName=";
643
109k
      directory.m_clipName=st;
644
109k
      break;
645
106k
    case 2:
646
106k
      f << "ProgIdName=";
647
106k
      break;
648
0
    default:
649
0
      break;
650
326k
    }
651
326k
    f << st;
652
326k
    ascii.addPos(actPos);
653
326k
    ascii.addNote(f.str().c_str());
654
326k
  }
655
656
106k
  if (ip->isEnd()) return true;
657
658
56.8k
  long actPos = ip->tell();
659
56.8k
  long nbElt = 4;
660
56.8k
  if (ip->seek(actPos+16,librevenge::RVNG_SEEK_SET) != 0 ||
661
56.8k
      ip->tell() != actPos+16) {
662
39.5k
    if ((ip->tell()-actPos)%4)
663
170
      return false;
664
39.3k
    nbElt = (ip->tell()-actPos)/4;
665
39.3k
  }
666
667
56.7k
  f.str("");
668
56.7k
  f << "@@CompObj(Footer): " << std::hex;
669
56.7k
  ip->seek(actPos,librevenge::RVNG_SEEK_SET);
670
166k
  for (long i = 0; i < nbElt; i++)
671
109k
    f << ip->readULong(4) << ",";
672
56.7k
  ascii.addPos(actPos);
673
56.7k
  ascii.addNote(f.str().c_str());
674
675
56.7k
  ascii.addPos(ip->tell());
676
677
56.7k
  return true;
678
56.8k
}
679
680
//////////////////////////////////////////////////
681
//
682
// OlePres001 seems to contained standart picture file and size
683
//    extract the picture if it is possible
684
//
685
//////////////////////////////////////////////////
686
687
bool STOFFOLEParser::isOlePres(STOFFInputStreamPtr ip, std::string const &oleName)
688
309k
{
689
309k
  if (!ip.get()) return false;
690
691
309k
  if (strncmp("OlePres",oleName.c_str(),7) != 0) return false;
692
693
4.45k
  if (ip->seek(40, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 40) return false;
694
695
4.18k
  ip->seek(0, librevenge::RVNG_SEEK_SET);
696
11.7k
  for (int i= 0; i < 2; i++) {
697
7.95k
    long val = ip->readLong(4);
698
7.95k
    if (val < -10 || val > 10) {
699
4.02k
      if (i!=1 && val != 0x50494354)
700
408
        return false;
701
4.02k
    }
702
7.95k
  }
703
704
3.77k
  long actPos = ip->tell();
705
3.77k
  long hSize = ip->readLong(4);
706
3.77k
  if (hSize < 4) return false;
707
3.57k
  if (ip->seek(actPos+hSize+28, librevenge::RVNG_SEEK_SET) != 0
708
3.57k
      || ip->tell() != actPos+hSize+28)
709
306
    return false;
710
711
3.26k
  ip->seek(actPos+hSize, librevenge::RVNG_SEEK_SET);
712
15.4k
  for (int i= 3; i < 7; i++) {
713
12.5k
    long val = ip->readLong(4);
714
12.5k
    if (val < -10 || val > 10) {
715
3.17k
      if (i != 5 || val > 256) return false;
716
3.17k
    }
717
12.5k
  }
718
719
2.94k
  ip->seek(8, librevenge::RVNG_SEEK_CUR);
720
2.94k
  long size = ip->readLong(4);
721
722
2.94k
  if (size <= 0) return ip->isEnd();
723
724
2.68k
  actPos = ip->tell();
725
2.68k
  if (ip->seek(actPos+size, librevenge::RVNG_SEEK_SET) != 0
726
2.68k
      || ip->tell() != actPos+size)
727
120
    return false;
728
729
2.56k
  return true;
730
2.68k
}
731
732
bool STOFFOLEParser::readOlePres(STOFFInputStreamPtr ip, STOFFOLEParser::OleContent &content)
733
1.37k
{
734
1.37k
  if (!isOlePres(ip, "OlePres")) return false;
735
1.37k
  content.setParsed(true);
736
1.37k
  libstoff::DebugFile ascii(ip);
737
1.37k
  ascii.open(content.getOleName());
738
1.37k
  libstoff::DebugStream f;
739
1.37k
  f << "Entries(OlePress)(header): ";
740
1.37k
  ip->seek(0,librevenge::RVNG_SEEK_SET);
741
4.11k
  for (int i = 0; i < 2; i++) {
742
2.74k
    long val = ip->readLong(4);
743
2.74k
    f << val << ", ";
744
2.74k
  }
745
746
1.37k
  long actPos = ip->tell();
747
1.37k
  long hSize = ip->readLong(4);
748
1.37k
  if (hSize < 4) return false;
749
1.37k
  f << "hSize = " << hSize;
750
1.37k
  ascii.addPos(0);
751
1.37k
  ascii.addNote(f.str().c_str());
752
753
1.37k
  long endHPos = actPos+hSize;
754
1.37k
  if (!ip->checkPosition(endHPos+28)) return true;
755
1.37k
  if (hSize!=4) {
756
1.36k
    bool ok = true;
757
1.36k
    f.str("");
758
1.36k
    f << "@@OlePress(headerA): ";
759
1.36k
    if (hSize < 14) ok = false;
760
136
    else {
761
      // 12,21,32|48,0
762
680
      for (int i = 0; i < 4; i++) f << ip->readLong(2) << ",";
763
      // 3 name of creator
764
447
      for (int ch=0; ch < 3; ch++) {
765
390
        std::string str;
766
390
        bool find = false;
767
3.58k
        while (ip->tell() < endHPos) {
768
3.50k
          auto c = static_cast<unsigned char>(ip->readULong(1));
769
3.50k
          if (c == 0) {
770
311
            find = true;
771
311
            break;
772
311
          }
773
3.19k
          str += char(c);
774
3.19k
        }
775
390
        if (!find) {
776
79
          ok = false;
777
79
          break;
778
79
        }
779
311
        f << ", name" <<  ch << "=" << str;
780
311
      }
781
136
      if (ok) ok = ip->tell() == endHPos;
782
136
    }
783
    // FIXME, normally they remain only a few bits (size unknown)
784
1.36k
    if (!ok) f << "###";
785
1.36k
    ascii.addPos(actPos);
786
1.36k
    ascii.addNote(f.str().c_str());
787
1.36k
  }
788
789
1.37k
  if (!ip->checkPosition(endHPos+28))
790
0
    return true;
791
792
1.37k
  ip->seek(endHPos, librevenge::RVNG_SEEK_SET);
793
794
1.37k
  actPos = ip->tell();
795
1.37k
  f.str("");
796
1.37k
  f << "@@OlePress(headerB): ";
797
6.85k
  for (int i = 3; i < 7; i++) {
798
5.48k
    long val = ip->readLong(4);
799
5.48k
    f << val << ", ";
800
5.48k
  }
801
  // dim in TWIP ?
802
1.37k
  auto extendX = long(ip->readULong(4));
803
1.37k
  auto extendY = long(ip->readULong(4));
804
1.37k
  if (extendX > 0 && extendY > 0) {
805
1.17k
    STOFFPosition pos;
806
1.17k
    pos.m_anchorTo=STOFFPosition::Char;
807
1.17k
    pos.setSize(STOFFVec2f(float(extendX)/20.f, float(extendY)/20.f));
808
1.17k
    content.setPosition(pos);
809
1.17k
  }
810
1.37k
  long fSize = ip->readLong(4);
811
1.37k
  f << "extendX="<< extendX << ", extendY=" << extendY << ", fSize=" << fSize;
812
813
1.37k
  ascii.addPos(actPos);
814
1.37k
  ascii.addNote(f.str().c_str());
815
816
1.37k
  if (fSize == 0) return true;
817
818
1.35k
  librevenge::RVNGBinaryData data;
819
1.35k
  if (!ip->readDataBlock(fSize, data)) return true;
820
1.28k
  content.setImageData(data,"object/ole");
821
#ifdef DEBUG_WITH_FILES
822
  libstoff::Debug::dumpFile(data, content.getOleName().c_str());
823
#endif
824
825
1.28k
  if (!ip->isEnd()) {
826
1.23k
    ascii.addPos(ip->tell());
827
1.23k
    ascii.addNote("@@OlePress###");
828
1.23k
  }
829
830
1.28k
  ascii.skipZone(36+hSize,36+hSize+fSize-1);
831
1.28k
  return true;
832
1.35k
}
833
834
//////////////////////////////////////////////////
835
//
836
//  Ole10Native: basic Windows picture, with no size
837
//          - in general used to store a bitmap
838
//          - may also store some Math StarOffice document
839
//
840
//////////////////////////////////////////////////
841
842
bool STOFFOLEParser::isOle10Native(STOFFInputStreamPtr ip, std::string const &oleName)
843
306k
{
844
306k
  if (strncmp("Ole10Native",oleName.c_str(),11) != 0) return false;
845
846
285
  if (ip->seek(4, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 4) return false;
847
848
256
  ip->seek(0, librevenge::RVNG_SEEK_SET);
849
256
  long size = ip->readLong(4);
850
851
256
  if (size <= 0) return false;
852
167
  if (ip->seek(4+size, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 4+size)
853
93
    return false;
854
855
74
  return true;
856
167
}
857
858
bool STOFFOLEParser::readOle10Native(STOFFInputStreamPtr ip, STOFFOLEParser::OleContent &content)
859
37
{
860
37
  if (!isOle10Native(ip, "Ole10Native")) return false;
861
862
37
  content.setParsed(true);
863
37
  libstoff::DebugFile ascii(ip);
864
37
  ascii.open(content.getOleName());
865
37
  libstoff::DebugStream f;
866
37
  f << "Entries(Ole10Native)(Header): ";
867
37
  ip->seek(0,librevenge::RVNG_SEEK_SET);
868
37
  long fSize = ip->readLong(4);
869
37
  f << "fSize=" << fSize;
870
37
  if (ip->readULong(4)==0x10001) { // TODO: check if the OLE is a old StarMathDocument 2.0
871
0
    STOFF_DEBUG_MSG(("STOFFOLEParser::readOle10Native: find an OLE which can be a StarMathDocument\n"));
872
0
  }
873
37
  ascii.addPos(0);
874
37
  ascii.addNote(f.str().c_str());
875
876
37
  librevenge::RVNGBinaryData data;
877
37
  ip->seek(4, librevenge::RVNG_SEEK_SET);
878
37
  if (!ip->readDataBlock(fSize, data)) return false;
879
37
  content.setImageData(data,"object/ole");
880
#ifdef DEBUG_WITH_FILES
881
  libstoff::Debug::dumpFile(data, content.getOleName().c_str());
882
#endif
883
884
37
  if (!ip->isEnd()) {
885
30
    ascii.addPos(ip->tell());
886
30
    ascii.addNote("@@Ole10Native###");
887
30
  }
888
37
  ascii.skipZone(4,4+fSize-1);
889
37
  return true;
890
37
}
891
892
////////////////////////////////////////////////////////////////
893
//
894
// In general a picture : a PNG, an JPEG, a basic metafile,
895
//    find also a MSDraw.1.01 picture (with first bytes 0x78563412="xV4") or WordArt,
896
//    ( with first bytes "WordArt" )  which are not sucefull read
897
//    (can probably contain a list of data, but do not know how to
898
//     detect that)
899
//
900
// To check: does this is related to MSO_BLIPTYPE ?
901
//        or OO/filter/sources/msfilter/msdffimp.cxx ?
902
//
903
////////////////////////////////////////////////////////////////
904
bool STOFFOLEParser::readContents(STOFFInputStreamPtr input, STOFFOLEParser::OleContent &content)
905
306k
{
906
306k
  if (content.getBaseName()!="Contents") return false;
907
908
3.66k
  content.setParsed(true);
909
3.66k
  libstoff::DebugFile ascii(input);
910
3.66k
  ascii.open(content.getOleName());
911
3.66k
  libstoff::DebugStream f;
912
3.66k
  input->seek(0, librevenge::RVNG_SEEK_SET);
913
3.66k
  f << "Entries(Contents):";
914
915
3.66k
  bool ok = true;
916
  // bdbox 0 : size in the file ?
917
3.66k
  long dim[2];
918
3.66k
  dim[0] = input->readLong(4);
919
3.66k
  dim[1] = input->readLong(4);
920
3.66k
  f << "bdbox0=(" << dim[0] << "," << dim[1]<<"),";
921
14.6k
  for (int i = 0; i < 3; i++) {
922
    /* 0,{10|21|75|101|116}x2 */
923
10.9k
    auto val = long(input->readULong(4));
924
10.9k
    if (val < 1000)
925
4.01k
      f << val << ",";
926
6.98k
    else
927
6.98k
      f << std::hex << "0x" << val << std::dec << ",";
928
10.9k
    if (val > 0x10000) ok=false;
929
10.9k
  }
930
  // new bdbox : size of the picture ?
931
3.66k
  long naturalSize[2];
932
3.66k
  naturalSize[0] = input->readLong(4);
933
3.66k
  naturalSize[1] = input->readLong(4);
934
3.66k
  f << std::dec << "bdbox1=(" << naturalSize[0] << "," << naturalSize[1]<<"),";
935
3.66k
  f << "unk=" << input->readULong(4) << ","; // 24 or 32
936
3.66k
  if (input->isEnd()) {
937
93
    STOFF_DEBUG_MSG(("STOFFOLEParser: warning: Contents header length\n"));
938
93
    return false;
939
93
  }
940
3.57k
  STOFFPosition pos;
941
3.57k
  pos.m_anchorTo=STOFFPosition::Char;
942
3.57k
  bool sizeSet=false;
943
3.57k
  if (dim[0] > 0 && dim[0] < 3000 &&
944
1.37k
      dim[1] > 0 && dim[1] < 3000) {
945
742
    pos.setSize(STOFFVec2f(float(dim[0]),float(dim[1])));
946
742
    sizeSet=true;
947
742
  }
948
2.83k
  else {
949
2.83k
    STOFF_DEBUG_MSG(("STOFFOLEParser: warning: Contents odd size : %ld %ld\n", dim[0], dim[1]));
950
2.83k
  }
951
3.57k
  if (naturalSize[0] > 0 && naturalSize[0] < 5000 &&
952
1.79k
      naturalSize[1] > 0 && naturalSize[1] < 5000) {
953
792
    if (!sizeSet)
954
280
      pos.setSize(STOFFVec2f(float(naturalSize[0]),float(naturalSize[1])));
955
792
  }
956
2.78k
  else {
957
2.78k
    STOFF_DEBUG_MSG(("STOFFOLEParser: warning: Contents odd naturalsize : %ld %ld\n", naturalSize[0], naturalSize[1]));
958
2.78k
  }
959
3.57k
  content.setPosition(pos);
960
961
3.57k
  long actPos = input->tell();
962
3.57k
  auto size = long(input->readULong(4));
963
3.57k
  if (size <= 0) ok = false;
964
3.57k
  if (ok) {
965
909
    input->seek(actPos+size+4, librevenge::RVNG_SEEK_SET);
966
909
    if (input->tell() != actPos+size+4 || !input->isEnd()) {
967
744
      ok = false;
968
744
      STOFF_DEBUG_MSG(("STOFFOLEParser: warning: Contents unexpected file size=%ld\n",
969
744
                       size));
970
744
    }
971
909
  }
972
973
3.57k
  if (!ok) f << "###";
974
3.57k
  f << "dataSize=" << size;
975
976
3.57k
  ascii.addPos(0);
977
3.57k
  ascii.addNote(f.str().c_str());
978
979
3.57k
  input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
980
981
3.57k
  if (ok) {
982
165
    librevenge::RVNGBinaryData pict;
983
165
    if (input->readDataBlock(size, pict)) {
984
165
      content.setImageData(pict,"object/ole");
985
#ifdef DEBUG_WITH_FILES
986
      libstoff::Debug::dumpFile(pict, content.getOleName().c_str());
987
#endif
988
165
      ascii.skipZone(actPos+4, actPos+size+4-1);
989
165
    }
990
0
    else {
991
0
      input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
992
0
      ok = false;
993
0
    }
994
165
  }
995
996
3.57k
  if (!input->isEnd()) {
997
3.39k
    ascii.addPos(actPos);
998
3.39k
    ascii.addNote("@@Contents:###");
999
3.39k
  }
1000
1001
3.57k
  if (!ok) {
1002
3.40k
    STOFF_DEBUG_MSG(("STOFFOLEParser: warning: read ole Contents: failed\n"));
1003
3.40k
  }
1004
3.57k
  return true;
1005
3.66k
}
1006
1007
////////////////////////////////////////////////////////////////
1008
//
1009
// Another different type of contents (this time in majuscule)
1010
// we seem to contain the header of a EMF and then the EMF file
1011
//
1012
////////////////////////////////////////////////////////////////
1013
bool STOFFOLEParser::readCONTENTS(STOFFInputStreamPtr input, STOFFOLEParser::OleContent &content)
1014
303k
{
1015
303k
  if (content.getBaseName()!="CONTENTS") return false;
1016
1017
747
  content.setParsed(true);
1018
747
  libstoff::DebugFile ascii(input);
1019
747
  ascii.open(content.getOleName());
1020
747
  libstoff::DebugStream f;
1021
1022
747
  input->seek(0, librevenge::RVNG_SEEK_SET);
1023
747
  f << "Entries(CONTMAJ):";
1024
1025
747
  auto hSize = long(input->readULong(4));
1026
747
  if (input->isEnd()) return false;
1027
715
  f << "hSize=" << std::hex << hSize << std::dec;
1028
1029
715
  if (hSize <= 52 || input->seek(hSize+8,librevenge::RVNG_SEEK_SET) != 0
1030
676
      || input->tell() != hSize+8) {
1031
160
    STOFF_DEBUG_MSG(("STOFFOLEParser: warning: CONTENTS headerSize=%ld\n",
1032
160
                     hSize));
1033
160
    return false;
1034
160
  }
1035
1036
  // minimal checking of the "copied" header
1037
555
  input->seek(4, librevenge::RVNG_SEEK_SET);
1038
555
  auto type = long(input->readULong(4));
1039
555
  if (type < 0 || type > 4) return false;
1040
512
  auto newSize = long(input->readULong(4));
1041
1042
512
  f << ", type=" << type;
1043
512
  if (newSize < 8) return false;
1044
1045
471
  if (newSize != hSize) // can sometimes happen, pb after a conversion ?
1046
468
    f << ", ###newSize=" << std::hex << newSize << std::dec;
1047
1048
  // checkme: two bdbox, in document then data : units ?
1049
  //     Maybe first in POINT, second in TWIP ?
1050
471
  STOFFPosition pos;
1051
471
  pos.m_anchorTo=STOFFPosition::Char;
1052
1.41k
  for (int st = 0; st < 2 ; st++) {
1053
942
    long dim[4];
1054
3.76k
    for (long &i : dim) i = input->readLong(4);
1055
1056
942
    bool ok = dim[0] >= 0 && dim[2] > dim[0] && dim[1] >= 0 && dim[3] > dim[2];
1057
942
    if (ok && st==0) pos.setSize(STOFFVec2f(float(dim[2]-dim[0]), float(dim[3]-dim[1])));
1058
942
    if (st==0) f << ", bdbox(Text)";
1059
471
    else f << ", bdbox(Data)";
1060
942
    if (!ok) f << "###";
1061
942
    f << "=(" << dim[0] << "x" << dim[1] << "<->" << dim[2] << "x" << dim[3] << ")";
1062
942
  }
1063
471
  content.setPosition(pos);
1064
471
  char dataType[5];
1065
2.35k
  for (int i = 0; i < 4; i++) dataType[i] = char(input->readULong(1));
1066
471
  dataType[4] = '\0';
1067
471
  f << ",typ=\""<<dataType<<"\""; // always " EMF" ?
1068
1069
1.41k
  for (int i = 0; i < 2; i++) { // always id0=0, id1=1 ?
1070
942
    auto val = int(input->readULong(2));
1071
942
    if (val) f << ",id"<< i << "=" << val;
1072
942
  }
1073
471
  auto dataLength = long(input->readULong(4));
1074
471
  f << ",length=" << dataLength+hSize;
1075
1076
471
  ascii.addPos(0);
1077
471
  ascii.addNote(f.str().c_str());
1078
1079
471
  ascii.addPos(input->tell());
1080
471
  f.str("");
1081
471
  f << "@@CONTMAJ(2)";
1082
3.19k
  for (int i = 0; i < 12 && 4*i+52 < hSize; i++) {
1083
    // f0=7,f1=1,f5=500,f6=320,f7=1c4,f8=11a
1084
    // or f0=a,f1=1,f2=2,f3=6c,f5=480,f6=360,f7=140,f8=f0
1085
    // or f0=61,f1=1,f2=2,f3=58,f5=280,f6=1e0,f7=a9,f8=7f
1086
    // f3=some header sub size ? f5/f6 and f7/f8 two other bdbox ?
1087
2.72k
    auto val = long(input->readULong(4));
1088
2.72k
    if (val) f << std::hex << ",f" << i << "=" << val;
1089
2.72k
  }
1090
14.5k
  for (int i = 0; 2*i+100 < hSize; i++) {
1091
    // g0=e3e3,g1=6,g2=4e6e,g3=4
1092
    // g0=e200,g1=4,g2=a980,g3=3,g4=4c,g5=50
1093
    // ---
1094
14.1k
    auto val = long(input->readULong(2));
1095
14.1k
    if (val) f << std::hex << ",g" << i << "=" << val;
1096
14.1k
  }
1097
471
  ascii.addNote(f.str().c_str());
1098
1099
471
  if (dataLength <= 0 || input->seek(hSize+4+dataLength,librevenge::RVNG_SEEK_SET) != 0
1100
468
      || input->tell() != hSize+4+dataLength || !input->isEnd()) {
1101
468
    STOFF_DEBUG_MSG(("STOFFOLEParser: warning: CONTENTS unexpected file length=%ld\n",
1102
468
                     dataLength));
1103
468
    return true;
1104
468
  }
1105
1106
3
  input->seek(4+hSize, librevenge::RVNG_SEEK_SET);
1107
3
  librevenge::RVNGBinaryData pict;
1108
3
  if (!input->readEndDataBlock(pict)) return true;
1109
3
  content.setImageData(pict,"object/ole");
1110
#ifdef DEBUG_WITH_FILES
1111
  libstoff::Debug::dumpFile(pict, content.getOleName().c_str());
1112
#endif
1113
1114
3
  ascii.skipZone(hSize+4, input->tell()-1);
1115
3
  return true;
1116
3
}
1117
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: