/src/libvisio/src/lib/VisioDocument.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 libvisio 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 <libvisio/libvisio.h> |
11 | | |
12 | | #include <algorithm> |
13 | | #include <memory> |
14 | | #include <string> |
15 | | |
16 | | #include <librevenge/librevenge.h> |
17 | | #include "libvisio_utils.h" |
18 | | #include "libvisio_xml.h" |
19 | | #include "VDXParser.h" |
20 | | #include "VSDParser.h" |
21 | | #include "VSDXParser.h" |
22 | | #include "VSD5Parser.h" |
23 | | #include "VSD6Parser.h" |
24 | | #include "VSDXMLHelper.h" |
25 | | |
26 | | namespace |
27 | | { |
28 | | |
29 | | static bool checkVisioMagic(librevenge::RVNGInputStream *input) |
30 | 23.3k | { |
31 | 23.3k | const unsigned char magic[] = |
32 | 23.3k | { |
33 | 23.3k | 0x56, 0x69, 0x73, 0x69, 0x6f, 0x20, 0x28, 0x54, 0x4d, 0x29, |
34 | 23.3k | 0x20, 0x44, 0x72, 0x61, 0x77, 0x69, 0x6e, 0x67, 0x0d, 0x0a, |
35 | 23.3k | 0x0 |
36 | 23.3k | }; |
37 | 23.3k | auto startPosition = (int)input->tell(); |
38 | 23.3k | unsigned long numBytesRead = 0; |
39 | 23.3k | const unsigned char *buffer = input->read(VSD_NUM_ELEMENTS(magic), numBytesRead); |
40 | 23.3k | const bool returnValue = VSD_NUM_ELEMENTS(magic) == numBytesRead |
41 | 21.9k | && std::equal(magic, magic + VSD_NUM_ELEMENTS(magic), buffer); |
42 | 23.3k | input->seek(startPosition, librevenge::RVNG_SEEK_SET); |
43 | 23.3k | return returnValue; |
44 | 23.3k | } |
45 | | |
46 | 23.3k | static bool isBinaryVisioDocument(librevenge::RVNGInputStream *input) try |
47 | 23.3k | { |
48 | 23.3k | std::shared_ptr<librevenge::RVNGInputStream> docStream; |
49 | 23.3k | input->seek(0, librevenge::RVNG_SEEK_SET); |
50 | 23.3k | if (input->isStructured()) |
51 | 2.62k | { |
52 | 2.62k | input->seek(0, librevenge::RVNG_SEEK_SET); |
53 | 2.62k | docStream.reset(input->getSubStreamByName("VisioDocument")); |
54 | 2.62k | } |
55 | 23.3k | if (!docStream) |
56 | 22.3k | docStream.reset(input, libvisio::VSDDummyDeleter()); |
57 | | |
58 | 23.3k | docStream->seek(0, librevenge::RVNG_SEEK_SET); |
59 | 23.3k | unsigned char version = 0; |
60 | 23.3k | if (checkVisioMagic(docStream.get())) |
61 | 9.69k | { |
62 | 9.69k | docStream->seek(0x1A, librevenge::RVNG_SEEK_SET); |
63 | 9.69k | version = libvisio::readU8(docStream.get()); |
64 | 9.69k | } |
65 | 23.3k | input->seek(0, librevenge::RVNG_SEEK_SET); |
66 | | |
67 | 23.3k | VSD_DEBUG_MSG(("VisioDocument: version %i\n", version)); |
68 | | |
69 | | // Versions 2k (6) and 2k3 (11) |
70 | 23.3k | return ((version >= 1 && version <= 6) || version == 11); |
71 | 23.3k | } |
72 | 23.3k | catch (...) |
73 | 23.3k | { |
74 | 1 | return false; |
75 | 1 | } |
76 | | |
77 | 9.69k | static bool parseBinaryVisioDocument(librevenge::RVNGInputStream *input, librevenge::RVNGDrawingInterface *painter, bool isStencilExtraction) try |
78 | 9.69k | { |
79 | 9.69k | VSD_DEBUG_MSG(("Parsing Binary Visio Document\n")); |
80 | 9.69k | input->seek(0, librevenge::RVNG_SEEK_SET); |
81 | 9.69k | std::shared_ptr<librevenge::RVNGInputStream> docStream; |
82 | 9.69k | if (input->isStructured()) |
83 | 864 | docStream.reset(input->getSubStreamByName("VisioDocument")); |
84 | 9.69k | if (!docStream) |
85 | 8.82k | docStream.reset(input, libvisio::VSDDummyDeleter()); |
86 | | |
87 | 9.69k | docStream->seek(0x1A, librevenge::RVNG_SEEK_SET); |
88 | | |
89 | 9.69k | std::unique_ptr<libvisio::VSDParser> parser; |
90 | | |
91 | 9.69k | unsigned char version = libvisio::readU8(docStream.get()); |
92 | 9.69k | switch (version) |
93 | 9.69k | { |
94 | 13 | case 1: |
95 | 91 | case 2: |
96 | 451 | case 3: |
97 | 1.81k | case 4: |
98 | 5.41k | case 5: |
99 | 5.41k | parser.reset(new libvisio::VSD5Parser(docStream.get(), painter)); |
100 | 5.41k | break; |
101 | 2.08k | case 6: |
102 | 2.08k | parser.reset(new libvisio::VSD6Parser(docStream.get(), painter)); |
103 | 2.08k | break; |
104 | 2.19k | case 11: |
105 | 2.19k | parser.reset(new libvisio::VSDParser(docStream.get(), painter, input)); |
106 | 2.19k | break; |
107 | 0 | default: |
108 | 0 | break; |
109 | 9.69k | } |
110 | | |
111 | 9.69k | if (isStencilExtraction) |
112 | 0 | return parser->extractStencils(); |
113 | 9.69k | else |
114 | 9.69k | return parser->parseMain(); |
115 | 9.69k | } |
116 | 9.69k | catch (...) |
117 | 9.69k | { |
118 | 14 | return false; |
119 | 14 | } |
120 | | |
121 | 13.6k | static bool isOpcVisioDocument(librevenge::RVNGInputStream *input) try |
122 | 13.6k | { |
123 | 13.6k | input->seek(0, librevenge::RVNG_SEEK_SET); |
124 | 13.6k | if (!input->isStructured()) |
125 | 11.9k | return false; |
126 | | |
127 | 1.75k | std::unique_ptr<librevenge::RVNGInputStream> tmpInput(input->getSubStreamByName("_rels/.rels")); |
128 | 1.75k | if (!tmpInput) |
129 | 874 | return false; |
130 | | |
131 | 884 | libvisio::VSDXRelationships rootRels(tmpInput.get()); |
132 | | |
133 | | // Check whether the relationship points to a Visio document stream |
134 | 884 | const libvisio::VSDXRelationship *rel = rootRels.getRelationshipByType("http://schemas.microsoft.com/visio/2010/relationships/document"); |
135 | 884 | if (!rel) |
136 | 28 | return false; |
137 | | |
138 | | // check whether the pointed Visio document stream exists in the document |
139 | 856 | tmpInput.reset(input->getSubStreamByName(rel->getTarget().c_str())); |
140 | 856 | return bool(tmpInput); |
141 | 884 | } |
142 | 13.6k | catch (...) |
143 | 13.6k | { |
144 | 0 | return false; |
145 | 0 | } |
146 | | |
147 | 821 | static bool parseOpcVisioDocument(librevenge::RVNGInputStream *input, librevenge::RVNGDrawingInterface *painter, bool isStencilExtraction) try |
148 | 821 | { |
149 | 821 | VSD_DEBUG_MSG(("Parsing Visio Document based on Open Packaging Convention\n")); |
150 | 821 | input->seek(0, librevenge::RVNG_SEEK_SET); |
151 | 821 | libvisio::VSDXParser parser(input, painter); |
152 | 821 | if (isStencilExtraction && parser.extractStencils()) |
153 | 0 | return true; |
154 | 821 | else if (!isStencilExtraction && parser.parseMain()) |
155 | 658 | return true; |
156 | 163 | return false; |
157 | 821 | } |
158 | 821 | catch (...) |
159 | 821 | { |
160 | 0 | return false; |
161 | 0 | } |
162 | | |
163 | 12.8k | static bool isXmlVisioDocument(librevenge::RVNGInputStream *input) try |
164 | 12.8k | { |
165 | 12.8k | input->seek(0, librevenge::RVNG_SEEK_SET); |
166 | 12.8k | auto reader = libvisio::xmlReaderForStream(input); |
167 | 12.8k | if (!reader) |
168 | 0 | return false; |
169 | 12.8k | int ret = xmlTextReaderRead(reader.get()); |
170 | 20.3k | while (ret == 1 && 1 != xmlTextReaderNodeType(reader.get())) |
171 | 7.51k | ret = xmlTextReaderRead(reader.get()); |
172 | 12.8k | if (ret != 1) |
173 | 7.97k | { |
174 | 7.97k | return false; |
175 | 7.97k | } |
176 | 4.87k | const xmlChar *name = xmlTextReaderConstName(reader.get()); |
177 | 4.87k | if (!name) |
178 | 0 | { |
179 | 0 | return false; |
180 | 0 | } |
181 | 4.87k | if (!xmlStrEqual(name, BAD_CAST("VisioDocument"))) |
182 | 450 | { |
183 | 450 | return false; |
184 | 450 | } |
185 | 4.42k | return true; |
186 | 4.87k | } |
187 | 12.8k | catch (...) |
188 | 12.8k | { |
189 | 0 | return false; |
190 | 0 | } |
191 | | |
192 | 4.42k | static bool parseXmlVisioDocument(librevenge::RVNGInputStream *input, librevenge::RVNGDrawingInterface *painter, bool isStencilExtraction) try |
193 | 4.42k | { |
194 | 4.42k | VSD_DEBUG_MSG(("Parsing Visio DrawingML Document\n")); |
195 | 4.42k | input->seek(0, librevenge::RVNG_SEEK_SET); |
196 | 4.42k | libvisio::VDXParser parser(input, painter); |
197 | 4.42k | if (isStencilExtraction && parser.extractStencils()) |
198 | 0 | return true; |
199 | 4.42k | else if (!isStencilExtraction && parser.parseMain()) |
200 | 3.93k | return true; |
201 | 488 | return false; |
202 | 4.42k | } |
203 | 4.42k | catch (...) |
204 | 4.42k | { |
205 | 0 | return false; |
206 | 0 | } |
207 | | |
208 | | } // anonymous namespace |
209 | | |
210 | | |
211 | | /** |
212 | | Analyzes the content of an input stream to see if it can be parsed |
213 | | \param input The input stream |
214 | | \return A value that indicates whether the content from the input |
215 | | stream is a Visio Document that libvisio able to parse |
216 | | */ |
217 | | VSDAPI bool libvisio::VisioDocument::isSupported(librevenge::RVNGInputStream *input) |
218 | 0 | { |
219 | 0 | if (!input) |
220 | 0 | return false; |
221 | | |
222 | 0 | if (isBinaryVisioDocument(input)) |
223 | 0 | return true; |
224 | 0 | if (isOpcVisioDocument(input)) |
225 | 0 | return true; |
226 | 0 | if (isXmlVisioDocument(input)) |
227 | 0 | return true; |
228 | 0 | return false; |
229 | 0 | } |
230 | | |
231 | | /** |
232 | | Parses the input stream content. It will make callbacks to the functions provided by a |
233 | | librevenge::RVNGDrawingInterface class implementation when needed. This is often commonly called the |
234 | | 'main parsing routine'. |
235 | | \param input The input stream |
236 | | \param painter A WPGPainterInterface implementation |
237 | | \return A value that indicates whether the parsing was successful |
238 | | */ |
239 | | VSDAPI bool libvisio::VisioDocument::parse(librevenge::RVNGInputStream *input, librevenge::RVNGDrawingInterface *painter) |
240 | 23.3k | { |
241 | 23.3k | if (!input || !painter) |
242 | 0 | return false; |
243 | | |
244 | 23.3k | if (isBinaryVisioDocument(input)) |
245 | 9.69k | { |
246 | 9.69k | if (parseBinaryVisioDocument(input, painter, false)) |
247 | 8.34k | return true; |
248 | 1.34k | return false; |
249 | 9.69k | } |
250 | 13.6k | if (isOpcVisioDocument(input)) |
251 | 821 | { |
252 | 821 | if (parseOpcVisioDocument(input, painter, false)) |
253 | 658 | return true; |
254 | 163 | return false; |
255 | 821 | } |
256 | 12.8k | if (isXmlVisioDocument(input)) |
257 | 4.42k | { |
258 | 4.42k | if (parseXmlVisioDocument(input, painter, false)) |
259 | 3.93k | return true; |
260 | 488 | return false; |
261 | 4.42k | } |
262 | 8.42k | return false; |
263 | 12.8k | } |
264 | | |
265 | | /** |
266 | | Parses the input stream content and extracts stencil pages, one stencil page per output page. |
267 | | It will make callbacks to the functions provided by a librevenge::RVNGDrawingInterface class implementation |
268 | | when needed. |
269 | | \param input The input stream |
270 | | \param painter A WPGPainterInterface implementation |
271 | | \return A value that indicates whether the parsing was successful |
272 | | */ |
273 | | VSDAPI bool libvisio::VisioDocument::parseStencils(librevenge::RVNGInputStream *input, librevenge::RVNGDrawingInterface *painter) |
274 | 0 | { |
275 | 0 | if (!input || !painter) |
276 | 0 | return false; |
277 | | |
278 | 0 | if (isBinaryVisioDocument(input)) |
279 | 0 | { |
280 | 0 | if (parseBinaryVisioDocument(input, painter, true)) |
281 | 0 | return true; |
282 | 0 | return false; |
283 | 0 | } |
284 | 0 | if (isOpcVisioDocument(input)) |
285 | 0 | { |
286 | 0 | if (parseOpcVisioDocument(input, painter, true)) |
287 | 0 | return true; |
288 | 0 | return false; |
289 | 0 | } |
290 | 0 | if (isXmlVisioDocument(input)) |
291 | 0 | { |
292 | 0 | if (parseXmlVisioDocument(input, painter, true)) |
293 | 0 | return true; |
294 | 0 | return false; |
295 | 0 | } |
296 | 0 | return false; |
297 | 0 | } |
298 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |