/src/libetonyek/src/lib/EtonyekDocument.cpp
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* |
3 | | * This file is part of the libetonyek project. |
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 | | |
10 | | #include <libetonyek/libetonyek.h> |
11 | | |
12 | | #include <cassert> |
13 | | #include <cstring> |
14 | | #include <memory> |
15 | | |
16 | | #include <boost/algorithm/string/predicate.hpp> |
17 | | #include <boost/optional.hpp> |
18 | | |
19 | | #include <libxml/xmlreader.h> |
20 | | |
21 | | #include "libetonyek_utils.h" |
22 | | #include "libetonyek_xml.h" |
23 | | #include "IWAMessage.h" |
24 | | #include "IWASnappyStream.h" |
25 | | #include "IWORKPresentationRedirector.h" |
26 | | #include "IWORKSpreadsheetRedirector.h" |
27 | | #include "IWORKSubDirStream.h" |
28 | | #include "IWORKTextRedirector.h" |
29 | | #include "IWORKTokenizer.h" |
30 | | #include "IWORKZlibStream.h" |
31 | | #include "KEY1Dictionary.h" |
32 | | #include "KEY1Parser.h" |
33 | | #include "KEY1Token.h" |
34 | | #include "KEY2Dictionary.h" |
35 | | #include "KEY2Parser.h" |
36 | | #include "KEY2Token.h" |
37 | | #include "KEY6Parser.h" |
38 | | #include "KEYCollector.h" |
39 | | #include "NUMCollector.h" |
40 | | #include "NUM1Dictionary.h" |
41 | | #include "NUM1Parser.h" |
42 | | #include "NUM1Token.h" |
43 | | #include "NUM3Parser.h" |
44 | | #include "PAGCollector.h" |
45 | | #include "PAG1Dictionary.h" |
46 | | #include "PAG1Parser.h" |
47 | | #include "PAG1Token.h" |
48 | | #include "PAG5Parser.h" |
49 | | |
50 | | |
51 | | using std::shared_ptr; |
52 | | using std::string; |
53 | | |
54 | | using librevenge::RVNG_SEEK_SET; |
55 | | |
56 | | namespace libetonyek |
57 | | { |
58 | | |
59 | | namespace |
60 | | { |
61 | | |
62 | | enum Format |
63 | | { |
64 | | FORMAT_UNKNOWN, |
65 | | FORMAT_XML1, |
66 | | FORMAT_XML2, |
67 | | FORMAT_BINARY |
68 | | }; |
69 | | |
70 | | struct DetectionInfo |
71 | | { |
72 | | explicit DetectionInfo(EtonyekDocument::Type type = EtonyekDocument::TYPE_UNKNOWN); |
73 | | |
74 | | RVNGInputStreamPtr_t m_input; |
75 | | RVNGInputStreamPtr_t m_package; |
76 | | RVNGInputStreamPtr_t m_fragments; |
77 | | EtonyekDocument::Confidence m_confidence; |
78 | | EtonyekDocument::Type m_type; |
79 | | Format m_format; |
80 | | }; |
81 | | |
82 | | DetectionInfo::DetectionInfo(const EtonyekDocument::Type type) |
83 | 24.9k | : m_input() |
84 | 24.9k | , m_package() |
85 | 24.9k | , m_fragments() |
86 | 24.9k | , m_confidence(EtonyekDocument::CONFIDENCE_NONE) |
87 | 24.9k | , m_type(type) |
88 | 24.9k | , m_format(FORMAT_UNKNOWN) |
89 | 24.9k | { |
90 | 24.9k | } |
91 | | |
92 | | bool probeXMLFormat(const Format format, const EtonyekDocument::Type type, const int docId, |
93 | | const IWORKTokenizer &tokenizer, const char *const name, const char *const ns, |
94 | | DetectionInfo &info) |
95 | 3.57k | { |
96 | 3.57k | if (((info.m_format == format) || (info.m_format == FORMAT_UNKNOWN)) |
97 | 3.57k | && ((info.m_type == type) || info.m_type == EtonyekDocument::TYPE_UNKNOWN)) |
98 | 1.78k | { |
99 | 1.78k | if (tokenizer.getQualifiedId(name, ns) == docId) |
100 | 0 | { |
101 | 0 | info.m_format = format; |
102 | 0 | info.m_type = type; |
103 | 0 | return true; |
104 | 0 | } |
105 | 1.78k | } |
106 | 3.57k | return false; |
107 | 3.57k | } |
108 | | |
109 | | namespace |
110 | | { |
111 | | void handleError(void * /*arg*/, const char * /*msg*/, xmlParserSeverities /*severity*/, xmlTextReaderLocatorPtr /*locator*/) |
112 | 474k | { |
113 | 474k | } |
114 | | } |
115 | | |
116 | | bool probeXML(DetectionInfo &info) |
117 | 20.8k | { |
118 | 20.8k | const auto reader = xmlReaderForStream(info.m_input); |
119 | 20.8k | if (!reader) |
120 | 0 | return false; |
121 | | |
122 | 20.8k | xmlTextReaderSetErrorHandler(reader.get(), handleError, nullptr); |
123 | | |
124 | 20.8k | int ret = 0; |
125 | 20.8k | bool checkAPXL=false; |
126 | 20.8k | do |
127 | 107k | { |
128 | 107k | ret = xmlTextReaderRead(reader.get()); |
129 | 107k | if (ret==1 && XML_READER_TYPE_DOCUMENT_TYPE==xmlTextReaderNodeType(reader.get())) |
130 | 103 | { |
131 | 103 | auto name = xmlTextReaderConstName(reader.get()); |
132 | 103 | if (name) checkAPXL=std::string((char const *)name)=="APXL"; |
133 | 103 | } |
134 | 107k | } |
135 | 107k | while ((1 == ret) && (XML_READER_TYPE_ELEMENT != xmlTextReaderNodeType(reader.get()))); |
136 | | |
137 | 20.8k | if (1 != ret) |
138 | 19.9k | return false; |
139 | | |
140 | 893 | const char *const name = char_cast(xmlTextReaderConstLocalName(reader.get())); |
141 | 893 | const char *const ns = char_cast(xmlTextReaderConstNamespaceUri(reader.get())); |
142 | 893 | if (probeXMLFormat(FORMAT_XML2, EtonyekDocument::TYPE_KEYNOTE, +KEY2Token::NS_URI_KEY | KEY2Token::presentation, |
143 | 893 | KEY2Token::getTokenizer(), name, ns, info)) |
144 | 0 | return true; |
145 | 893 | if (probeXMLFormat(FORMAT_XML2, EtonyekDocument::TYPE_NUMBERS, +NUM1Token::NS_URI_LS | NUM1Token::document, |
146 | 893 | NUM1Token::getTokenizer(), name, ns, info)) |
147 | 0 | return true; |
148 | 893 | if (probeXMLFormat(FORMAT_XML2, EtonyekDocument::TYPE_PAGES, +PAG1Token::NS_URI_SL | PAG1Token::document, |
149 | 893 | PAG1Token::getTokenizer(), name, ns, info)) |
150 | 0 | return true; |
151 | | // Keynote 1 files define the document type with <!DOCTYPE APXL> |
152 | 893 | if (probeXMLFormat(FORMAT_XML1, EtonyekDocument::TYPE_KEYNOTE, +KEY1Token::NS_URI_KEY | KEY1Token::presentation, |
153 | 893 | KEY1Token::getTokenizer(), name, (ns||!checkAPXL) ? ns : "http://developer.apple.com/schemas/APXL", info)) |
154 | 0 | return true; |
155 | 893 | return false; |
156 | 893 | } |
157 | | |
158 | | bool probeBinary(DetectionInfo &info) |
159 | 2.95k | { |
160 | 2.95k | const uint64_t headerLen = readUVar(info.m_input); |
161 | 2.95k | if (headerLen < 8) |
162 | 4 | return false; |
163 | | |
164 | 2.95k | EtonyekDocument::Type detected = EtonyekDocument::TYPE_UNKNOWN; |
165 | | |
166 | 2.95k | const auto pos = uint64_t(info.m_input->tell()); |
167 | 2.95k | const IWAMessage header(info.m_input, (unsigned long) headerLen); |
168 | | |
169 | 2.95k | if (header.uint32(1) && header.message(2) && header.message(2).uint32(1) && (header.uint32(1).get() == 1)) |
170 | 2.51k | { |
171 | 2.51k | switch (header.message(2).uint32(1).get()) |
172 | 2.51k | { |
173 | 2.51k | case 1 : |
174 | 2.51k | if (header.message(2).uint32(3)) |
175 | 2.51k | { |
176 | 2.51k | uint32_t dataLen = 0; |
177 | 2.51k | for (auto const &infoT : header.message(2)) |
178 | 2.56k | { |
179 | 2.56k | if (infoT.uint32(3)) dataLen += infoT.uint32(3).get(); |
180 | 2.56k | } |
181 | 2.51k | const IWAMessage data(info.m_input, long(pos + headerLen), long(pos + headerLen + dataLen)); |
182 | | // keynote: presentation ref in 2 |
183 | | // number: sheet ref in 1 |
184 | 2.51k | if (!data.message(1)) |
185 | 2.42k | detected = EtonyekDocument::TYPE_KEYNOTE; |
186 | 87 | else if (!data.message(2)) |
187 | 2 | detected = EtonyekDocument::TYPE_NUMBERS; |
188 | 85 | else |
189 | 85 | { |
190 | 85 | unsigned potentialRef[2]; |
191 | 227 | for (unsigned test=1; test<=2; ++test) |
192 | 154 | { |
193 | 154 | auto ref=data.message(test).uint32(1).optional(); |
194 | 154 | if (!ref) |
195 | 12 | { |
196 | 12 | detected = test==1 ? EtonyekDocument::TYPE_KEYNOTE : EtonyekDocument::TYPE_NUMBERS; |
197 | 12 | break; |
198 | 12 | } |
199 | 142 | potentialRef[test-1]=get(ref); |
200 | 142 | } |
201 | 85 | if (detected != EtonyekDocument::TYPE_UNKNOWN) |
202 | 12 | break; |
203 | | // undecise, try to find the first ref |
204 | 73 | IWAObjectIndex objIndex(info.m_fragments, info.m_package); |
205 | 73 | objIndex.parse(); |
206 | 73 | auto type = objIndex.getObjectType(potentialRef[0]); |
207 | 73 | detected = type && get(type)==2 ? |
208 | 73 | EtonyekDocument::TYPE_NUMBERS : EtonyekDocument::TYPE_KEYNOTE; |
209 | 73 | } |
210 | 2.51k | } |
211 | 2.50k | break; |
212 | 2.50k | case 10000 : |
213 | 0 | detected = EtonyekDocument::TYPE_PAGES; |
214 | 0 | break; |
215 | 5 | default: |
216 | 5 | break; |
217 | 2.51k | } |
218 | 2.51k | } |
219 | | |
220 | 2.93k | if ((info.m_type == EtonyekDocument::TYPE_UNKNOWN) || (info.m_type == detected)) |
221 | 2.49k | { |
222 | 2.49k | info.m_type = detected; |
223 | 2.49k | return true; |
224 | 2.49k | } |
225 | 446 | return false; |
226 | 2.93k | } |
227 | | |
228 | | RVNGInputStreamPtr_t getSubStream(const RVNGInputStreamPtr_t &input, const char *const name) |
229 | 99 | { |
230 | 99 | return RVNGInputStreamPtr_t(input->getSubStreamByName(name)); |
231 | 99 | } |
232 | | |
233 | 3.23k | RVNGInputStreamPtr_t getUncompressedSubStream(const RVNGInputStreamPtr_t &input, const char *const name, bool snappy = false) try |
234 | 3.23k | { |
235 | 3.23k | const RVNGInputStreamPtr_t compressed(input->getSubStreamByName(name)); |
236 | 3.23k | if (bool(compressed)) |
237 | 3.18k | { |
238 | 3.18k | if (snappy) |
239 | 3.18k | return RVNGInputStreamPtr_t(new IWASnappyStream(compressed)); |
240 | 1 | return RVNGInputStreamPtr_t(new IWORKZlibStream(compressed)); |
241 | 3.18k | } |
242 | 48 | return RVNGInputStreamPtr_t(); |
243 | 3.23k | } |
244 | 3.23k | catch (...) |
245 | 3.23k | { |
246 | 229 | return RVNGInputStreamPtr_t(); |
247 | 229 | } |
248 | | |
249 | | bool detectBinary(RVNGInputStreamPtr_t input, DetectionInfo &info) |
250 | 4.49k | { |
251 | 4.49k | assert(input->isStructured()); |
252 | | |
253 | 4.49k | if (input->existsSubStream("Metadata/DocumentIdentifier")) |
254 | 671 | info.m_package = input; |
255 | | |
256 | 4.49k | if (input->existsSubStream("Index.zip")) |
257 | 96 | { |
258 | 96 | RVNGInputStreamPtr_t zipInput = getSubStream(input, "Index.zip"); |
259 | 96 | if (bool(zipInput)) |
260 | 11 | input = zipInput; |
261 | 96 | } |
262 | | |
263 | 4.49k | const bool hasDocument = input->existsSubStream("Index/Document.iwa"); |
264 | | |
265 | 4.49k | if (hasDocument) |
266 | 3.23k | { |
267 | 3.23k | info.m_format = FORMAT_BINARY; |
268 | 3.23k | info.m_fragments = input; |
269 | 3.23k | info.m_input = getUncompressedSubStream(input, "Index/Document.iwa", true); |
270 | 3.23k | } |
271 | | |
272 | 4.49k | return hasDocument; |
273 | 4.49k | } |
274 | | |
275 | | RVNGInputStreamPtr_t queryTopDirStream(const RVNGInputStreamPtr_t &input) |
276 | 916 | { |
277 | 916 | assert(input->isStructured()); |
278 | | |
279 | 916 | string top; |
280 | | |
281 | | // find the common top level dir of all substream names, if there is one |
282 | 3.34k | for (unsigned i = 0; i < input->subStreamCount(); ++i) |
283 | 2.58k | { |
284 | 2.58k | const char *const path = input->subStreamName(i); |
285 | 2.58k | if (path) |
286 | 2.58k | { |
287 | 2.58k | if (top.empty()) |
288 | 882 | { |
289 | | // initialize top dir |
290 | 882 | const char *const pos = std::strchr(path, '/'); |
291 | 882 | if (pos) |
292 | 588 | top.assign(path, std::size_t(pos - path)); |
293 | 294 | else |
294 | 294 | top = path; |
295 | 882 | } |
296 | 1.70k | else |
297 | 1.70k | { |
298 | | // check that the current path starts with top dir |
299 | 1.70k | if (!boost::starts_with(path, top)) |
300 | 152 | return RVNGInputStreamPtr_t(); |
301 | 1.55k | const char end = path[top.size()]; |
302 | 1.55k | if (end != '/' && end != '\0') |
303 | 2 | return RVNGInputStreamPtr_t(); |
304 | 1.55k | } |
305 | 2.58k | } |
306 | 2.58k | } |
307 | | |
308 | 762 | RVNGInputStreamPtr_t stream; |
309 | 762 | if (!top.empty()) |
310 | 342 | stream.reset(new IWORKSubDirStream(input, top)); |
311 | | |
312 | 762 | return stream; |
313 | 916 | } |
314 | | |
315 | | bool detect(const RVNGInputStreamPtr_t &input, DetectionInfo &info) |
316 | 24.9k | { |
317 | 24.9k | if (input->isStructured()) |
318 | 4.14k | { |
319 | 4.14k | if ((info.m_format == FORMAT_BINARY) || (info.m_format == FORMAT_UNKNOWN)) |
320 | 4.14k | { |
321 | 4.14k | if (!detectBinary(input, info)) |
322 | 916 | { |
323 | 916 | RVNGInputStreamPtr_t dir = queryTopDirStream(input); |
324 | 916 | if (dir) |
325 | 342 | detectBinary(dir, info); |
326 | 916 | } |
327 | 4.14k | } |
328 | | |
329 | 4.14k | if ((info.m_format == FORMAT_XML2) || (info.m_format == FORMAT_UNKNOWN)) |
330 | 915 | { |
331 | 915 | info.m_package = input; |
332 | | |
333 | 915 | if ((info.m_type == EtonyekDocument::TYPE_KEYNOTE) || (info.m_type == EtonyekDocument::TYPE_UNKNOWN)) |
334 | 915 | { |
335 | 915 | if (input->existsSubStream("index.apxl")) |
336 | 1 | { |
337 | 1 | info.m_format = FORMAT_XML2; |
338 | 1 | info.m_type = EtonyekDocument::TYPE_KEYNOTE; |
339 | 1 | info.m_input = getSubStream(input, "index.apxl"); |
340 | 1 | } |
341 | 914 | else if (input->existsSubStream("index.apxl.gz")) |
342 | 0 | { |
343 | 0 | info.m_format = FORMAT_XML2; |
344 | 0 | info.m_type = EtonyekDocument::TYPE_KEYNOTE; |
345 | 0 | info.m_input = getUncompressedSubStream(input, "index.apxl.gz"); |
346 | 0 | } |
347 | 915 | } |
348 | | |
349 | 915 | if ((info.m_type == EtonyekDocument::TYPE_NUMBERS) || (info.m_type == EtonyekDocument::TYPE_PAGES) || (info.m_type == EtonyekDocument::TYPE_UNKNOWN)) |
350 | 0 | { |
351 | 0 | if (input->existsSubStream("index.xml")) |
352 | 0 | { |
353 | 0 | info.m_format = FORMAT_XML2; |
354 | 0 | info.m_input = getSubStream(input, "index.xml"); |
355 | 0 | } |
356 | 0 | else if (input->existsSubStream("index.xml.gz")) |
357 | 0 | { |
358 | 0 | info.m_format = FORMAT_XML2; |
359 | 0 | info.m_input = getUncompressedSubStream(input, "index.xml.gz"); |
360 | 0 | } |
361 | 0 | } |
362 | 915 | } |
363 | | |
364 | 4.14k | if (info.m_format == FORMAT_XML1 || info.m_format == FORMAT_UNKNOWN) |
365 | 914 | { |
366 | 914 | info.m_package = input; |
367 | | |
368 | 914 | if (input->existsSubStream("presentation.apxl")) |
369 | 2 | { |
370 | 2 | info.m_type = EtonyekDocument::TYPE_KEYNOTE; |
371 | 2 | info.m_format = FORMAT_XML1; |
372 | 2 | info.m_input = getSubStream(input, "presentation.apxl"); |
373 | 2 | } |
374 | 912 | else if (input->existsSubStream("presentation.apxl.gz")) |
375 | 1 | { |
376 | 1 | info.m_type = EtonyekDocument::TYPE_KEYNOTE; |
377 | 1 | info.m_format = FORMAT_XML1; |
378 | 1 | info.m_input = getUncompressedSubStream(input, "presentation.apxl.gz"); |
379 | 1 | } |
380 | 914 | } |
381 | 4.14k | } |
382 | 20.8k | else |
383 | 20.8k | { |
384 | 20.8k | try |
385 | 20.8k | { |
386 | 20.8k | info.m_input = std::make_shared<IWORKZlibStream>(input); |
387 | 20.8k | } |
388 | 20.8k | catch (...) |
389 | 20.8k | { |
390 | 18.2k | info.m_input = input; |
391 | 18.2k | } |
392 | 20.8k | } |
393 | | |
394 | 24.9k | if (bool(info.m_input)) |
395 | 23.7k | { |
396 | 23.7k | assert(!info.m_input->isStructured()); |
397 | 23.7k | info.m_input->seek(0, RVNG_SEEK_SET); |
398 | | |
399 | 23.7k | bool supported = false; |
400 | 23.7k | if (info.m_format == FORMAT_BINARY) |
401 | 2.95k | supported = probeBinary(info); |
402 | 20.8k | else |
403 | 20.8k | supported = probeXML(info); |
404 | 23.7k | if (supported) |
405 | 2.49k | info.m_confidence = bool(info.m_package) ? EtonyekDocument::CONFIDENCE_EXCELLENT : EtonyekDocument::CONFIDENCE_SUPPORTED_PART; |
406 | 23.7k | } |
407 | | |
408 | 24.9k | if (info.m_confidence != EtonyekDocument::CONFIDENCE_NONE) |
409 | 2.49k | { |
410 | 2.49k | assert(EtonyekDocument::TYPE_UNKNOWN != info.m_type); |
411 | 2.49k | assert(FORMAT_UNKNOWN != info.m_format); |
412 | 2.49k | assert(bool(info.m_input)); |
413 | 2.49k | if (info.m_confidence == EtonyekDocument::CONFIDENCE_EXCELLENT) |
414 | 669 | { |
415 | 669 | assert(bool(info.m_package)); |
416 | 669 | } |
417 | 2.49k | } |
418 | | |
419 | 24.9k | return info.m_confidence != EtonyekDocument::CONFIDENCE_NONE; |
420 | 24.9k | } |
421 | | |
422 | | } |
423 | | |
424 | 0 | ETONYEKAPI EtonyekDocument::Confidence EtonyekDocument::isSupported(librevenge::RVNGInputStream *const input, EtonyekDocument::Type *type) try |
425 | 0 | { |
426 | 0 | if (!input) |
427 | 0 | return CONFIDENCE_NONE; |
428 | | |
429 | 0 | if (type) |
430 | 0 | *type = TYPE_UNKNOWN; |
431 | |
|
432 | 0 | DetectionInfo info; |
433 | |
|
434 | 0 | if (detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), info)) |
435 | 0 | { |
436 | 0 | if (type) |
437 | 0 | *type = info.m_type; |
438 | 0 | return info.m_confidence; |
439 | 0 | } |
440 | | |
441 | 0 | return CONFIDENCE_NONE; |
442 | 0 | } |
443 | 0 | catch (...) |
444 | 0 | { |
445 | 0 | return CONFIDENCE_NONE; |
446 | 0 | } |
447 | | |
448 | 24.9k | ETONYEKAPI bool EtonyekDocument::parse(librevenge::RVNGInputStream *const input, librevenge::RVNGPresentationInterface *const generator) try |
449 | 24.9k | { |
450 | 24.9k | if (!input || !generator) |
451 | 0 | return false; |
452 | | |
453 | 24.9k | DetectionInfo info(EtonyekDocument::TYPE_KEYNOTE); |
454 | | |
455 | 24.9k | if (!detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), info)) |
456 | 22.4k | return false; |
457 | | |
458 | 2.56k | info.m_input->seek(0, librevenge::RVNG_SEEK_SET); |
459 | | |
460 | 2.56k | IWORKPresentationRedirector redirector(generator); |
461 | 2.56k | KEYCollector collector(&redirector); |
462 | 2.56k | if (info.m_format == FORMAT_XML1) |
463 | 0 | { |
464 | 0 | KEY1Dictionary dict; |
465 | 0 | input->seek(0, librevenge::RVNG_SEEK_SET); |
466 | 0 | shared_ptr<KEY1Parser> key1Parser(new KEY1Parser(info.m_input, info.m_package, collector, dict)); |
467 | 0 | return key1Parser->parse(); |
468 | 0 | } |
469 | 2.56k | else if (info.m_format == FORMAT_XML2) |
470 | 0 | { |
471 | 0 | KEY2Dictionary dict; |
472 | 0 | shared_ptr<KEY2Parser> key2Parser(new KEY2Parser(info.m_input, info.m_package, collector, dict)); |
473 | 0 | return key2Parser->parse(); |
474 | 0 | } |
475 | 2.56k | else if (info.m_format == FORMAT_BINARY) |
476 | 2.49k | { |
477 | 2.49k | KEY6Parser parser(info.m_fragments, info.m_package, collector); |
478 | 2.49k | return parser.parse(); |
479 | 2.49k | } |
480 | | |
481 | 73 | ETONYEK_DEBUG_MSG(("EtonyekDocument::parse: unhandled format %d\n", info.m_format)); |
482 | 73 | return false; |
483 | 2.56k | } |
484 | 24.9k | catch (...) |
485 | 24.9k | { |
486 | 826 | return false; |
487 | 826 | } |
488 | | |
489 | 0 | ETONYEKAPI bool EtonyekDocument::parse(librevenge::RVNGInputStream *const input, librevenge::RVNGSpreadsheetInterface *const document) try |
490 | 0 | { |
491 | 0 | if (!input || !document) |
492 | 0 | return false; |
493 | | |
494 | 0 | DetectionInfo info(EtonyekDocument::TYPE_NUMBERS); |
495 | |
|
496 | 0 | if (!detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), info)) |
497 | 0 | return false; |
498 | | |
499 | 0 | info.m_input->seek(0, librevenge::RVNG_SEEK_SET); |
500 | |
|
501 | 0 | IWORKSpreadsheetRedirector redirector(document); |
502 | 0 | NUMCollector collector(&redirector); |
503 | 0 | if (info.m_format == FORMAT_XML2) |
504 | 0 | { |
505 | 0 | NUM1Dictionary dict; |
506 | 0 | NUM1Parser parser(info.m_input, info.m_package, collector, &dict); |
507 | 0 | return parser.parse(); |
508 | 0 | } |
509 | 0 | else if (info.m_format == FORMAT_BINARY) |
510 | 0 | { |
511 | 0 | NUM3Parser parser(info.m_fragments, info.m_package, collector); |
512 | 0 | return parser.parse(); |
513 | 0 | } |
514 | | |
515 | 0 | ETONYEK_DEBUG_MSG(("EtonyekDocument::parse: unhandled format %d\n", info.m_format)); |
516 | 0 | return false; |
517 | 0 | } |
518 | 0 | catch (...) |
519 | 0 | { |
520 | 0 | return false; |
521 | 0 | } |
522 | | |
523 | 0 | ETONYEKAPI bool EtonyekDocument::parse(librevenge::RVNGInputStream *const input, librevenge::RVNGTextInterface *const document) try |
524 | 0 | { |
525 | 0 | if (!input || !document) |
526 | 0 | return false; |
527 | | |
528 | 0 | DetectionInfo info(EtonyekDocument::TYPE_PAGES); |
529 | |
|
530 | 0 | if (!detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), info)) |
531 | 0 | return false; |
532 | | |
533 | 0 | info.m_input->seek(0, librevenge::RVNG_SEEK_SET); |
534 | |
|
535 | 0 | IWORKTextRedirector redirector(document); |
536 | 0 | PAGCollector collector(&redirector); |
537 | 0 | if (info.m_format == FORMAT_XML2) |
538 | 0 | { |
539 | 0 | PAG1Dictionary dict; |
540 | 0 | PAG1Parser parser(info.m_input, info.m_package, collector, &dict); |
541 | 0 | return parser.parse(); |
542 | 0 | } |
543 | 0 | else if (info.m_format == FORMAT_BINARY) |
544 | 0 | { |
545 | 0 | PAG5Parser parser(info.m_fragments, info.m_package, collector); |
546 | 0 | return parser.parse(); |
547 | 0 | } |
548 | | |
549 | 0 | ETONYEK_DEBUG_MSG(("EtonyekDocument::parse: unhandled format %d\n", info.m_format)); |
550 | 0 | return false; |
551 | 0 | } |
552 | 0 | catch (...) |
553 | 0 | { |
554 | 0 | return false; |
555 | 0 | } |
556 | | |
557 | | } |
558 | | |
559 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |