/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: |