/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 | 57.9k | { |
31 | 57.9k | const unsigned char magic[] = |
32 | 57.9k | { |
33 | 57.9k | 0x56, 0x69, 0x73, 0x69, 0x6f, 0x20, 0x28, 0x54, 0x4d, 0x29, |
34 | 57.9k | 0x20, 0x44, 0x72, 0x61, 0x77, 0x69, 0x6e, 0x67, 0x0d, 0x0a, |
35 | 57.9k | 0x0 |
36 | 57.9k | }; |
37 | 57.9k | auto startPosition = (int)input->tell(); |
38 | 57.9k | unsigned long numBytesRead = 0; |
39 | 57.9k | const unsigned char *buffer = input->read(VSD_NUM_ELEMENTS(magic), numBytesRead); |
40 | 57.9k | const bool returnValue = VSD_NUM_ELEMENTS(magic) == numBytesRead |
41 | 54.4k | && std::equal(magic, magic + VSD_NUM_ELEMENTS(magic), buffer); |
42 | 57.9k | input->seek(startPosition, librevenge::RVNG_SEEK_SET); |
43 | 57.9k | return returnValue; |
44 | 57.9k | } |
45 | | |
46 | 57.9k | static bool isBinaryVisioDocument(librevenge::RVNGInputStream *input) try |
47 | 57.9k | { |
48 | 57.9k | std::shared_ptr<librevenge::RVNGInputStream> docStream; |
49 | 57.9k | input->seek(0, librevenge::RVNG_SEEK_SET); |
50 | 57.9k | if (input->isStructured()) |
51 | 4.04k | { |
52 | 4.04k | input->seek(0, librevenge::RVNG_SEEK_SET); |
53 | 4.04k | docStream.reset(input->getSubStreamByName("VisioDocument")); |
54 | 4.04k | } |
55 | 57.9k | if (!docStream) |
56 | 56.9k | docStream.reset(input, libvisio::VSDDummyDeleter()); |
57 | | |
58 | 57.9k | docStream->seek(0, librevenge::RVNG_SEEK_SET); |
59 | 57.9k | unsigned char version = 0; |
60 | 57.9k | if (checkVisioMagic(docStream.get())) |
61 | 15.9k | { |
62 | 15.9k | docStream->seek(0x1A, librevenge::RVNG_SEEK_SET); |
63 | 15.9k | version = libvisio::readU8(docStream.get()); |
64 | 15.9k | } |
65 | 57.9k | input->seek(0, librevenge::RVNG_SEEK_SET); |
66 | | |
67 | 57.9k | VSD_DEBUG_MSG(("VisioDocument: version %i\n", version)); |
68 | | |
69 | | // Versions 2k (6) and 2k3 (11) |
70 | 57.9k | return ((version >= 1 && version <= 6) || version == 11); |
71 | 57.9k | } |
72 | 57.9k | catch (...) |
73 | 57.9k | { |
74 | 2 | return false; |
75 | 2 | } |
76 | | |
77 | 15.9k | static bool parseBinaryVisioDocument(librevenge::RVNGInputStream *input, librevenge::RVNGDrawingInterface *painter, bool isStencilExtraction) try |
78 | 15.9k | { |
79 | 15.9k | VSD_DEBUG_MSG(("Parsing Binary Visio Document\n")); |
80 | 15.9k | input->seek(0, librevenge::RVNG_SEEK_SET); |
81 | 15.9k | std::shared_ptr<librevenge::RVNGInputStream> docStream; |
82 | 15.9k | if (input->isStructured()) |
83 | 783 | docStream.reset(input->getSubStreamByName("VisioDocument")); |
84 | 15.9k | if (!docStream) |
85 | 15.1k | docStream.reset(input, libvisio::VSDDummyDeleter()); |
86 | | |
87 | 15.9k | docStream->seek(0x1A, librevenge::RVNG_SEEK_SET); |
88 | | |
89 | 15.9k | std::unique_ptr<libvisio::VSDParser> parser; |
90 | | |
91 | 15.9k | unsigned char version = libvisio::readU8(docStream.get()); |
92 | 15.9k | switch (version) |
93 | 15.9k | { |
94 | 158 | case 1: |
95 | 241 | case 2: |
96 | 731 | case 3: |
97 | 4.26k | case 4: |
98 | 8.86k | case 5: |
99 | 8.86k | parser.reset(new libvisio::VSD5Parser(docStream.get(), painter)); |
100 | 8.86k | break; |
101 | 3.54k | case 6: |
102 | 3.54k | parser.reset(new libvisio::VSD6Parser(docStream.get(), painter)); |
103 | 3.54k | break; |
104 | 3.56k | case 11: |
105 | 3.56k | parser.reset(new libvisio::VSDParser(docStream.get(), painter, input)); |
106 | 3.56k | break; |
107 | 0 | default: |
108 | 0 | break; |
109 | 15.9k | } |
110 | | |
111 | 15.9k | if (isStencilExtraction) |
112 | 0 | return parser->extractStencils(); |
113 | 15.9k | else |
114 | 15.9k | return parser->parseMain(); |
115 | 15.9k | } |
116 | 15.9k | catch (...) |
117 | 15.9k | { |
118 | 24 | return false; |
119 | 24 | } |
120 | | |
121 | 41.9k | static bool isOpcVisioDocument(librevenge::RVNGInputStream *input) try |
122 | 41.9k | { |
123 | 41.9k | input->seek(0, librevenge::RVNG_SEEK_SET); |
124 | 41.9k | if (!input->isStructured()) |
125 | 38.7k | return false; |
126 | | |
127 | 3.25k | std::unique_ptr<librevenge::RVNGInputStream> tmpInput(input->getSubStreamByName("_rels/.rels")); |
128 | 3.25k | if (!tmpInput) |
129 | 1.55k | return false; |
130 | | |
131 | 1.70k | libvisio::VSDXRelationships rootRels(tmpInput.get()); |
132 | | |
133 | | // Check whether the relationship points to a Visio document stream |
134 | 1.70k | const libvisio::VSDXRelationship *rel = rootRels.getRelationshipByType("http://schemas.microsoft.com/visio/2010/relationships/document"); |
135 | 1.70k | if (!rel) |
136 | 61 | return false; |
137 | | |
138 | | // check whether the pointed Visio document stream exists in the document |
139 | 1.64k | tmpInput.reset(input->getSubStreamByName(rel->getTarget().c_str())); |
140 | 1.64k | return bool(tmpInput); |
141 | 1.70k | } |
142 | 41.9k | catch (...) |
143 | 41.9k | { |
144 | 0 | return false; |
145 | 0 | } |
146 | | |
147 | 1.56k | static bool parseOpcVisioDocument(librevenge::RVNGInputStream *input, librevenge::RVNGDrawingInterface *painter, bool isStencilExtraction) try |
148 | 1.56k | { |
149 | 1.56k | VSD_DEBUG_MSG(("Parsing Visio Document based on Open Packaging Convention\n")); |
150 | 1.56k | input->seek(0, librevenge::RVNG_SEEK_SET); |
151 | 1.56k | libvisio::VSDXParser parser(input, painter); |
152 | 1.56k | if (isStencilExtraction && parser.extractStencils()) |
153 | 0 | return true; |
154 | 1.56k | else if (!isStencilExtraction && parser.parseMain()) |
155 | 1.23k | return true; |
156 | 334 | return false; |
157 | 1.56k | } |
158 | 1.56k | catch (...) |
159 | 1.56k | { |
160 | 0 | return false; |
161 | 0 | } |
162 | | |
163 | 40.4k | static bool isXmlVisioDocument(librevenge::RVNGInputStream *input) try |
164 | 40.4k | { |
165 | 40.4k | input->seek(0, librevenge::RVNG_SEEK_SET); |
166 | 40.4k | auto reader = libvisio::xmlReaderForStream(input); |
167 | 40.4k | if (!reader) |
168 | 0 | return false; |
169 | 40.4k | int ret = xmlTextReaderRead(reader.get()); |
170 | 50.8k | while (ret == 1 && 1 != xmlTextReaderNodeType(reader.get())) |
171 | 10.4k | ret = xmlTextReaderRead(reader.get()); |
172 | 40.4k | if (ret != 1) |
173 | 22.4k | { |
174 | 22.4k | return false; |
175 | 22.4k | } |
176 | 17.9k | const xmlChar *name = xmlTextReaderConstName(reader.get()); |
177 | 17.9k | if (!name) |
178 | 0 | { |
179 | 0 | return false; |
180 | 0 | } |
181 | 17.9k | if (!xmlStrEqual(name, BAD_CAST("VisioDocument"))) |
182 | 680 | { |
183 | 680 | return false; |
184 | 680 | } |
185 | 17.2k | return true; |
186 | 17.9k | } |
187 | 40.4k | catch (...) |
188 | 40.4k | { |
189 | 0 | return false; |
190 | 0 | } |
191 | | |
192 | 17.2k | static bool parseXmlVisioDocument(librevenge::RVNGInputStream *input, librevenge::RVNGDrawingInterface *painter, bool isStencilExtraction) try |
193 | 17.2k | { |
194 | 17.2k | VSD_DEBUG_MSG(("Parsing Visio DrawingML Document\n")); |
195 | 17.2k | input->seek(0, librevenge::RVNG_SEEK_SET); |
196 | 17.2k | libvisio::VDXParser parser(input, painter); |
197 | 17.2k | if (isStencilExtraction && parser.extractStencils()) |
198 | 0 | return true; |
199 | 17.2k | else if (!isStencilExtraction && parser.parseMain()) |
200 | 15.1k | return true; |
201 | 2.06k | return false; |
202 | 17.2k | } |
203 | 17.2k | catch (...) |
204 | 17.2k | { |
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 | 57.9k | { |
241 | 57.9k | if (!input || !painter) |
242 | 0 | return false; |
243 | | |
244 | 57.9k | if (isBinaryVisioDocument(input)) |
245 | 15.9k | { |
246 | 15.9k | if (parseBinaryVisioDocument(input, painter, false)) |
247 | 13.5k | return true; |
248 | 2.40k | return false; |
249 | 15.9k | } |
250 | 41.9k | if (isOpcVisioDocument(input)) |
251 | 1.56k | { |
252 | 1.56k | if (parseOpcVisioDocument(input, painter, false)) |
253 | 1.23k | return true; |
254 | 334 | return false; |
255 | 1.56k | } |
256 | 40.4k | if (isXmlVisioDocument(input)) |
257 | 17.2k | { |
258 | 17.2k | if (parseXmlVisioDocument(input, painter, false)) |
259 | 15.1k | return true; |
260 | 2.06k | return false; |
261 | 17.2k | } |
262 | 23.1k | return false; |
263 | 40.4k | } |
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: */ |