Coverage Report

Created: 2026-03-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libstaroffice/src/lib/SDGParser.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
#include <cstring>
35
#include <iomanip>
36
#include <iostream>
37
#include <limits>
38
#include <sstream>
39
40
#include <librevenge/librevenge.h>
41
42
#include "STOFFFrameStyle.hxx"
43
#include "STOFFGraphicListener.hxx"
44
#include "STOFFOLEParser.hxx"
45
#include "STOFFSubDocument.hxx"
46
47
#include "StarBitmap.hxx"
48
#include "StarZone.hxx"
49
50
#include "SDGParser.hxx"
51
52
/** Internal: the structures of a SDGParser */
53
namespace SDGParserInternal
54
{
55
56
////////////////////////////////////////
57
//! Internal: small class use to store an image content in a SDGParser
58
class Image
59
{
60
public:
61
  //! constructor
62
  Image()
63
151k
    : m_object()
64
151k
    , m_size()
65
151k
    , m_link()
66
151k
  {
67
151k
  }
68
  //! the object
69
  STOFFEmbeddedObject m_object;
70
  //! the bitmap size
71
  STOFFVec2i m_size;
72
  //! the link name
73
  librevenge::RVNGString m_link;
74
};
75
76
////////////////////////////////////////
77
//! Internal: the state of a SDGParser
78
struct State {
79
  //! constructor
80
  State()
81
13.8k
    : m_imagesList()
82
13.8k
  {
83
13.8k
  }
84
85
  //! the list of image
86
  std::vector<Image> m_imagesList;
87
};
88
89
////////////////////////////////////////
90
//! Internal: the subdocument of a SDGParser
91
class SubDocument final : public STOFFSubDocument
92
{
93
public:
94
  explicit SubDocument(librevenge::RVNGString const &text)
95
20.7k
    : STOFFSubDocument(nullptr, STOFFInputStreamPtr(), STOFFEntry())
96
20.7k
    , m_text(text) {}
97
98
  //! destructor
99
20.7k
  ~SubDocument() final {}
100
101
  //! operator!=
102
  bool operator!=(STOFFSubDocument const &doc) const final
103
0
  {
104
0
    if (STOFFSubDocument::operator!=(doc)) return true;
105
0
    auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
106
0
    if (!sDoc) return true;
107
0
    if (m_text != sDoc->m_text) return true;
108
0
    return false;
109
0
  }
110
111
  //! the parser function
112
  void parse(STOFFListenerPtr &listener, libstoff::SubDocumentType type) final;
113
114
protected:
115
  //! the text
116
  librevenge::RVNGString m_text;
117
};
118
119
void SubDocument::parse(STOFFListenerPtr &listener, libstoff::SubDocumentType /*type*/)
120
20.7k
{
121
20.7k
  if (!listener.get()) {
122
0
    STOFF_DEBUG_MSG(("StarObjectSmallGraphicInternal::SubDocument::parse: no listener\n"));
123
0
    return;
124
0
  }
125
20.7k
  if (m_text.empty())
126
0
    listener->insertChar(' ');
127
20.7k
  else
128
20.7k
    listener->insertUnicodeString(m_text);
129
20.7k
}
130
}
131
132
////////////////////////////////////////////////////////////
133
// constructor/destructor, ...
134
////////////////////////////////////////////////////////////
135
SDGParser::SDGParser(STOFFInputStreamPtr &input, STOFFHeader *header)
136
5.54k
  : STOFFGraphicParser(input, header)
137
5.54k
  , m_password(nullptr)
138
5.54k
  , m_state(new SDGParserInternal::State)
139
5.54k
{
140
5.54k
}
141
142
SDGParser::~SDGParser()
143
5.54k
{
144
5.54k
}
145
146
////////////////////////////////////////////////////////////
147
// the parser
148
////////////////////////////////////////////////////////////
149
void SDGParser::parse(librevenge::RVNGDrawingInterface *docInterface)
150
2.76k
{
151
2.76k
  if (!getInput().get() || !checkHeader(nullptr))  throw(libstoff::ParseException());
152
2.76k
  bool ok = true;
153
2.76k
  try {
154
2.76k
    checkHeader(nullptr);
155
2.76k
    ok = createZones();
156
2.76k
    if (ok) {
157
589
      createDocument(docInterface);
158
589
      STOFFListenerPtr listener=getGraphicListener();
159
589
      if (listener) {
160
589
        STOFFFrameStyle frame;
161
589
        auto &position=frame.m_position;
162
589
        position.setAnchor(STOFFPosition::Page);
163
589
        STOFFGraphicStyle style;
164
589
        style.m_propertyList.insert("draw:stroke", "none");
165
589
        style.m_propertyList.insert("draw:fill", "none");
166
589
        bool first=true;
167
27.0k
        for (auto const &image : m_state->m_imagesList) {
168
27.0k
          if (image.m_object.isEmpty())
169
0
            continue;
170
27.0k
          if (!first)
171
26.4k
            listener->insertBreak(STOFFListener::PageBreak);
172
589
          else
173
589
            first=false;
174
27.0k
          position.setOrigin(STOFFVec2f(20,20));
175
27.0k
          STOFFVec2f size=(image.m_size[0]>0 && image.m_size[1]>0) ? STOFFVec2f(image.m_size) : STOFFVec2f(400,400);
176
27.0k
          position.setSize(size);
177
27.0k
          listener->insertPicture(frame, image.m_object, style);
178
27.0k
          if (!image.m_link.empty()) {
179
20.7k
            std::shared_ptr<SDGParserInternal::SubDocument> doc(new SDGParserInternal::SubDocument(image.m_link));
180
20.7k
            position.setOrigin(STOFFVec2f(20,30+size[1]));
181
20.7k
            position.setSize(STOFFVec2f(600,200));
182
20.7k
            listener->insertTextBox(frame, doc, style);
183
20.7k
          }
184
27.0k
        }
185
589
      }
186
589
    }
187
2.76k
    ascii().reset();
188
2.76k
  }
189
2.76k
  catch (...) {
190
0
    STOFF_DEBUG_MSG(("SDGParser::parse: exception catched when parsing\n"));
191
0
    ok = false;
192
0
  }
193
194
2.76k
  resetGraphicListener();
195
2.76k
  if (!ok) throw(libstoff::ParseException());
196
2.76k
}
197
198
199
bool SDGParser::createZones()
200
2.76k
{
201
2.76k
  STOFFInputStreamPtr input=getInput();
202
2.76k
  if (!input)
203
0
    return false;
204
2.76k
  StarZone zone(input, "SDGDoc", "SDGDocument", m_password); // checkme: do we need to pass the password
205
2.76k
  libstoff::DebugFile &ascFile=zone.ascii();
206
2.76k
  ascFile.open("main-1");
207
208
2.76k
  ascFile.addPos(0);
209
2.76k
  ascFile.addNote("FileHeader");
210
211
2.76k
  input->seek(0, librevenge::RVNG_SEEK_SET);
212
2.76k
  long pos=input->tell();
213
154k
  while (!input->isEnd() && readSGA3(zone))
214
152k
    pos=input->tell();
215
2.76k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
216
2.76k
  pos=input->tell();
217
2.76k
  ascFile.addPos(pos);
218
2.76k
  ascFile.addNote("SGA3:##extra");
219
2.76k
  return !m_state->m_imagesList.empty();
220
2.76k
}
221
222
////////////////////////////////////////////////////////////
223
// create the document and send data
224
////////////////////////////////////////////////////////////
225
void SDGParser::createDocument(librevenge::RVNGDrawingInterface *documentInterface)
226
589
{
227
589
  if (!documentInterface) return;
228
229
589
  auto numImages=int(m_state->m_imagesList.size());
230
589
  std::vector<STOFFPageSpan> pageList;
231
589
  STOFFPageSpan ps(getPageSpan());
232
589
  ps.m_pageSpan=numImages ? numImages : 1;
233
589
  pageList.push_back(ps);
234
589
  STOFFGraphicListenerPtr listen(new STOFFGraphicListener(getParserState()->m_listManager, pageList, documentInterface));
235
589
  setGraphicListener(listen);
236
237
589
  listen->startDocument();
238
589
}
239
240
////////////////////////////////////////////////////////////
241
//
242
// Intermediate level
243
//
244
////////////////////////////////////////////////////////////
245
bool SDGParser::readSGA3(StarZone &zone)
246
154k
{
247
154k
  STOFFInputStreamPtr input=zone.input();
248
154k
  if (!input || input->isEnd())
249
0
    return false;
250
154k
  long pos=input->tell();
251
  // look for 53474133
252
154k
  bool findHeader=false;
253
4.66M
  while (true) {
254
4.66M
    if (!input->checkPosition(input->tell()+10)) {
255
2.03k
      findHeader=false;
256
2.03k
      break;
257
2.03k
    }
258
4.66M
    long val=int(input->readULong(4));
259
4.66M
    if (val==0x33414753) {
260
152k
      findHeader=true;
261
152k
      break;
262
152k
    }
263
4.51M
    if ((val>>8)==0x414753)
264
57.0k
      input->seek(-3, librevenge::RVNG_SEEK_CUR);
265
4.45M
    else if ((val>>16)==0x4753)
266
57.7k
      input->seek(-2, librevenge::RVNG_SEEK_CUR);
267
4.39M
    else if ((val>>24)==0x47)
268
19.2k
      input->seek(-1, librevenge::RVNG_SEEK_CUR);
269
4.51M
  }
270
154k
  libstoff::DebugFile &ascFile=zone.ascii();
271
154k
  libstoff::DebugStream f;
272
154k
  f << "Entries(SGA3):";
273
154k
  if (!findHeader) {
274
2.03k
    STOFF_DEBUG_MSG(("SDGParser::readSGA3: can not find the header\n"));
275
2.03k
    f << "###header";
276
2.03k
  }
277
152k
  else if (input->tell()!=pos+4) {
278
126k
    STOFF_DEBUG_MSG(("SDGParser::readSGA3: find unknown header\n"));
279
126k
    ascFile.addPos(pos);
280
126k
    ascFile.addNote("Entries(SGA3):###unknown");
281
126k
    pos=input->tell()-4;
282
126k
  }
283
154k
  ascFile.addPos(pos);
284
154k
  ascFile.addNote(f.str().c_str());
285
154k
  if (findHeader)
286
152k
    readBitmap(zone);
287
154k
  return findHeader;
288
154k
}
289
290
bool SDGParser::readBitmap(StarZone &zone)
291
152k
{
292
152k
  STOFFInputStreamPtr input=zone.input();
293
152k
  if (!input)
294
0
    return false;
295
152k
  libstoff::DebugFile &ascFile=zone.ascii();
296
152k
  libstoff::DebugStream f;
297
152k
  long pos=input->tell();
298
152k
  if (!input->checkPosition(pos+7)) return false;
299
151k
  f << "Entries(SGA3):";
300
607k
  for (int i=0; i<3; ++i) { // f0=4, f1=5|6, f2=1|2|5
301
455k
    auto val=int(input->readULong(2));
302
455k
    int const expected[]= {4,5,1};
303
455k
    if (val!=expected[i]) f << "f" << i << "=" << val << ",";
304
455k
  }
305
151k
  SDGParserInternal::Image image;
306
151k
  int val;
307
207k
  for (int step=0; step<2; ++step) {
308
190k
    auto type=int(input->readULong(1));
309
190k
    if (type>2) {
310
22.9k
      input->seek(-1, librevenge::RVNG_SEEK_CUR);
311
22.9k
      break;
312
22.9k
    }
313
168k
    f << "type=" << type << ",";
314
168k
    if (type) {
315
124k
      StarBitmap bitmap;
316
124k
      librevenge::RVNGBinaryData data;
317
124k
      std::string bType;
318
124k
      val=int(input->readULong(2));
319
124k
      input->seek(-2, librevenge::RVNG_SEEK_CUR);
320
124k
      if (val!=0x4D42 || !bitmap.readBitmap(zone, true, input->size(), data, bType)) {
321
79.5k
        STOFF_DEBUG_MSG(("SDGParser::readBitmap: sorry, can not read a bitmap\n"));
322
79.5k
        input->seek(pos, librevenge::RVNG_SEEK_SET);
323
79.5k
        f << "###";
324
79.5k
        ascFile.addPos(pos);
325
79.5k
        ascFile.addNote(f.str().c_str());
326
79.5k
        return false;
327
79.5k
      }
328
44.6k
      else if (step==0 && bitmap.getData(data, bType)) {
329
27.0k
        image.m_object.add(data, bType);
330
27.0k
        image.m_size=bitmap.getBitmapSize();
331
27.0k
      }
332
44.6k
      ascFile.addPos(pos);
333
44.6k
      ascFile.addNote(f.str().c_str());
334
44.6k
      pos=input->tell();
335
44.6k
      f.str("");
336
44.6k
      f << "SGA3:";
337
44.6k
    }
338
339
88.4k
    val=int(input->readULong(2));
340
88.4k
    bool findText=false;
341
88.4k
    if (val==0x1962) {
342
264
      f << "empty,";
343
1.05k
      for (int i=0; i<3; ++i) {
344
792
        val=int(input->readULong(2));
345
792
        int const expected[]= {0x2509, 0x201, 0xacb2};
346
792
        if (val!=expected[i])
347
792
          f << "f" << i << "=" << std::hex << val << std::dec << ",";
348
792
      }
349
264
    }
350
88.1k
    else {
351
88.1k
      input->seek(-2, librevenge::RVNG_SEEK_CUR);
352
175k
      for (int i=0; i<2; ++i) {
353
133k
        std::vector<uint32_t> text;
354
133k
        val=int(input->readULong(2));
355
133k
        if (val==0x5300 || (type==0 && val>=0x80)) {
356
45.1k
          input->seek(-2, librevenge::RVNG_SEEK_CUR);
357
45.1k
          break;
358
45.1k
        }
359
87.8k
        if (val>=0x80) {
360
31.0k
          f << "val" << i << "=" << std::hex << val << std::dec << ",";
361
31.0k
          continue;
362
31.0k
        }
363
56.7k
        input->seek(-2, librevenge::RVNG_SEEK_CUR);
364
56.7k
        if (!zone.readString(text)) {
365
548
          STOFF_DEBUG_MSG(("SDGParser::readBitmap: sorry, can not read a text zone\n"));
366
548
          input->seek(pos, librevenge::RVNG_SEEK_SET);
367
548
          f << "###";
368
548
          ascFile.addPos(pos);
369
548
          ascFile.addNote(f.str().c_str());
370
548
          return false;
371
548
        }
372
56.2k
        else if (!text.empty()) {
373
26.2k
          if (i==0)
374
24.2k
            image.m_link=libstoff::getString(text);
375
26.2k
          f << "text" << i << "=" << libstoff::getString(text).cstr() << ",";
376
26.2k
        }
377
56.2k
        findText=true;
378
56.2k
      }
379
88.1k
    }
380
87.9k
    ascFile.addPos(pos);
381
87.9k
    ascFile.addNote(f.str().c_str());
382
87.9k
    pos=input->tell();
383
87.9k
    f.str("");
384
87.9k
    f << "SGA3:";
385
87.9k
    if (findText) break;
386
87.9k
  }
387
71.8k
  if (!image.m_object.isEmpty())
388
27.0k
    m_state->m_imagesList.push_back(image);
389
71.8k
  if (input->checkPosition(input->tell()+2)) {
390
71.6k
    long actPos=input->tell();
391
71.6k
    val=int(input->readULong(2));
392
71.6k
    if (val==0x4753 || val==0x5300)
393
14.6k
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
394
57.0k
    else {
395
57.0k
      f << "val=" << std::hex << val << std::dec << ",";
396
57.0k
      val=int(input->readULong(2));
397
57.0k
      if (val==0x4753)
398
5.99k
        input->seek(actPos+2, librevenge::RVNG_SEEK_SET);
399
51.0k
      else
400
51.0k
        input->seek(actPos+12, librevenge::RVNG_SEEK_SET);
401
57.0k
    }
402
71.6k
  }
403
71.8k
  if (pos!=input->tell()) {
404
58.5k
    ascFile.addPos(pos);
405
58.5k
    ascFile.addNote(f.str().c_str());
406
58.5k
  }
407
71.8k
  return true;
408
151k
}
409
////////////////////////////////////////////////////////////
410
//
411
// Low level
412
//
413
////////////////////////////////////////////////////////////
414
415
////////////////////////////////////////////////////////////
416
// read the header
417
////////////////////////////////////////////////////////////
418
bool SDGParser::checkHeader(STOFFHeader *header, bool /*strict*/)
419
8.30k
{
420
8.30k
  *m_state = SDGParserInternal::State();
421
422
8.30k
  STOFFInputStreamPtr input = getInput();
423
8.30k
  input->setReadInverted(true);
424
8.30k
  if (!input || !input->hasDataFork() || input->isStructured() || input->size()<30)
425
16
    return false;
426
8.28k
  input->seek(0, librevenge::RVNG_SEEK_SET);
427
8.28k
  if (input->readULong(4)!=0x33414753) // SGA3
428
0
    return false;
429
8.28k
  if (header)
430
2.76k
    header->reset(1, STOFFDocument::STOFF_K_GRAPHIC);
431
8.28k
  return true;
432
8.28k
}
433
434
435
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: