/src/libmspub/src/lib/MSPUBParser.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 libmspub 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 "MSPUBParser.h" |
11 | | |
12 | | #include <algorithm> |
13 | | #include <cassert> |
14 | | #include <list> |
15 | | #include <memory> |
16 | | #include <set> |
17 | | #include <sstream> |
18 | | #include <string> |
19 | | #include <utility> |
20 | | |
21 | | #include <boost/numeric/conversion/cast.hpp> |
22 | | |
23 | | #include <librevenge-stream/librevenge-stream.h> |
24 | | |
25 | | #include "Arrow.h" |
26 | | #include "ColorReference.h" |
27 | | #include "Coordinate.h" |
28 | | #include "Dash.h" |
29 | | #include "EscherContainerType.h" |
30 | | #include "EscherFieldIds.h" |
31 | | #include "Fill.h" |
32 | | #include "FillType.h" |
33 | | #include "Line.h" |
34 | | #include "ListInfo.h" |
35 | | #include "MSPUBBlockID.h" |
36 | | #include "MSPUBBlockType.h" |
37 | | #include "MSPUBCollector.h" |
38 | | #include "MSPUBConstants.h" |
39 | | #include "MSPUBContentChunkType.h" |
40 | | #include "MSPUBMetaData.h" |
41 | | #include "Shadow.h" |
42 | | #include "ShapeFlags.h" |
43 | | #include "ShapeType.h" |
44 | | #include "TableInfo.h" |
45 | | #include "VerticalAlign.h" |
46 | | #include "libmspub_utils.h" |
47 | | |
48 | | namespace libmspub |
49 | | { |
50 | | |
51 | | namespace |
52 | | { |
53 | | |
54 | | Underline readUnderline(const unsigned value) |
55 | 25.7k | { |
56 | 25.7k | switch (value & 0xff) |
57 | 25.7k | { |
58 | 11.7k | case 0x0: |
59 | 11.7k | return Underline::None; |
60 | 1.57k | default: |
61 | 1.57k | MSPUB_DEBUG_MSG(("unknown underline type %u\n", value & 0xff)); |
62 | 1.57k | MSPUB_FALLTHROUGH; |
63 | 2.73k | case 0x1: |
64 | 2.73k | return Underline::Single; |
65 | 360 | case 0x2: |
66 | 360 | return Underline::WordsOnly; |
67 | 346 | case 0x3: |
68 | 346 | return Underline::Double; |
69 | 734 | case 0x4: |
70 | 734 | return Underline::Dotted; |
71 | 619 | case 0x6: |
72 | 619 | return Underline::Thick; |
73 | 314 | case 0x7: |
74 | 314 | return Underline::Dash; |
75 | 976 | case 0x9: |
76 | 976 | return Underline::DotDash; |
77 | 767 | case 0xa: |
78 | 767 | return Underline::DotDotDash; |
79 | 1.01k | case 0xb: |
80 | 1.01k | return Underline::Wave; |
81 | 938 | case 0x10: |
82 | 938 | return Underline::ThickWave; |
83 | 731 | case 0x11: |
84 | 731 | return Underline::ThickDot; |
85 | 687 | case 0x12: |
86 | 687 | return Underline::ThickDash; |
87 | 930 | case 0x13: |
88 | 930 | return Underline::ThickDotDash; |
89 | 342 | case 0x14: |
90 | 342 | return Underline::ThickDotDotDash; |
91 | 463 | case 0x15: |
92 | 463 | return Underline::LongDash; |
93 | 1.01k | case 0x16: |
94 | 1.01k | return Underline::ThickLongDash; |
95 | 1.00k | case 0x17: |
96 | 1.00k | return Underline::DoubleWave; |
97 | 25.7k | } |
98 | 25.7k | } |
99 | | |
100 | | } |
101 | | |
102 | | MSPUBParser::MSPUBParser(librevenge::RVNGInputStream *input, MSPUBCollector *collector) |
103 | 7.90k | : m_input(input), |
104 | 7.90k | m_length(boost::numeric_cast<unsigned>(getLength(input))), |
105 | 7.90k | m_collector(collector), |
106 | 7.90k | m_blockInfo(), m_contentChunks(), |
107 | 7.90k | m_cellsChunkIndices(), |
108 | 7.90k | m_pageChunkIndices(), m_shapeChunkIndices(), |
109 | 7.90k | m_paletteChunkIndices(), m_borderArtChunkIndices(), |
110 | 7.90k | m_fontChunkIndices(), |
111 | 7.90k | m_unknownChunkIndices(), m_documentChunkIndex(), |
112 | 7.90k | m_lastSeenSeqNum(-1), m_lastAddedImage(0), |
113 | 7.90k | m_alternateShapeSeqNums(), m_escherDelayIndices() |
114 | 7.90k | { |
115 | 7.90k | } |
116 | | |
117 | | MSPUBParser::~MSPUBParser() |
118 | 7.90k | { |
119 | 7.90k | } |
120 | | |
121 | | bool MSPUBParser::lineExistsByFlagPointer(unsigned *flags, |
122 | | unsigned *geomFlags) |
123 | 25.9k | { |
124 | 25.9k | return flags && |
125 | 20.1k | !(((*flags) & FLAG_USE_LINE) && !((*flags) & FLAG_LINE)) && |
126 | 13.3k | ((!geomFlags) || !((*geomFlags) & FLAG_GEOM_USE_LINE_OK) |
127 | 71 | || ((*geomFlags) & FLAG_GEOM_LINE_OK)); |
128 | | |
129 | 25.9k | } |
130 | | |
131 | | unsigned MSPUBParser::getColorIndexByQuillEntry(unsigned entry) |
132 | 43.0k | { |
133 | 43.0k | return entry; |
134 | 43.0k | } |
135 | | |
136 | | short MSPUBParser::getBlockDataLength(unsigned type) // -1 for variable-length block with the data length as the first DWORD |
137 | 70.0M | { |
138 | 70.0M | switch (type) |
139 | 70.0M | { |
140 | 633k | case DUMMY: |
141 | 808k | case 0x5: |
142 | 1.72M | case 0x8: |
143 | 1.91M | case 0xa: |
144 | 1.91M | return 0; |
145 | 678k | case 0x10: |
146 | 792k | case 0x12: |
147 | 1.97M | case 0x18: |
148 | 2.06M | case 0x1a: |
149 | 2.86M | case 0x07: |
150 | 2.86M | return 2; |
151 | 1.76M | case 0x20: |
152 | 2.51M | case 0x22: |
153 | 2.53M | case 0x58: |
154 | 3.14M | case 0x68: |
155 | 3.47M | case 0x70: |
156 | 4.01M | case 0xb8: |
157 | 4.01M | return 4; |
158 | 330k | case 0x28: |
159 | 330k | return 8; |
160 | 51.9k | case 0x38: |
161 | 51.9k | return 16; |
162 | 34.8k | case 0x48: |
163 | 34.8k | return 24; |
164 | 163k | case STRING_CONTAINER: |
165 | 193k | case 0x80: |
166 | 220k | case 0x82: |
167 | 1.02M | case GENERAL_CONTAINER: |
168 | 1.14M | case 0x8a: |
169 | 1.16M | case 0x90: |
170 | 1.17M | case 0x98: |
171 | 1.21M | case 0xa0: |
172 | 1.21M | return -1; |
173 | 70.0M | } |
174 | | //FIXME: Debug assertion here? Should never get here. |
175 | 59.5M | MSPUB_DEBUG_MSG(("Block of unknown type seen!\n")); |
176 | 59.5M | return 0; |
177 | 70.0M | } |
178 | | |
179 | | bool MSPUBParser::parse() |
180 | 4.35k | { |
181 | 4.35k | MSPUB_DEBUG_MSG(("***NOTE***: Where applicable, the meanings of block/chunk IDs and Types printed below may be found in:\n\t***MSPUBBlockType.h\n\t***MSPUBBlockID.h\n\t***MSPUBContentChunkType.h\n*****\n")); |
182 | 4.35k | if (!m_input->isStructured()) |
183 | 0 | return false; |
184 | | // No check: metadata are not important enough to fail if they can't be parsed |
185 | 4.35k | parseMetaData(); |
186 | 4.35k | std::unique_ptr<librevenge::RVNGInputStream> quill(m_input->getSubStreamByName("Quill/QuillSub/CONTENTS")); |
187 | 4.35k | if (!quill) |
188 | 308 | { |
189 | 308 | MSPUB_DEBUG_MSG(("Couldn't get quill stream.\n")); |
190 | 308 | return false; |
191 | 308 | } |
192 | 4.04k | if (!parseQuill(quill.get())) |
193 | 0 | { |
194 | 0 | MSPUB_DEBUG_MSG(("Couldn't parse quill stream.\n")); |
195 | 0 | return false; |
196 | 0 | } |
197 | 4.04k | std::unique_ptr<librevenge::RVNGInputStream> contents(m_input->getSubStreamByName("Contents")); |
198 | 4.04k | if (!contents) |
199 | 0 | { |
200 | 0 | MSPUB_DEBUG_MSG(("Couldn't get contents stream.\n")); |
201 | 0 | return false; |
202 | 0 | } |
203 | 4.04k | if (!parseContents(contents.get())) |
204 | 227 | { |
205 | 227 | MSPUB_DEBUG_MSG(("Couldn't parse contents stream.\n")); |
206 | 227 | return false; |
207 | 227 | } |
208 | 3.81k | std::unique_ptr<librevenge::RVNGInputStream> escherDelay(m_input->getSubStreamByName("Escher/EscherDelayStm")); |
209 | 3.81k | if (escherDelay) |
210 | 1.23k | { |
211 | 1.23k | parseEscherDelay(escherDelay.get()); |
212 | 1.23k | } |
213 | 3.81k | std::unique_ptr<librevenge::RVNGInputStream> escher(m_input->getSubStreamByName("Escher/EscherStm")); |
214 | 3.81k | if (!escher) |
215 | 521 | { |
216 | 521 | MSPUB_DEBUG_MSG(("Couldn't get escher stream.\n")); |
217 | 521 | return false; |
218 | 521 | } |
219 | 3.29k | if (!parseEscher(escher.get())) |
220 | 0 | { |
221 | 0 | MSPUB_DEBUG_MSG(("Couldn't parse escher stream.\n")); |
222 | 0 | return false; |
223 | 0 | } |
224 | | |
225 | 3.29k | return m_collector->go(); |
226 | 3.29k | } |
227 | | |
228 | | ImgType MSPUBParser::imgTypeByBlipType(unsigned short type) |
229 | 59.8k | { |
230 | 59.8k | switch (type) |
231 | 59.8k | { |
232 | 209 | case OFFICE_ART_BLIP_PNG: |
233 | 209 | return PNG; |
234 | 862 | case OFFICE_ART_BLIP_JPEG: |
235 | 862 | return JPEG; |
236 | 6 | case OFFICE_ART_BLIP_JPEGCMYK: |
237 | 6 | return JPEGCMYK; |
238 | 817 | case OFFICE_ART_BLIP_WMF: |
239 | 817 | return WMF; |
240 | 43 | case OFFICE_ART_BLIP_DIB: |
241 | 43 | return DIB; |
242 | 1.05k | case OFFICE_ART_BLIP_EMF: |
243 | 1.05k | return EMF; |
244 | 16 | case OFFICE_ART_BLIP_TIFF: |
245 | 16 | return TIFF; |
246 | 29 | case OFFICE_ART_BLIP_PICT: |
247 | 29 | return PICT; |
248 | 59.8k | } |
249 | 56.8k | return UNKNOWN; |
250 | 59.8k | } |
251 | | |
252 | | int MSPUBParser::getStartOffset(ImgType type, unsigned short initial) |
253 | 3.03k | { |
254 | 3.03k | bool oneUid = true; |
255 | 3.03k | int offset = 0x11; |
256 | 3.03k | unsigned short recInstance = initial >> 4; |
257 | 3.03k | switch (type) |
258 | 3.03k | { |
259 | 817 | case WMF: |
260 | 817 | oneUid = recInstance == 0x216; |
261 | 817 | offset = 0x34; |
262 | 817 | break; |
263 | 1.05k | case EMF: |
264 | 1.05k | oneUid = recInstance == 0x3D4; |
265 | 1.05k | offset = 0x34; |
266 | 1.05k | break; |
267 | 209 | case PNG: |
268 | 209 | oneUid = recInstance == 0x6E0; |
269 | 209 | offset = 0x11; |
270 | 209 | break; |
271 | 862 | case JPEG: |
272 | 862 | oneUid = recInstance == 0x46A || recInstance == 0x6E2; |
273 | 862 | offset = 0x11; |
274 | 862 | break; |
275 | 6 | case JPEGCMYK: |
276 | 6 | oneUid = recInstance == 0x46B || recInstance == 0x6E3; |
277 | 6 | offset = 33; |
278 | 6 | break; |
279 | 43 | case DIB: |
280 | 43 | oneUid = recInstance == 0x7A8; |
281 | 43 | offset = 0x11; |
282 | 43 | break; |
283 | 16 | case TIFF: |
284 | 16 | oneUid = recInstance == 0x6E4; |
285 | 16 | offset = 0x11; |
286 | 16 | break; |
287 | 29 | default: |
288 | 29 | break; |
289 | 3.03k | } |
290 | 3.03k | return offset + (oneUid ? 0 : 0x10); |
291 | 3.03k | } |
292 | | |
293 | | bool MSPUBParser::parseEscherDelay(librevenge::RVNGInputStream *input) |
294 | 1.23k | { |
295 | 61.1k | while (stillReading(input, (unsigned long)-1)) |
296 | 59.9k | { |
297 | 59.9k | EscherContainerInfo info = parseEscherContainer(input); |
298 | 59.9k | const ImgType imgType = imgTypeByBlipType(info.type); |
299 | 59.9k | if (imgType != UNKNOWN) |
300 | 3.03k | { |
301 | 3.03k | librevenge::RVNGBinaryData img; |
302 | 3.03k | unsigned long toRead = info.contentsLength; |
303 | 3.03k | input->seek(input->tell() + getStartOffset(imgType, info.initial), librevenge::RVNG_SEEK_SET); |
304 | 6.02k | while (toRead > 0 && stillReading(input, (unsigned long)-1)) |
305 | 2.99k | { |
306 | 2.99k | unsigned long howManyRead = 0; |
307 | 2.99k | const unsigned char *buf = input->read(toRead, howManyRead); |
308 | 2.99k | img.append(buf, howManyRead); |
309 | 2.99k | toRead -= howManyRead; |
310 | 2.99k | } |
311 | 3.03k | if (imgType == WMF || imgType == EMF) |
312 | 1.86k | { |
313 | 1.86k | img = inflateData(img); |
314 | 1.86k | } |
315 | 1.16k | else if (imgType == DIB) |
316 | 43 | { |
317 | | // Reconstruct BMP header |
318 | | // cf. http://en.wikipedia.org/wiki/BMP_file_format , accessed 2012-5-31 |
319 | 43 | librevenge::RVNGInputStream *buf = img.getDataStream(); |
320 | 43 | if (img.size() < 0x2E + 4) |
321 | 12 | { |
322 | 12 | ++m_lastAddedImage; |
323 | 12 | MSPUB_DEBUG_MSG(("Garbage DIB at index 0x%x\n", m_lastAddedImage)); |
324 | 12 | input->seek(info.contentsOffset + info.contentsLength, librevenge::RVNG_SEEK_SET); |
325 | 12 | continue; |
326 | 12 | } |
327 | 31 | buf->seek(0x0E, librevenge::RVNG_SEEK_SET); |
328 | 31 | unsigned short bitsPerPixel = readU16(buf); |
329 | 31 | buf->seek(0x20, librevenge::RVNG_SEEK_SET); |
330 | 31 | unsigned numPaletteColors = readU32(buf); |
331 | 31 | if (numPaletteColors == 0 && bitsPerPixel <= 8) |
332 | 2 | { |
333 | 2 | numPaletteColors = 1; |
334 | 10 | for (int i = 0; i < bitsPerPixel; ++i) |
335 | 8 | { |
336 | 8 | numPaletteColors *= 2; |
337 | 8 | } |
338 | 2 | } |
339 | | |
340 | 31 | librevenge::RVNGBinaryData tmpImg; |
341 | 31 | tmpImg.append((unsigned char)0x42); |
342 | 31 | tmpImg.append((unsigned char)0x4d); |
343 | | |
344 | 31 | tmpImg.append((unsigned char)((img.size() + 14) & 0x000000ff)); |
345 | 31 | tmpImg.append((unsigned char)(((img.size() + 14) & 0x0000ff00) >> 8)); |
346 | 31 | tmpImg.append((unsigned char)(((img.size() + 14) & 0x00ff0000) >> 16)); |
347 | 31 | tmpImg.append((unsigned char)(((img.size() + 14) & 0xff000000) >> 24)); |
348 | | |
349 | 31 | tmpImg.append((unsigned char)0x00); |
350 | 31 | tmpImg.append((unsigned char)0x00); |
351 | 31 | tmpImg.append((unsigned char)0x00); |
352 | 31 | tmpImg.append((unsigned char)0x00); |
353 | | |
354 | 31 | tmpImg.append((unsigned char)(0x36 + 4 * numPaletteColors)); |
355 | 31 | tmpImg.append((unsigned char)0x00); |
356 | 31 | tmpImg.append((unsigned char)0x00); |
357 | 31 | tmpImg.append((unsigned char)0x00); |
358 | 31 | tmpImg.append(img); |
359 | 31 | img = tmpImg; |
360 | 31 | } |
361 | 3.02k | m_collector->addImage(++m_lastAddedImage, imgType, img); |
362 | 3.02k | } |
363 | 56.8k | else |
364 | 56.8k | { |
365 | 56.8k | ++m_lastAddedImage; |
366 | 56.8k | MSPUB_DEBUG_MSG(("Image of unknown type at index 0x%x\n", m_lastAddedImage)); |
367 | 56.8k | } |
368 | 59.8k | input->seek(info.contentsOffset + info.contentsLength, librevenge::RVNG_SEEK_SET); |
369 | 59.8k | } |
370 | 1.23k | return true; |
371 | 1.23k | } |
372 | | |
373 | | bool MSPUBParser::parseContents(librevenge::RVNGInputStream *input) |
374 | 3.02k | { |
375 | 3.02k | MSPUB_DEBUG_MSG(("MSPUBParser::parseContents\n")); |
376 | 3.02k | input->seek(0x1a, librevenge::RVNG_SEEK_SET); |
377 | 3.02k | unsigned trailerOffset = readU32(input); |
378 | 3.02k | MSPUB_DEBUG_MSG(("MSPUBParser: trailerOffset %.8x\n", trailerOffset)); |
379 | 3.02k | input->seek(trailerOffset, librevenge::RVNG_SEEK_SET); |
380 | 3.02k | unsigned trailerLength = readU32(input); |
381 | 11.0k | for (unsigned i=0; i<3; i++) |
382 | 8.22k | { |
383 | 8.22k | MSPUBBlockInfo trailerPart = parseBlock(input); |
384 | 8.22k | MSPUB_DEBUG_MSG(("Trailer SubBlock %i, startPosition 0x%lx, id %i, type 0x%x, dataLength 0x%lx\n", i+1, trailerPart.startPosition, trailerPart.id, trailerPart.type, trailerPart.dataLength)); |
385 | 8.22k | if (trailerPart.type == TRAILER_DIRECTORY) |
386 | 2.38k | { |
387 | | |
388 | 971k | while (stillReading(input, trailerPart.dataOffset + trailerPart.dataLength)) |
389 | 968k | { |
390 | 968k | m_blockInfo.push_back(parseBlock(input)); |
391 | 968k | ++m_lastSeenSeqNum; |
392 | 968k | if (m_blockInfo.back().type == GENERAL_CONTAINER) |
393 | 109k | { |
394 | 109k | if (parseContentChunkReference(input, m_blockInfo.back())) |
395 | 54.4k | { |
396 | 54.4k | if (m_contentChunks.size() > 1) |
397 | 52.4k | { |
398 | 52.4k | m_contentChunks[m_contentChunks.size() - 2].end = m_contentChunks.back().offset; |
399 | 52.4k | } |
400 | 54.4k | } |
401 | 109k | } |
402 | 859k | else(skipBlock(input, m_blockInfo.back())); |
403 | 968k | } |
404 | 2.38k | if (!m_contentChunks.empty()) |
405 | 2.35k | { |
406 | 2.35k | m_contentChunks.back().end = trailerPart.dataOffset + trailerPart.dataLength; |
407 | 2.35k | } |
408 | 2.38k | if (!m_documentChunkIndex) |
409 | 104 | { |
410 | 104 | return false; |
411 | 104 | } |
412 | 2.28k | const ContentChunkReference &documentChunk = m_contentChunks.at(m_documentChunkIndex.get()); |
413 | 2.28k | for (unsigned int paletteChunkIndex : m_paletteChunkIndices) |
414 | 2.53k | { |
415 | 2.53k | const ContentChunkReference &paletteChunk = m_contentChunks.at(paletteChunkIndex); |
416 | 2.53k | input->seek(paletteChunk.offset, librevenge::RVNG_SEEK_SET); |
417 | 2.53k | if (! parsePaletteChunk(input, paletteChunk)) |
418 | 0 | { |
419 | 0 | return false; |
420 | 0 | } |
421 | 2.53k | } |
422 | 2.28k | for (unsigned int borderArtChunkIndex : m_borderArtChunkIndices) |
423 | 2.33k | { |
424 | 2.33k | const ContentChunkReference &baChunk = |
425 | 2.33k | m_contentChunks.at(borderArtChunkIndex); |
426 | 2.33k | input->seek(baChunk.offset, librevenge::RVNG_SEEK_SET); |
427 | 2.33k | if (!parseBorderArtChunk(input, baChunk)) |
428 | 0 | { |
429 | 0 | return false; |
430 | 0 | } |
431 | 2.33k | } |
432 | 2.28k | for (unsigned int shapeChunkIndex : m_shapeChunkIndices) |
433 | 31.6k | { |
434 | 31.6k | const ContentChunkReference &shapeChunk = |
435 | 31.6k | m_contentChunks.at(shapeChunkIndex); |
436 | 31.6k | input->seek(shapeChunk.offset, librevenge::RVNG_SEEK_SET); |
437 | 31.6k | if (!parseShape(input, shapeChunk)) |
438 | 123 | { |
439 | 123 | return false; |
440 | 123 | } |
441 | 31.6k | } |
442 | 2.16k | for (unsigned int fontChunkIndex : m_fontChunkIndices) |
443 | 1.65k | { |
444 | 1.65k | const ContentChunkReference &fontChunk = |
445 | 1.65k | m_contentChunks.at(fontChunkIndex); |
446 | 1.65k | input->seek(fontChunk.offset, librevenge::RVNG_SEEK_SET); |
447 | 1.65k | if (!parseFontChunk(input, fontChunk)) |
448 | 0 | { |
449 | 0 | return false; |
450 | 0 | } |
451 | 1.65k | } |
452 | 2.16k | input->seek(documentChunk.offset, librevenge::RVNG_SEEK_SET); |
453 | 2.16k | if (!parseDocumentChunk(input, documentChunk)) |
454 | 0 | { |
455 | 0 | return false; |
456 | 0 | } |
457 | 2.16k | for (unsigned int pageChunkIndex : m_pageChunkIndices) |
458 | 10.3k | { |
459 | 10.3k | const ContentChunkReference &pageChunk = m_contentChunks.at(pageChunkIndex); |
460 | 10.3k | input->seek(pageChunk.offset, librevenge::RVNG_SEEK_SET); |
461 | 10.3k | if (!parsePageChunk(input, pageChunk)) |
462 | 0 | { |
463 | 0 | return false; |
464 | 0 | } |
465 | 10.3k | } |
466 | 2.16k | } |
467 | 8.22k | } |
468 | 2.79k | input->seek(trailerOffset + trailerLength, librevenge::RVNG_SEEK_SET); |
469 | | |
470 | 2.79k | return true; |
471 | 3.02k | } |
472 | | |
473 | | #ifdef DEBUG |
474 | | bool MSPUBParser::parseDocumentChunk(librevenge::RVNGInputStream *input, const ContentChunkReference &chunk) |
475 | | #else |
476 | | bool MSPUBParser::parseDocumentChunk(librevenge::RVNGInputStream *input, const ContentChunkReference &) |
477 | | #endif |
478 | 2.07k | { |
479 | 2.07k | MSPUB_DEBUG_MSG(("parseDocumentChunk: offset 0x%lx, end 0x%lx\n", input->tell(), chunk.end)); |
480 | 2.07k | unsigned long begin = input->tell(); |
481 | 2.07k | unsigned long len = readU32(input); |
482 | 345k | while (stillReading(input, begin + len)) |
483 | 343k | { |
484 | 343k | MSPUBBlockInfo info = parseBlock(input); |
485 | 343k | if (info.id == DOCUMENT_SIZE) |
486 | 2.65k | { |
487 | 37.2k | while (stillReading(input, info.dataOffset + info.dataLength)) |
488 | 34.6k | { |
489 | 34.6k | MSPUBBlockInfo subInfo = parseBlock(input, true); |
490 | 34.6k | if (subInfo.id == DOCUMENT_WIDTH) |
491 | 2.67k | { |
492 | 2.67k | m_collector->setWidthInEmu(subInfo.data); |
493 | 2.67k | } |
494 | 31.9k | else if (subInfo.id == DOCUMENT_HEIGHT) |
495 | 2.01k | { |
496 | 2.01k | m_collector->setHeightInEmu(subInfo.data); |
497 | 2.01k | } |
498 | 34.6k | } |
499 | 2.65k | } |
500 | 340k | else if (info.id == DOCUMENT_PAGE_LIST) |
501 | 11.5k | { |
502 | 11.5k | input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
503 | 73.0k | while (stillReading(input, info.dataOffset + info.dataLength)) |
504 | 61.5k | { |
505 | 61.5k | MSPUBBlockInfo subInfo = parseBlock(input, true); |
506 | 61.5k | if (subInfo.id == 0) |
507 | 36.7k | { |
508 | 36.7k | m_collector->setNextPage(subInfo.data); |
509 | 36.7k | } |
510 | 61.5k | } |
511 | 11.5k | } |
512 | 329k | else |
513 | 329k | { |
514 | 329k | skipBlock(input, info); |
515 | 329k | } |
516 | 343k | } |
517 | 2.07k | return true; //FIXME: return false for failure |
518 | 2.07k | } |
519 | | |
520 | | bool MSPUBParser::parseFontChunk( |
521 | | librevenge::RVNGInputStream *input, const ContentChunkReference &chunk) |
522 | 1.65k | { |
523 | 1.65k | unsigned length = readU32(input); |
524 | 146k | while (stillReading(input, chunk.offset + length)) |
525 | 144k | { |
526 | 144k | MSPUBBlockInfo info = parseBlock(input, true); |
527 | 144k | if (info.id == FONT_CONTAINER_ARRAY) |
528 | 3.55k | { |
529 | 3.55k | input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
530 | 135k | while (stillReading(input, info.dataOffset + info.dataLength)) |
531 | 132k | { |
532 | 132k | MSPUBBlockInfo subInfo = parseBlock(input, true); |
533 | 132k | if (subInfo.id == 0) |
534 | 39.2k | { |
535 | 39.2k | boost::optional<librevenge::RVNGString> name; |
536 | 39.2k | boost::optional<unsigned> eotOffset; |
537 | 39.2k | unsigned eotLength = 0; |
538 | 39.2k | input->seek(subInfo.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
539 | 106k | while (stillReading(input, subInfo.dataOffset + subInfo.dataLength)) |
540 | 66.8k | { |
541 | 66.8k | MSPUBBlockInfo subSubInfo = parseBlock(input, true); |
542 | 66.8k | if (subSubInfo.id == EMBEDDED_FONT_NAME) |
543 | 9.12k | { |
544 | 9.12k | name = librevenge::RVNGString(); |
545 | | // drop trailing 0 |
546 | | // TODO: This could be a general problem. Check. |
547 | 9.12k | const std::size_t len = subSubInfo.stringData.size(); |
548 | 9.12k | if ((len > 2) && (subSubInfo.stringData[len - 1] == 0) && (subSubInfo.stringData[len - 2] == 0)) |
549 | 1.14k | { |
550 | 1.14k | subSubInfo.stringData.pop_back(); |
551 | 1.14k | subSubInfo.stringData.pop_back(); |
552 | 1.14k | } |
553 | 9.12k | appendCharacters(name.get(), subSubInfo.stringData, "UTF-16LE"); |
554 | 9.12k | } |
555 | 57.6k | else if (subSubInfo.id == EMBEDDED_EOT) |
556 | 1.45k | { |
557 | 1.45k | eotOffset = subSubInfo.dataOffset; |
558 | 1.45k | eotLength = subSubInfo.dataLength; |
559 | 1.45k | } |
560 | 66.8k | } |
561 | 39.2k | if (bool(name) && bool(eotOffset)) |
562 | 1.03k | { |
563 | | // skip length, we've already read that |
564 | | // TODO: Why do we not read the data as part of the block info? |
565 | 1.03k | input->seek(eotOffset.get() + 4, librevenge::RVNG_SEEK_SET); |
566 | 1.03k | librevenge::RVNGBinaryData data; |
567 | 1.03k | unsigned long toRead = eotLength; |
568 | 1.87k | while (toRead > 0 && stillReading(input, (unsigned long)-1)) |
569 | 838 | { |
570 | 838 | unsigned long howManyRead = 0; |
571 | 838 | const unsigned char *buf = input->read(toRead, howManyRead); |
572 | 838 | data.append(buf, howManyRead); |
573 | 838 | toRead -= howManyRead; |
574 | 838 | } |
575 | 1.03k | m_collector->addEOTFont(name.get(), data); |
576 | 1.03k | input->seek(subInfo.dataOffset + subInfo.dataLength, librevenge::RVNG_SEEK_SET); |
577 | 1.03k | } |
578 | 39.2k | } |
579 | 132k | } |
580 | 3.55k | } |
581 | 144k | } |
582 | 1.65k | return true; |
583 | 1.65k | } |
584 | | |
585 | | bool MSPUBParser::parseBorderArtChunk( |
586 | | librevenge::RVNGInputStream *input, const ContentChunkReference &chunk) |
587 | 2.33k | { |
588 | 2.33k | unsigned length = readU32(input); |
589 | 818k | while (stillReading(input, chunk.offset + length)) |
590 | 815k | { |
591 | 815k | MSPUBBlockInfo info = parseBlock(input, true); |
592 | 815k | if (info.id == BA_ARRAY) |
593 | 18.6k | { |
594 | 18.6k | input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
595 | 18.6k | unsigned i = 0; |
596 | 268k | while (stillReading(input, info.dataOffset + info.dataLength)) |
597 | 250k | { |
598 | 250k | MSPUBBlockInfo entry = parseBlock(input, false); |
599 | 383k | while (stillReading(input, entry.dataOffset + entry.dataLength)) |
600 | 133k | { |
601 | 133k | MSPUBBlockInfo subRecord = parseBlock(input, true); |
602 | 133k | if (subRecord.id == BA_IMAGE_ARRAY) |
603 | 1.74k | { |
604 | 1.74k | input->seek(subRecord.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
605 | 143k | while (stillReading(input, subRecord.dataOffset + subRecord.dataLength)) |
606 | 141k | { |
607 | 141k | MSPUBBlockInfo subSubRecord = parseBlock(input, false); |
608 | 141k | if (subSubRecord.id == BA_IMAGE_CONTAINER) |
609 | 40.7k | { |
610 | 40.7k | MSPUBBlockInfo imgRecord = parseBlock(input, false); |
611 | 40.7k | if (imgRecord.id == BA_IMAGE) |
612 | 3.87k | { |
613 | 3.87k | librevenge::RVNGBinaryData &img = *(m_collector->addBorderImage( |
614 | 3.87k | WMF, i)); |
615 | 3.87k | unsigned long toRead = imgRecord.dataLength; |
616 | 6.79k | while (toRead > 0 && stillReading(input, (unsigned long)-1)) |
617 | 2.91k | { |
618 | 2.91k | unsigned long howManyRead = 0; |
619 | 2.91k | const unsigned char *buf = input->read(toRead, howManyRead); |
620 | 2.91k | img.append(buf, howManyRead); |
621 | 2.91k | toRead -= howManyRead; |
622 | 2.91k | } |
623 | 3.87k | } |
624 | 40.7k | } |
625 | 141k | } |
626 | 1.74k | } |
627 | 131k | else if (subRecord.id == BA_OFFSET_CONTAINER) |
628 | 3.33k | { |
629 | 3.33k | input->seek(subRecord.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
630 | 94.9k | while (stillReading( |
631 | 94.9k | input, subRecord.dataOffset + subRecord.dataLength)) |
632 | 91.6k | { |
633 | 91.6k | MSPUBBlockInfo subSubRecord = parseBlock(input, true); |
634 | 91.6k | if (subSubRecord.id == BA_OFFSET_ENTRY) |
635 | 21.0k | { |
636 | 21.0k | m_collector->setBorderImageOffset(i, subSubRecord.data); |
637 | 21.0k | } |
638 | 91.6k | } |
639 | 3.33k | } |
640 | 133k | } |
641 | 250k | ++i; |
642 | 250k | input->seek(entry.dataOffset + entry.dataLength, librevenge::RVNG_SEEK_SET); |
643 | 250k | } |
644 | 18.6k | } |
645 | 815k | } |
646 | 2.33k | return true; |
647 | 2.33k | } |
648 | | |
649 | | bool MSPUBParser::parsePageChunk(librevenge::RVNGInputStream *input, const ContentChunkReference &chunk) |
650 | 10.3k | { |
651 | 10.3k | MSPUB_DEBUG_MSG(("parsePageChunk: offset 0x%lx, end 0x%lx, seqnum 0x%x, parent 0x%x\n", input->tell(), chunk.end, chunk.seqNum, chunk.parentSeqNum)); |
652 | 10.3k | unsigned long length = readU32(input); |
653 | 10.3k | PageType type = getPageTypeBySeqNum(chunk.seqNum); |
654 | 10.3k | if (type == NORMAL) |
655 | 9.70k | { |
656 | 9.70k | m_collector->addPage(chunk.seqNum); |
657 | 9.70k | } |
658 | 3.01M | while (stillReading(input, chunk.offset + length)) |
659 | 3.00M | { |
660 | 3.00M | MSPUBBlockInfo info = parseBlock(input); |
661 | 3.00M | if (info.id == PAGE_BG_SHAPE) |
662 | 13.4k | { |
663 | 13.4k | m_collector->setPageBgShape(chunk.seqNum, info.data); |
664 | 13.4k | } |
665 | 2.98M | else if (info.id == PAGE_SHAPES) |
666 | 43.3k | { |
667 | 43.3k | parsePageShapeList(input, info, chunk.seqNum); |
668 | 43.3k | } |
669 | 2.94M | else if (info.id == THIS_MASTER_NAME) |
670 | 18.5k | { |
671 | 18.5k | for (unsigned char i : info.stringData) |
672 | 209k | { |
673 | 209k | if (i != 0) |
674 | 93.5k | { |
675 | 93.5k | m_collector->designateMasterPage(chunk.seqNum); |
676 | 93.5k | } |
677 | 209k | } |
678 | 18.5k | } |
679 | 2.92M | else if (info.id == APPLIED_MASTER_NAME) |
680 | 33.8k | { |
681 | 33.8k | m_collector->setMasterPage(chunk.seqNum, info.data); |
682 | 33.8k | } |
683 | 2.89M | else |
684 | 2.89M | { |
685 | 2.89M | skipBlock(input, info); |
686 | 2.89M | } |
687 | 3.00M | } |
688 | 10.3k | return true; |
689 | 10.3k | } |
690 | | |
691 | | bool MSPUBParser::parsePageShapeList(librevenge::RVNGInputStream *input, MSPUBBlockInfo info, unsigned pageSeqNum) |
692 | 43.3k | { |
693 | 43.3k | MSPUB_DEBUG_MSG(("parsePageShapeList: page seqnum 0x%x\n", pageSeqNum)); |
694 | 267k | while (stillReading(input, info.dataOffset + info.dataLength)) |
695 | 224k | { |
696 | 224k | MSPUBBlockInfo subInfo = parseBlock(input, true); |
697 | 224k | if (subInfo.type == SHAPE_SEQNUM) |
698 | 57.1k | { |
699 | 57.1k | m_collector->setShapePage(subInfo.data, pageSeqNum); |
700 | 57.1k | } |
701 | 224k | } |
702 | 43.3k | return true; |
703 | 43.3k | } |
704 | | |
705 | | bool MSPUBParser::parseShape(librevenge::RVNGInputStream *input, |
706 | | const ContentChunkReference &chunk) |
707 | 31.6k | { |
708 | 31.6k | MSPUB_DEBUG_MSG(("parseShape: seqNum 0x%x\n", chunk.seqNum)); |
709 | 31.6k | unsigned long pos = input->tell(); |
710 | 31.6k | unsigned length = readU32(input); |
711 | 31.6k | bool isTable = chunk.type == TABLE; |
712 | 31.6k | bool isGroup = chunk.type == GROUP || chunk.type == LOGO; |
713 | 31.6k | if (isTable) |
714 | 1.23k | { |
715 | 1.23k | boost::optional<unsigned> cellsSeqNum; |
716 | 1.23k | boost::optional<unsigned> numRows; |
717 | 1.23k | boost::optional<unsigned> numCols; |
718 | 1.23k | boost::optional<unsigned> rowcolArrayOffset; |
719 | 1.23k | boost::optional<unsigned> textId; |
720 | 114k | while (stillReading(input, pos + length)) |
721 | 113k | { |
722 | 113k | MSPUBBlockInfo info = parseBlock(input, true); |
723 | 113k | if (info.id == TABLE_CELLS_SEQNUM) |
724 | 1.72k | { |
725 | 1.72k | cellsSeqNum = info.data; |
726 | 1.72k | } |
727 | 111k | else if (info.id == TABLE_NUM_ROWS) |
728 | 1.92k | { |
729 | 1.92k | numRows = info.data; |
730 | 1.92k | } |
731 | 109k | else if (info.id == TABLE_NUM_COLS) |
732 | 1.69k | { |
733 | 1.69k | numCols = info.data; |
734 | 1.69k | } |
735 | 108k | else if (info.id == TABLE_ROWCOL_ARRAY) |
736 | 1.58k | { |
737 | 1.58k | rowcolArrayOffset = info.dataOffset; |
738 | 1.58k | } |
739 | 106k | else if (info.id == SHAPE_TEXT_ID) |
740 | 1.49k | { |
741 | 1.49k | textId = info.data; |
742 | 1.49k | } |
743 | 113k | } |
744 | 1.23k | if (bool(cellsSeqNum) && bool(numRows) && bool(numCols) && bool(rowcolArrayOffset)) |
745 | 1.17k | { |
746 | 1.17k | unsigned nr = numRows.get(); |
747 | 1.17k | unsigned nc = numCols.get(); |
748 | 1.17k | unsigned rcao = rowcolArrayOffset.get(); |
749 | 1.17k | unsigned csn = cellsSeqNum.get(); |
750 | 1.17k | std::vector<unsigned> rowHeightsInEmu; |
751 | 1.17k | std::vector<unsigned> columnWidthsInEmu; |
752 | 1.17k | input->seek(rcao, librevenge::RVNG_SEEK_SET); |
753 | 1.17k | unsigned arrayLength = readU32(input); |
754 | 100k | while (stillReading(input, rcao + arrayLength)) |
755 | 99.4k | { |
756 | 99.4k | MSPUBBlockInfo info = parseBlock(input, true); |
757 | 99.4k | if (info.id == 0) |
758 | 39.3k | { |
759 | 39.3k | input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
760 | 119k | while (stillReading(input, info.dataOffset + info.dataLength)) |
761 | 80.3k | { |
762 | 80.3k | MSPUBBlockInfo subInfo = parseBlock(input, true); |
763 | 80.3k | if (subInfo.id == TABLE_ROWCOL_SIZE) |
764 | 12.9k | { |
765 | 12.9k | if (columnWidthsInEmu.size() < nc) |
766 | 3.28k | columnWidthsInEmu.push_back(subInfo.data); |
767 | 9.66k | else if (rowHeightsInEmu.size() < nr) |
768 | 4.81k | rowHeightsInEmu.push_back(subInfo.data); |
769 | 12.9k | } |
770 | 80.3k | } |
771 | 39.3k | } |
772 | 99.4k | } |
773 | 1.17k | if (rowHeightsInEmu.size() != nr || columnWidthsInEmu.size() != nc) |
774 | 36 | { |
775 | 36 | MSPUB_DEBUG_MSG(("ERROR: Wrong number of rows or columns found in table definition.\n")); |
776 | 36 | return false; |
777 | 36 | } |
778 | 1.13k | boost::optional<unsigned> index; |
779 | 3.10k | for (size_t i = 0; i < m_cellsChunkIndices.size(); ++i) |
780 | 3.07k | { |
781 | 3.07k | if (m_contentChunks[m_cellsChunkIndices[i]].seqNum == csn) |
782 | 1.10k | { |
783 | 1.10k | index = i; |
784 | 1.10k | break; |
785 | 1.10k | } |
786 | 3.07k | } |
787 | | |
788 | 1.13k | TableInfo ti(nr, nc); |
789 | 1.13k | ti.m_rowHeightsInEmu = rowHeightsInEmu; |
790 | 1.13k | ti.m_columnWidthsInEmu = columnWidthsInEmu; |
791 | | |
792 | 1.13k | if (!index) |
793 | 23 | { |
794 | 23 | MSPUB_DEBUG_MSG(("WARNING: Couldn't find cells of seqnum %u corresponding to table of seqnum %u.\n", |
795 | 23 | csn, chunk.seqNum)); |
796 | 23 | return false; |
797 | 23 | } |
798 | 1.11k | else |
799 | 1.11k | { |
800 | 1.11k | const ContentChunkReference &cellsChunk = m_contentChunks[m_cellsChunkIndices[get(index)]]; |
801 | 1.11k | input->seek(cellsChunk.offset, librevenge::RVNG_SEEK_SET); |
802 | 1.11k | const unsigned cellsLength = readU32(input); |
803 | 1.11k | boost::optional<unsigned> cellCount; |
804 | 163k | while (stillReading(input, cellsChunk.offset + cellsLength)) |
805 | 162k | { |
806 | 162k | MSPUBBlockInfo info = parseBlock(input, true); |
807 | 162k | switch (info.id) |
808 | 162k | { |
809 | 3.01k | case 0x01: |
810 | 3.01k | cellCount = info.data; |
811 | 3.01k | break; |
812 | 3.19k | case 0x02: |
813 | 3.19k | { |
814 | 3.19k | input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
815 | 42.3k | while (stillReading(input, info.dataOffset + info.dataLength)) |
816 | 39.1k | { |
817 | 39.1k | const MSPUBBlockInfo itemInfo = parseBlock(input, true); |
818 | 39.1k | if (itemInfo.id == 0) |
819 | 19.9k | { |
820 | 19.9k | input->seek(itemInfo.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
821 | 19.9k | CellInfo currentCell; |
822 | 135k | while (stillReading(input, itemInfo.dataOffset + itemInfo.dataLength)) |
823 | 115k | { |
824 | 115k | const MSPUBBlockInfo subInfo = parseBlock(input, true); |
825 | 115k | switch (subInfo.id) |
826 | 115k | { |
827 | 8.39k | case 0x01: |
828 | 8.39k | currentCell.m_startRow = subInfo.data; |
829 | 8.39k | break; |
830 | 8.46k | case 0x02: |
831 | 8.46k | currentCell.m_endRow = subInfo.data; |
832 | 8.46k | break; |
833 | 6.68k | case 0x03: |
834 | 6.68k | currentCell.m_startColumn = subInfo.data; |
835 | 6.68k | break; |
836 | 7.68k | case 0x04: |
837 | 7.68k | currentCell.m_endColumn = subInfo.data; |
838 | 7.68k | break; |
839 | | // TODO: 0x09 - 0x0e: width/height of content + margins? |
840 | 84.0k | default: |
841 | 84.0k | break; |
842 | 115k | } |
843 | 115k | } |
844 | 19.9k | ti.m_cells.push_back(currentCell); |
845 | 19.9k | } |
846 | 39.1k | } |
847 | | |
848 | 3.19k | break; |
849 | 3.19k | } |
850 | 156k | default: |
851 | 156k | break; |
852 | 162k | } |
853 | 162k | } |
854 | | |
855 | 1.10k | if (bool(cellCount) && (get(cellCount) != ti.m_cells.size())) |
856 | 426 | { |
857 | 426 | MSPUB_DEBUG_MSG(("%u cell records expected, but read %u\n", get(cellCount), unsigned(ti.m_cells.size()))); |
858 | 426 | } |
859 | 1.10k | } |
860 | | |
861 | 1.10k | m_collector->setShapeTableInfo(chunk.seqNum, ti); |
862 | 1.10k | if (bool(textId)) |
863 | 993 | m_collector->addTextShape(get(textId), chunk.seqNum); |
864 | 1.10k | return true; |
865 | 1.13k | } |
866 | 66 | return false; |
867 | 1.23k | } |
868 | 30.3k | else |
869 | 30.3k | { |
870 | 30.3k | bool isText = false; |
871 | 30.3k | bool shouldStretchBorderArt = true; |
872 | 30.3k | unsigned textId = 0; |
873 | 30.3k | unsigned width = 0; |
874 | 30.3k | unsigned height = 0; |
875 | 5.03M | while (stillReading(input, pos + length)) |
876 | 5.00M | { |
877 | 5.00M | MSPUBBlockInfo info = parseBlock(input, true); |
878 | 5.00M | if (info.id == SHAPE_WIDTH) |
879 | 55.9k | { |
880 | 55.9k | width = info.data; |
881 | 55.9k | } |
882 | 4.95M | else if (info.id == SHAPE_HEIGHT) |
883 | 55.8k | { |
884 | 55.8k | height = info.data; |
885 | 55.8k | } |
886 | 4.89M | else if (info.id == SHAPE_BORDER_IMAGE_ID) |
887 | 18.1k | { |
888 | 18.1k | m_collector->setShapeBorderImageId(chunk.seqNum, info.data); |
889 | 18.1k | } |
890 | 4.87M | else if (info.id == SHAPE_DONT_STRETCH_BA) |
891 | 38.1k | { |
892 | 38.1k | shouldStretchBorderArt = false; |
893 | 38.1k | } |
894 | 4.84M | else if (info.id == SHAPE_TEXT_ID) |
895 | 15.8k | { |
896 | 15.8k | textId = info.data; |
897 | 15.8k | isText = true; |
898 | 15.8k | } |
899 | 4.82M | else if (info.id == SHAPE_VALIGN) |
900 | 7.63k | { |
901 | 7.63k | m_collector->setShapeVerticalTextAlign(chunk.seqNum, |
902 | 7.63k | static_cast<VerticalAlign>(info.data)); |
903 | 7.63k | } |
904 | 4.81M | else if (info.id == SHAPE_CROP && info.data != 0) |
905 | 411 | { |
906 | 411 | m_collector->setShapeCropType(chunk.seqNum, |
907 | 411 | static_cast<ShapeType>(info.data)); |
908 | 411 | } |
909 | 5.00M | } |
910 | 30.3k | if (shouldStretchBorderArt) |
911 | 26.3k | { |
912 | 26.3k | m_collector->setShapeStretchBorderArt(chunk.seqNum); |
913 | 26.3k | } |
914 | 30.3k | bool parseWithoutDimensions = true; //FIXME: Should we ever ignore if height and width not given? |
915 | 30.3k | if (isGroup || (height > 0 && width > 0) || parseWithoutDimensions) |
916 | 30.3k | { |
917 | 30.3k | if (! isGroup) |
918 | 29.1k | { |
919 | 29.1k | if (isText) |
920 | 5.95k | { |
921 | 5.95k | m_collector->addTextShape(textId, chunk.seqNum); |
922 | 5.95k | } |
923 | 29.1k | } |
924 | 30.3k | } |
925 | 11 | else |
926 | 11 | { |
927 | 11 | MSPUB_DEBUG_MSG(("Height and width not both specified, ignoring. (Height: 0x%x, Width: 0x%x)\n", height, width)); |
928 | 11 | } |
929 | 30.3k | return true; |
930 | 30.3k | } |
931 | 31.6k | } |
932 | | |
933 | | QuillChunkReference MSPUBParser::parseQuillChunkReference(librevenge::RVNGInputStream *input) |
934 | 82.6k | { |
935 | 82.6k | QuillChunkReference ret; |
936 | 82.6k | readU16(input); //FIXME: Can we do something sensible if this is not 0x18 ? |
937 | 82.6k | char name[5]; |
938 | 413k | for (int i = 0; i < 4; ++i) |
939 | 330k | { |
940 | 330k | name[i] = (char)readU8(input); |
941 | 330k | } |
942 | 82.6k | name[4] = '\0'; |
943 | 82.6k | ret.name = name; |
944 | 82.6k | ret.id = readU16(input); |
945 | 82.6k | input->seek(input->tell() + 4, librevenge::RVNG_SEEK_SET); //Seek past what is normally 0x01000000. We don't know what this represents. |
946 | 82.6k | char name2[5]; |
947 | 413k | for (int i = 0; i < 4; ++i) |
948 | 330k | { |
949 | 330k | name2[i] = (char)readU8(input); |
950 | 330k | } |
951 | 82.6k | name2[4] = '\0'; |
952 | 82.6k | ret.name2 = name2; |
953 | 82.6k | ret.offset = readU32(input); |
954 | 82.6k | ret.length = readU32(input); |
955 | 82.6k | return ret; |
956 | 82.6k | } |
957 | | |
958 | | std::vector<unsigned> MSPUBParser::parseTableCellDefinitions( |
959 | | librevenge::RVNGInputStream *input, const QuillChunkReference &chunk) |
960 | 1.52k | { |
961 | 1.52k | std::vector<unsigned> ret; |
962 | 1.52k | unsigned numElements = readU32(input) + 1; |
963 | 1.52k | input->seek(chunk.offset + 0xC, librevenge::RVNG_SEEK_SET); |
964 | 56.7k | for (unsigned i = 0; i < numElements; ++i) |
965 | 55.2k | { |
966 | 55.2k | ret.push_back(readU32(input)); |
967 | | // compensate for all but the last offset not including the terminating 0x0D00 |
968 | 55.2k | if (i != numElements - 1) |
969 | 53.7k | { |
970 | 53.7k | ret.back() += 2; |
971 | 53.7k | } |
972 | 55.2k | } |
973 | 1.52k | return ret; |
974 | 1.52k | } |
975 | | |
976 | | bool MSPUBParser::parseQuill(librevenge::RVNGInputStream *input) |
977 | 4.24k | { |
978 | 4.24k | MSPUB_DEBUG_MSG(("MSPUBParser::parseQuill\n")); |
979 | 4.24k | unsigned chunkReferenceListOffset = 0x18; |
980 | 4.24k | std::list<QuillChunkReference> chunkReferences; |
981 | 4.24k | std::set<unsigned> readChunks; // guard against cycle in the chunk list |
982 | 8.87k | while (chunkReferenceListOffset != 0xffffffff) |
983 | 6.70k | { |
984 | 6.70k | input->seek(chunkReferenceListOffset + 2, librevenge::RVNG_SEEK_SET); |
985 | 6.70k | unsigned short numChunks = readU16(input); |
986 | 6.70k | chunkReferenceListOffset = readU32(input); |
987 | 6.70k | if (readChunks.find(chunkReferenceListOffset) != readChunks.end()) |
988 | 2.06k | { |
989 | 2.06k | MSPUB_DEBUG_MSG(("Found a cycle in chunk reference list: a broken file!\n")); |
990 | 2.06k | break; |
991 | 2.06k | } |
992 | 4.63k | readChunks.insert(chunkReferenceListOffset); |
993 | 87.3k | for (unsigned i = 0; i < numChunks; ++i) |
994 | 82.6k | { |
995 | 82.6k | QuillChunkReference quillChunkReference = parseQuillChunkReference(input); |
996 | 82.6k | chunkReferences.push_back(quillChunkReference); |
997 | 82.6k | } |
998 | 4.63k | } |
999 | 4.24k | MSPUB_DEBUG_MSG(("Found %u Quill chunks\n", (unsigned)chunkReferences.size())); |
1000 | | //Make sure we parse the STRS chunk before the TEXT chunk |
1001 | 4.24k | std::list<QuillChunkReference>::const_iterator textChunkReference = chunkReferences.end(); |
1002 | 4.24k | bool parsedStrs = false; |
1003 | 4.24k | bool parsedSyid = false; |
1004 | 4.24k | bool parsedFdpc = false; |
1005 | 4.24k | bool parsedFdpp = false; |
1006 | 4.24k | bool parsedStsh = false; |
1007 | 4.24k | bool parsedFont = false; |
1008 | 4.24k | std::vector<unsigned> textLengths; |
1009 | 4.24k | std::vector<unsigned> textIDs; |
1010 | 4.24k | std::vector<unsigned> textOffsets; |
1011 | 4.24k | std::map<unsigned, std::vector<unsigned> > tableCellTextEnds; |
1012 | 4.24k | unsigned textOffsetAccum = 0; |
1013 | 4.24k | std::vector<TextSpanReference> spans; |
1014 | 4.24k | std::vector<TextParagraphReference> paras; |
1015 | 4.24k | unsigned whichStsh = 0; |
1016 | 81.0k | for (std::list<QuillChunkReference>::const_iterator i = chunkReferences.begin(); i != chunkReferences.end(); ++i) |
1017 | 76.7k | { |
1018 | 76.7k | if (i->name == "TEXT") |
1019 | 1.38k | { |
1020 | 1.38k | textChunkReference = i; |
1021 | 1.38k | } |
1022 | 75.4k | else if (i->name == "STRS") |
1023 | 1.08k | { |
1024 | 1.08k | input->seek(i->offset, librevenge::RVNG_SEEK_SET); |
1025 | 1.08k | unsigned numLengths = readU32(input); //Assuming the first DWORD is the number of children and that the next is the remaining length before children start. We are unsure that this is correct. |
1026 | 1.08k | input->seek(4 + i->offset + readU32(input), librevenge::RVNG_SEEK_SET); |
1027 | 26.2k | for (unsigned j = 0; j < numLengths; ++j) |
1028 | 25.1k | { |
1029 | 25.1k | unsigned length = readU32(input); |
1030 | 25.1k | textLengths.push_back(length); |
1031 | 25.1k | textOffsets.push_back(textOffsetAccum); |
1032 | 25.1k | textOffsetAccum += length * 2; |
1033 | 25.1k | } |
1034 | 1.08k | parsedStrs = true; |
1035 | 1.08k | } |
1036 | 74.3k | else if (i->name == "SYID") |
1037 | 1.15k | { |
1038 | 1.15k | input->seek(i->offset, librevenge::RVNG_SEEK_SET); |
1039 | 1.15k | readU32(input); // Don't know what the first DWORD means. |
1040 | 1.15k | unsigned numIDs = readU32(input); |
1041 | 74.1k | for (unsigned j = 0; j < numIDs; ++j) |
1042 | 72.9k | { |
1043 | 72.9k | textIDs.push_back(readU32(input)); |
1044 | 72.9k | } |
1045 | 1.15k | parsedSyid = true; |
1046 | 1.15k | } |
1047 | 73.1k | else if (i->name == "PL ") |
1048 | 608 | { |
1049 | 608 | input->seek(i->offset, librevenge::RVNG_SEEK_SET); |
1050 | 608 | parseColors(input, *i); |
1051 | 608 | } |
1052 | 72.5k | else if (i->name == "FDPC") |
1053 | 2.45k | { |
1054 | 2.45k | input->seek(i->offset, librevenge::RVNG_SEEK_SET); |
1055 | 2.45k | std::vector<TextSpanReference> thisBlockSpans = parseCharacterStyles(input, *i); |
1056 | 2.45k | spans.insert(spans.end(), thisBlockSpans.begin(), thisBlockSpans.end()); |
1057 | 2.45k | parsedFdpc |= !thisBlockSpans.empty(); |
1058 | 2.45k | } |
1059 | 70.0k | else if (i->name == "FDPP") |
1060 | 2.08k | { |
1061 | 2.08k | input->seek(i->offset, librevenge::RVNG_SEEK_SET); |
1062 | 2.08k | std::vector<TextParagraphReference> thisBlockParas = parseParagraphStyles(input, *i); |
1063 | 2.08k | paras.insert(paras.end(), thisBlockParas.begin(), thisBlockParas.end()); |
1064 | 2.08k | parsedFdpp |= !thisBlockParas.empty(); |
1065 | 2.08k | } |
1066 | 68.0k | else if (i->name == "STSH") |
1067 | 2.83k | { |
1068 | 2.83k | if (whichStsh++ == 1) |
1069 | 1.13k | { |
1070 | 1.13k | input->seek(i->offset, librevenge::RVNG_SEEK_SET); |
1071 | 1.13k | parseDefaultStyle(input, *i); |
1072 | 1.13k | parsedStsh = true; |
1073 | 1.13k | } |
1074 | 2.83k | } |
1075 | 65.1k | else if (i->name == "FONT") |
1076 | 1.02k | { |
1077 | 1.02k | input->seek(i->offset, librevenge::RVNG_SEEK_SET); |
1078 | 1.02k | parseFonts(input, *i); |
1079 | 1.02k | parsedFont = true; |
1080 | 1.02k | } |
1081 | 64.1k | else if (i->name == "TCD ") |
1082 | 1.52k | { |
1083 | 1.52k | input->seek(i->offset, librevenge::RVNG_SEEK_SET); |
1084 | 1.52k | tableCellTextEnds[i->id] = parseTableCellDefinitions(input, *i); |
1085 | 1.52k | } |
1086 | 76.7k | } |
1087 | 4.24k | if (parsedStrs && parsedSyid && parsedFdpc && parsedFdpp && parsedStsh && parsedFont && textChunkReference != chunkReferences.end()) |
1088 | 819 | { |
1089 | 819 | input->seek(textChunkReference->offset, librevenge::RVNG_SEEK_SET); |
1090 | 819 | unsigned bytesRead = 0; |
1091 | 819 | auto currentTextSpan = spans.begin(); |
1092 | 819 | auto currentTextPara = paras.begin(); |
1093 | 11.9k | for (unsigned j = 0; j < textIDs.size() && j < textLengths.size(); ++j) |
1094 | 11.1k | { |
1095 | 11.1k | MSPUB_DEBUG_MSG(("Parsing a text block.\n")); |
1096 | 11.1k | std::vector<TextParagraph> readParas; |
1097 | 11.1k | std::vector<TextSpan> readSpans; |
1098 | 11.1k | std::vector<unsigned char> text; |
1099 | 837k | for (unsigned k = 0; k < textLengths[j] && currentTextPara != paras.end() && currentTextSpan != spans.end(); ++k) |
1100 | 826k | { |
1101 | 826k | text.push_back(readU8(input)); |
1102 | 826k | text.push_back(readU8(input)); |
1103 | 826k | bytesRead += 2; |
1104 | 826k | if (bytesRead >= currentTextSpan->last - textChunkReference->offset) |
1105 | 16.0k | { |
1106 | 16.0k | if (!text.empty()) |
1107 | 16.0k | { |
1108 | 16.0k | readSpans.push_back(TextSpan(text, currentTextSpan->charStyle)); |
1109 | 16.0k | MSPUB_DEBUG_MSG(("Saw text span %d in the current text paragraph.\n", (unsigned)readSpans.size())); |
1110 | 16.0k | } |
1111 | 16.0k | ++currentTextSpan; |
1112 | 16.0k | text.clear(); |
1113 | 16.0k | } |
1114 | 826k | if (bytesRead >= currentTextPara->last - textChunkReference->offset) |
1115 | 21.4k | { |
1116 | 21.4k | if (!text.empty()) |
1117 | 16.9k | { |
1118 | 16.9k | readSpans.push_back(TextSpan(text, currentTextSpan->charStyle)); |
1119 | 16.9k | MSPUB_DEBUG_MSG(("Saw text span %d in the current text paragraph.\n", (unsigned)readSpans.size())); |
1120 | 16.9k | } |
1121 | 21.4k | text.clear(); |
1122 | 21.4k | if (!readSpans.empty()) |
1123 | 21.4k | { |
1124 | 21.4k | readParas.push_back(TextParagraph(readSpans, currentTextPara->paraStyle)); |
1125 | 21.4k | MSPUB_DEBUG_MSG(("Saw paragraph %d in the current text block.\n", (unsigned)readParas.size())); |
1126 | 21.4k | } |
1127 | 21.4k | ++currentTextPara; |
1128 | 21.4k | readSpans.clear(); |
1129 | 21.4k | } |
1130 | 826k | } |
1131 | 11.1k | if (!text.empty() && currentTextSpan != spans.end()) |
1132 | 1.48k | { |
1133 | 1.48k | readSpans.push_back(TextSpan(text, currentTextSpan->charStyle)); |
1134 | 1.48k | MSPUB_DEBUG_MSG(("Saw text span %d in the current text paragraph.\n", (unsigned)readSpans.size())); |
1135 | 1.48k | } |
1136 | 11.1k | text.clear(); |
1137 | 11.1k | if (!readSpans.empty() && currentTextPara != paras.end()) |
1138 | 1.94k | { |
1139 | 1.94k | readParas.push_back(TextParagraph(readSpans, currentTextPara->paraStyle)); |
1140 | 1.94k | MSPUB_DEBUG_MSG(("Saw paragraph %d in the current text block.\n", (unsigned)readParas.size())); |
1141 | 1.94k | } |
1142 | 11.1k | m_collector->addTextString(readParas, textIDs[j]); |
1143 | 11.1k | m_collector->setTextStringOffset(textIDs[j], textOffsets[j]); |
1144 | 11.1k | const std::map<unsigned, std::vector<unsigned> >::const_iterator it = tableCellTextEnds.find(j); |
1145 | 11.1k | if (it != tableCellTextEnds.end()) |
1146 | 868 | m_collector->setTableCellTextEnds(textIDs[j], it->second); |
1147 | 11.1k | } |
1148 | 819 | textChunkReference = chunkReferences.end(); |
1149 | 819 | } |
1150 | 4.24k | return true; |
1151 | 4.24k | } |
1152 | | |
1153 | | void MSPUBParser::parseFonts(librevenge::RVNGInputStream *input, const QuillChunkReference &) |
1154 | 1.02k | { |
1155 | 1.02k | readU32(input); |
1156 | 1.02k | unsigned numElements = readU32(input); |
1157 | 1.02k | input->seek(input->tell() + 12 + 4 * numElements, librevenge::RVNG_SEEK_SET); |
1158 | 7.76k | for (unsigned i = 0; i < numElements; ++i) |
1159 | 6.73k | { |
1160 | 6.73k | unsigned short nameLength = readU16(input); |
1161 | 6.73k | if (nameLength > 0) |
1162 | 5.59k | { |
1163 | 5.59k | std::vector<unsigned char> name; |
1164 | 5.59k | readNBytes(input, nameLength * 2, name); |
1165 | 5.59k | m_collector->addFont(name); |
1166 | 5.59k | } |
1167 | 6.73k | readU32(input); |
1168 | 6.73k | } |
1169 | 1.02k | } |
1170 | | |
1171 | | void MSPUBParser::parseDefaultStyle(librevenge::RVNGInputStream *input, const QuillChunkReference &chunk) |
1172 | 1.13k | { |
1173 | 1.13k | readU32(input); |
1174 | 1.13k | unsigned numElements = std::min(readU32(input), m_length); |
1175 | 1.13k | input->seek(input->tell() + 12, librevenge::RVNG_SEEK_SET); |
1176 | 1.13k | std::vector<unsigned> offsets; |
1177 | 1.13k | offsets.reserve(numElements); |
1178 | 20.2k | for (unsigned i = 0; i < numElements; ++i) |
1179 | 19.1k | { |
1180 | 19.1k | offsets.push_back(readU32(input)); |
1181 | 19.1k | } |
1182 | 8.06k | for (unsigned i = 0; i < numElements; ++i) |
1183 | 6.92k | { |
1184 | 6.92k | input->seek(chunk.offset + 20 + offsets[i], librevenge::RVNG_SEEK_SET); |
1185 | 6.92k | readU16(input); |
1186 | 6.92k | if (i % 2 == 0) |
1187 | 3.46k | { |
1188 | | //FIXME: Does STSH2 hold information for associating style indices in FDPP to indices in STSH1 ? |
1189 | 3.46k | m_collector->addDefaultCharacterStyle(getCharacterStyle(input)); |
1190 | 3.46k | } |
1191 | 3.46k | else |
1192 | 3.46k | { |
1193 | 3.46k | m_collector->addDefaultParagraphStyle(getParagraphStyle(input)); |
1194 | 3.46k | } |
1195 | 6.92k | } |
1196 | 1.13k | } |
1197 | | |
1198 | | |
1199 | | void MSPUBParser::parseColors(librevenge::RVNGInputStream *input, const QuillChunkReference &) |
1200 | 608 | { |
1201 | 608 | unsigned numEntries = readU32(input); |
1202 | 608 | input->seek(input->tell() + 8, librevenge::RVNG_SEEK_SET); |
1203 | 1.52k | for (unsigned i = 0; i < numEntries; ++i) |
1204 | 917 | { |
1205 | 917 | unsigned blocksOffset = input->tell(); |
1206 | 917 | unsigned len = readU32(input); |
1207 | 22.9k | while (stillReading(input, blocksOffset + len)) |
1208 | 22.0k | { |
1209 | 22.0k | MSPUBBlockInfo info = parseBlock(input, true); |
1210 | 22.0k | if (info.id == 0x01) |
1211 | 1.03k | { |
1212 | 1.03k | m_collector->addTextColor(ColorReference(info.data)); |
1213 | 1.03k | } |
1214 | 22.0k | } |
1215 | 917 | } |
1216 | 608 | } |
1217 | | |
1218 | | std::vector<MSPUBParser::TextParagraphReference> MSPUBParser::parseParagraphStyles(librevenge::RVNGInputStream *input, const QuillChunkReference &chunk) |
1219 | 2.08k | { |
1220 | 2.08k | std::vector<TextParagraphReference> ret; |
1221 | 2.08k | unsigned short numEntries = readU16(input); |
1222 | 2.08k | input->seek(input->tell() + 6, librevenge::RVNG_SEEK_SET); |
1223 | 2.08k | std::vector<unsigned> textOffsets; |
1224 | 2.08k | textOffsets.reserve(numEntries); |
1225 | 2.08k | std::vector<unsigned short> chunkOffsets; |
1226 | 2.08k | textOffsets.reserve(numEntries); |
1227 | 85.2k | for (unsigned short i = 0; i < numEntries; ++i) |
1228 | 83.1k | { |
1229 | 83.1k | textOffsets.push_back(readU32(input)); |
1230 | 83.1k | } |
1231 | 78.2k | for (unsigned short i = 0; i < numEntries; ++i) |
1232 | 76.2k | { |
1233 | 76.2k | chunkOffsets.push_back(readU16(input)); |
1234 | 76.2k | } |
1235 | 2.08k | unsigned currentSpanBegin = 0; |
1236 | 74.1k | for (unsigned short i = 0; i < numEntries; ++i) |
1237 | 72.1k | { |
1238 | 72.1k | input->seek(chunk.offset + chunkOffsets[i], librevenge::RVNG_SEEK_SET); |
1239 | 72.1k | ParagraphStyle style = getParagraphStyle(input); |
1240 | 72.1k | ret.push_back(TextParagraphReference(currentSpanBegin, textOffsets[i], style)); |
1241 | 72.1k | currentSpanBegin = textOffsets[i] + 1; |
1242 | 72.1k | } |
1243 | 2.08k | return ret; |
1244 | 2.08k | } |
1245 | | |
1246 | | std::vector<MSPUBParser::TextSpanReference> MSPUBParser::parseCharacterStyles(librevenge::RVNGInputStream *input, const QuillChunkReference &chunk) |
1247 | 2.45k | { |
1248 | 2.45k | unsigned short numEntries = readU16(input); |
1249 | 2.45k | input->seek(input->tell() + 6, librevenge::RVNG_SEEK_SET); |
1250 | 2.45k | std::vector<unsigned> textOffsets; |
1251 | 2.45k | textOffsets.reserve(numEntries); |
1252 | 2.45k | std::vector<unsigned short> chunkOffsets; |
1253 | 2.45k | chunkOffsets.reserve(numEntries); |
1254 | 2.45k | std::vector<TextSpanReference> ret; |
1255 | 83.6k | for (unsigned short i = 0; i < numEntries; ++i) |
1256 | 81.2k | { |
1257 | 81.2k | textOffsets.push_back(readU32(input)); |
1258 | 81.2k | } |
1259 | 58.8k | for (unsigned short i = 0; i < numEntries; ++i) |
1260 | 56.4k | { |
1261 | 56.4k | chunkOffsets.push_back(readU16(input)); |
1262 | 56.4k | } |
1263 | 2.45k | unsigned currentSpanBegin = 0; |
1264 | 53.3k | for (unsigned short i = 0; i < numEntries; ++i) |
1265 | 50.9k | { |
1266 | 50.9k | input->seek(chunk.offset + chunkOffsets[i], librevenge::RVNG_SEEK_SET); |
1267 | 50.9k | CharacterStyle style = getCharacterStyle(input); |
1268 | 50.9k | currentSpanBegin = textOffsets[i] + 1; |
1269 | 50.9k | ret.push_back(TextSpanReference(currentSpanBegin, textOffsets[i], style)); |
1270 | 50.9k | } |
1271 | 2.45k | return ret; |
1272 | 2.45k | } |
1273 | | ParagraphStyle MSPUBParser::getParagraphStyle(librevenge::RVNGInputStream *input) |
1274 | 75.5k | { |
1275 | 75.5k | ParagraphStyle ret; |
1276 | | |
1277 | 75.5k | bool isList = false; |
1278 | 75.5k | unsigned bulletChar = 0; |
1279 | 75.5k | NumberingType numberingType = STANDARD_WESTERN; |
1280 | 75.5k | NumberingDelimiter numberingDelimiter = NO_DELIMITER; |
1281 | 75.5k | boost::optional<unsigned> numberIfRestarted; |
1282 | | |
1283 | 75.5k | unsigned offset = input->tell(); |
1284 | 75.5k | unsigned len = readU32(input); |
1285 | 29.5M | while (stillReading(input, offset + len)) |
1286 | 29.4M | { |
1287 | 29.4M | MSPUBBlockInfo info = parseBlock(input, true); |
1288 | 29.4M | switch (info.id) |
1289 | 29.4M | { |
1290 | 171k | case PARAGRAPH_ALIGNMENT: |
1291 | 171k | ret.m_align = (Alignment)(info.data & 0xFF); // Is this correct? |
1292 | 171k | break; |
1293 | 22.4k | case PARAGRAPH_DEFAULT_CHAR_STYLE: |
1294 | 22.4k | ret.m_defaultCharStyleIndex = info.data; |
1295 | 22.4k | break; |
1296 | 62.8k | case PARAGRAPH_LINE_SPACING: |
1297 | 62.8k | if (info.data & 1) |
1298 | 4.08k | { |
1299 | | // line spacing expressed in points in the UI, |
1300 | | // in eighths of an emu in the file format. |
1301 | | // (WTF??) |
1302 | 4.08k | ret.m_lineSpacing = LineSpacingInfo(LINE_SPACING_PT, |
1303 | 4.08k | static_cast<double>(info.data - 1) / 8 * 72 / EMUS_IN_INCH); |
1304 | 4.08k | } |
1305 | 58.7k | else if (info.data & 2) |
1306 | 4.17k | { |
1307 | | // line spacing expressed in SP in the UI, |
1308 | | // in what would be EMUs if font size were 96pt in the file format |
1309 | | // (WTF??) |
1310 | 4.17k | ret.m_lineSpacing = LineSpacingInfo(LINE_SPACING_SP, |
1311 | 4.17k | static_cast<double>(info.data - 2) / EMUS_IN_INCH * 72 / 96); |
1312 | 4.17k | } |
1313 | 62.8k | break; |
1314 | 226k | case PARAGRAPH_SPACE_BEFORE: |
1315 | 226k | ret.m_spaceBeforeEmu = info.data; |
1316 | 226k | break; |
1317 | 44.4k | case PARAGRAPH_SPACE_AFTER: |
1318 | 44.4k | ret.m_spaceAfterEmu = info.data; |
1319 | 44.4k | break; |
1320 | 60.6k | case PARAGRAPH_FIRST_LINE_INDENT: |
1321 | 60.6k | ret.m_firstLineIndentEmu = (int)info.data; |
1322 | 60.6k | break; |
1323 | 291k | case PARAGRAPH_LEFT_INDENT: |
1324 | 291k | ret.m_leftIndentEmu = info.data; |
1325 | 291k | break; |
1326 | 29.5k | case PARAGRAPH_RIGHT_INDENT: |
1327 | 29.5k | ret.m_rightIndentEmu = info.data; |
1328 | 29.5k | break; |
1329 | 173k | case PARAGRAPH_TABS: |
1330 | 173k | input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
1331 | 630k | while (stillReading(input, info.dataOffset + info.dataLength)) |
1332 | 457k | { |
1333 | 457k | MSPUBBlockInfo tabArrayInfo = parseBlock(input, true); |
1334 | 457k | if (tabArrayInfo.id == TAB_ARRAY) |
1335 | 14.6k | { |
1336 | 14.6k | input->seek(tabArrayInfo.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
1337 | 158k | while (stillReading(input, tabArrayInfo.dataOffset + tabArrayInfo.dataLength)) |
1338 | 143k | { |
1339 | 143k | MSPUBBlockInfo tabEntryInfo = parseBlock(input, true); |
1340 | 143k | if (tabEntryInfo.type == GENERAL_CONTAINER) |
1341 | 22.4k | { |
1342 | 22.4k | input->seek(tabEntryInfo.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
1343 | 22.4k | MSPUBBlockInfo tabInfo = parseBlock(input, true); |
1344 | 22.4k | if (tabInfo.id == TAB_AMOUNT) |
1345 | 21.9k | { |
1346 | 21.9k | ret.m_tabStopsInEmu.push_back(tabInfo.data); |
1347 | 21.9k | } |
1348 | 22.4k | } |
1349 | 143k | } |
1350 | 14.6k | } |
1351 | 457k | } |
1352 | 173k | break; |
1353 | 13.9k | case PARAGRAPH_LIST_INFO: |
1354 | 13.9k | { |
1355 | 13.9k | isList = true; |
1356 | 13.9k | input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
1357 | 34.2k | while (stillReading(input, info.dataOffset + info.dataLength)) |
1358 | 20.3k | { |
1359 | 20.3k | MSPUBBlockInfo listSubInfo = parseBlock(input, true); |
1360 | 20.3k | switch (listSubInfo.id) |
1361 | 20.3k | { |
1362 | 12.1k | case PARAGRAPH_LIST_NUMBERING_TYPE: |
1363 | 12.1k | numberingType = static_cast<NumberingType>(info.data); |
1364 | 12.1k | break; |
1365 | 947 | case PARAGRAPH_LIST_BULLET_CHAR: |
1366 | 947 | bulletChar = info.data; |
1367 | 947 | break; |
1368 | 7.27k | default: |
1369 | 7.27k | break; |
1370 | 20.3k | } |
1371 | 20.3k | } |
1372 | 13.9k | break; |
1373 | 13.9k | } |
1374 | 35.2k | case PARAGRAPH_LIST_NUMBER_RESTART: |
1375 | 35.2k | numberIfRestarted = info.data; |
1376 | 35.2k | break; |
1377 | 180k | case PARAGRAPH_DROP_CAP_LINES: |
1378 | 180k | ret.m_dropCapLines = info.data; |
1379 | 180k | break; |
1380 | 31.0k | case PARAGRAPH_DROP_CAP_LETTERS: |
1381 | 31.0k | ret.m_dropCapLetters = info.data; |
1382 | 31.0k | break; |
1383 | 28.1M | default: |
1384 | 28.1M | break; |
1385 | 29.4M | } |
1386 | 29.4M | } |
1387 | 75.5k | if (isList) |
1388 | 12.2k | { |
1389 | 12.2k | if (bulletChar) |
1390 | 0 | { |
1391 | 0 | ret.m_listInfo = ListInfo(bulletChar); |
1392 | 0 | } |
1393 | 12.2k | else |
1394 | 12.2k | { |
1395 | 12.2k | ret.m_listInfo = ListInfo(numberIfRestarted, numberingType, |
1396 | 12.2k | numberingDelimiter); |
1397 | 12.2k | } |
1398 | 12.2k | } |
1399 | | |
1400 | 75.5k | return ret; |
1401 | 75.5k | } |
1402 | | |
1403 | | CharacterStyle MSPUBParser::getCharacterStyle(librevenge::RVNGInputStream *input) |
1404 | 54.3k | { |
1405 | 54.3k | CharacterStyle style; |
1406 | | |
1407 | 54.3k | bool seenBold1 = false, seenBold2 = false, seenItalic1 = false, seenItalic2 = false; |
1408 | 54.3k | int textSize1 = -1, /* textSize2 = -1,*/ colorIndex = -1; |
1409 | 54.3k | boost::optional<unsigned> fontIndex; |
1410 | 54.3k | unsigned offset = input->tell(); |
1411 | 54.3k | unsigned len = readU32(input); |
1412 | 21.1M | while (stillReading(input, offset + len)) |
1413 | 21.1M | { |
1414 | 21.1M | MSPUBBlockInfo info = parseBlock(input, true); |
1415 | 21.1M | switch (info.id) |
1416 | 21.1M | { |
1417 | 64.1k | case BOLD_1_ID: |
1418 | 64.1k | seenBold1 = true; |
1419 | 64.1k | break; |
1420 | 15.1k | case BOLD_2_ID: |
1421 | 15.1k | seenBold2 = true; |
1422 | 15.1k | break; |
1423 | 28.7k | case ITALIC_1_ID: |
1424 | 28.7k | seenItalic1 = true; |
1425 | 28.7k | break; |
1426 | 20.8k | case ITALIC_2_ID: |
1427 | 20.8k | seenItalic2 = true; |
1428 | 20.8k | break; |
1429 | 25.7k | case UNDERLINE_ID: |
1430 | 25.7k | style.underline = readUnderline(info.data); |
1431 | 25.7k | break; |
1432 | 26.4k | case TEXT_SIZE_1_ID: |
1433 | 26.4k | textSize1 = info.data; |
1434 | 26.4k | break; |
1435 | 25.8k | case TEXT_SIZE_2_ID: |
1436 | | // textSize2 = info.data; |
1437 | 25.8k | break; |
1438 | 60.0k | case BARE_COLOR_INDEX_ID: |
1439 | 60.0k | colorIndex = info.data; |
1440 | 60.0k | break; |
1441 | 25.7k | case COLOR_INDEX_CONTAINER_ID: |
1442 | 25.7k | colorIndex = getColorIndex(input, info); |
1443 | 25.7k | break; |
1444 | 26.8k | case FONT_INDEX_CONTAINER_ID: |
1445 | 26.8k | fontIndex = getFontIndex(input, info); |
1446 | 26.8k | break; |
1447 | 13.1k | case SUPER_SUB_TYPE_ID: |
1448 | 13.1k | style.superSubType = static_cast<SuperSubType>(info.data); |
1449 | 13.1k | break; |
1450 | 69.4k | case OUTLINE_ID: |
1451 | 69.4k | style.outline = true; |
1452 | 69.4k | break; |
1453 | 31.1k | case SHADOW_ID: |
1454 | 31.1k | style.shadow = true; |
1455 | 31.1k | break; |
1456 | 19.8k | case SMALL_CAPS_ID: |
1457 | 19.8k | style.smallCaps = true; |
1458 | 19.8k | break; |
1459 | 13.4k | case ALL_CAPS_ID: |
1460 | 13.4k | style.allCaps = true; |
1461 | 13.4k | break; |
1462 | 14.6k | case EMBOSS_ID: |
1463 | 14.6k | style.emboss = true; |
1464 | 14.6k | break; |
1465 | 19.6k | case ENGRAVE_ID: |
1466 | 19.6k | style.engrave = true; |
1467 | 19.6k | break; |
1468 | 869k | case SCALING_ID: |
1469 | 869k | style.textScale = double(info.data) / 10; |
1470 | 869k | break; |
1471 | 130k | case LOCALE_ID: |
1472 | 130k | style.lcid = info.data; |
1473 | 130k | break; |
1474 | 19.6M | default: |
1475 | 19.6M | break; |
1476 | 21.1M | } |
1477 | 21.1M | } |
1478 | | //FIXME: Figure out what textSize2 is used for. Can we find a document where it differs from textSize1 ? |
1479 | | // textSize2 = textSize1; |
1480 | 54.3k | boost::optional<double> dTextSize; |
1481 | 54.3k | if (textSize1 != -1) |
1482 | 11.1k | { |
1483 | 11.1k | dTextSize = textSize1 * (double(POINTS_IN_INCH) / EMUS_IN_INCH); |
1484 | 11.1k | } |
1485 | | // FIXME: What's this with foo1 && foo2? I've only seen foo1 in 2k2 |
1486 | | // files and either just foo1 or foo1+foo2 in 2k7 files... |
1487 | 54.3k | style.italic = seenItalic1; // && seenItalic2; |
1488 | 54.3k | style.bold = seenBold1; // && seenBold2; |
1489 | 54.3k | (void) seenItalic2; |
1490 | 54.3k | (void) seenBold2; |
1491 | 54.3k | style.textSizeInPt = dTextSize; |
1492 | 54.3k | style.colorIndex = getColorIndexByQuillEntry(colorIndex); |
1493 | 54.3k | style.fontIndex = fontIndex; |
1494 | | |
1495 | 54.3k | return style; |
1496 | 54.3k | } |
1497 | | |
1498 | | unsigned MSPUBParser::getFontIndex(librevenge::RVNGInputStream *input, const MSPUBBlockInfo &info) |
1499 | 26.8k | { |
1500 | 26.8k | MSPUB_DEBUG_MSG(("In getFontIndex\n")); |
1501 | 26.8k | input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
1502 | 254k | while (stillReading(input, info.dataOffset + info.dataLength)) |
1503 | 234k | { |
1504 | 234k | MSPUBBlockInfo subInfo = parseBlock(input, true); |
1505 | 234k | if (subInfo.type == GENERAL_CONTAINER) |
1506 | 7.29k | { |
1507 | 7.29k | input->seek(subInfo.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
1508 | 7.29k | if (stillReading(input, subInfo.dataOffset + subInfo.dataLength)) |
1509 | 7.06k | { |
1510 | 7.06k | MSPUBBlockInfo subSubInfo = parseBlock(input, true); |
1511 | 7.06k | skipBlock(input, info); |
1512 | 7.06k | return subSubInfo.data; |
1513 | 7.06k | } |
1514 | 7.29k | } |
1515 | 234k | } |
1516 | 19.7k | return 0; |
1517 | 26.8k | } |
1518 | | |
1519 | | int MSPUBParser::getColorIndex(librevenge::RVNGInputStream *input, const MSPUBBlockInfo &info) |
1520 | 25.7k | { |
1521 | 25.7k | input->seek(info.dataOffset + 4, librevenge::RVNG_SEEK_SET); |
1522 | 26.7k | while (stillReading(input, info.dataOffset + info.dataLength)) |
1523 | 4.23k | { |
1524 | 4.23k | MSPUBBlockInfo subInfo = parseBlock(input, true); |
1525 | 4.23k | if (subInfo.id == COLOR_INDEX_ID) |
1526 | 3.24k | { |
1527 | 3.24k | skipBlock(input, info); |
1528 | 3.24k | MSPUB_DEBUG_MSG(("Found color index 0x%x\n", (unsigned)subInfo.data)); |
1529 | 3.24k | return subInfo.data; |
1530 | 3.24k | } |
1531 | 4.23k | } |
1532 | 22.5k | MSPUB_DEBUG_MSG(("Failed to find color index!\n")); |
1533 | 22.5k | return -1; |
1534 | 25.7k | } |
1535 | | |
1536 | | bool MSPUBParser::parseEscher(librevenge::RVNGInputStream *input) |
1537 | 1.85k | { |
1538 | 1.85k | MSPUB_DEBUG_MSG(("MSPUBParser::parseEscher\n")); |
1539 | 1.85k | EscherContainerInfo fakeroot; |
1540 | 1.85k | fakeroot.initial = 0; |
1541 | 1.85k | fakeroot.type = 0; |
1542 | 1.85k | fakeroot.contentsOffset = input->tell(); |
1543 | 1.85k | fakeroot.contentsLength = (unsigned long)-1; //FIXME: Get the actual length |
1544 | 1.85k | EscherContainerInfo dg, dgg; |
1545 | | //Note: this assumes that dgg comes before any dg with images. |
1546 | 1.85k | if (findEscherContainer(input, fakeroot, dgg, OFFICE_ART_DGG_CONTAINER)) |
1547 | 1.60k | { |
1548 | 1.60k | EscherContainerInfo bsc; |
1549 | 1.60k | if (findEscherContainer(input, fakeroot, bsc, OFFICE_ART_B_STORE_CONTAINER)) |
1550 | 1.17k | { |
1551 | 1.17k | unsigned short currentDelayIndex = 1; |
1552 | 67.3k | while (stillReading(input, bsc.contentsOffset + bsc.contentsLength)) |
1553 | 66.1k | { |
1554 | 66.1k | unsigned begin = input->tell(); |
1555 | 66.1k | input->seek(begin + 10, librevenge::RVNG_SEEK_SET); |
1556 | 66.1k | if (!(readU32(input) == 0 && readU32(input) == 0 && readU32(input) == 0 && readU32(input) == 0)) |
1557 | 64.1k | { |
1558 | 64.1k | m_escherDelayIndices.push_back(currentDelayIndex++); |
1559 | 64.1k | } |
1560 | 1.98k | else |
1561 | 1.98k | { |
1562 | 1.98k | m_escherDelayIndices.push_back(-1); |
1563 | 1.98k | } |
1564 | 66.1k | input->seek(begin + 44, librevenge::RVNG_SEEK_SET); |
1565 | 66.1k | } |
1566 | 1.17k | } |
1567 | 1.60k | input->seek(dgg.contentsOffset + dgg.contentsLength + getEscherElementTailLength(OFFICE_ART_DGG_CONTAINER), librevenge::RVNG_SEEK_SET); |
1568 | 1.60k | } |
1569 | 6.33k | while (findEscherContainer(input, fakeroot, dg, OFFICE_ART_DG_CONTAINER)) |
1570 | 4.47k | { |
1571 | 4.47k | EscherContainerInfo spgr; |
1572 | 8.90k | while (findEscherContainer(input, dg, spgr, OFFICE_ART_SPGR_CONTAINER)) |
1573 | 4.43k | { |
1574 | 4.43k | Coordinate c1, c2; |
1575 | 4.43k | parseShapeGroup(input, spgr, c1, c2); |
1576 | 4.43k | } |
1577 | 4.47k | input->seek(input->tell() + getEscherElementTailLength(OFFICE_ART_DG_CONTAINER), librevenge::RVNG_SEEK_SET); |
1578 | 4.47k | } |
1579 | 1.85k | return true; |
1580 | 1.85k | } |
1581 | | |
1582 | | void MSPUBParser::parseShapeGroup(librevenge::RVNGInputStream *input, const EscherContainerInfo &spgr, Coordinate parentCoordinateSystem, Coordinate parentGroupAbsoluteCoord) |
1583 | 5.09k | { |
1584 | 5.09k | EscherContainerInfo shapeOrGroup; |
1585 | 5.09k | std::set<unsigned short> types; |
1586 | 5.09k | types.insert(OFFICE_ART_SPGR_CONTAINER); |
1587 | 5.09k | types.insert(OFFICE_ART_SP_CONTAINER); |
1588 | 58.4k | while (findEscherContainerWithTypeInSet(input, spgr, shapeOrGroup, types)) |
1589 | 53.5k | { |
1590 | 53.5k | switch (shapeOrGroup.type) |
1591 | 53.5k | { |
1592 | 661 | case OFFICE_ART_SPGR_CONTAINER: |
1593 | 661 | m_collector->beginGroup(); |
1594 | 661 | parseShapeGroup(input, shapeOrGroup, parentCoordinateSystem, parentGroupAbsoluteCoord); |
1595 | 661 | m_collector->endGroup(); |
1596 | 661 | break; |
1597 | 52.8k | case OFFICE_ART_SP_CONTAINER: |
1598 | 52.8k | parseEscherShape(input, shapeOrGroup, parentCoordinateSystem, parentGroupAbsoluteCoord); |
1599 | 52.8k | break; |
1600 | 53.5k | } |
1601 | 53.3k | input->seek(shapeOrGroup.contentsOffset + shapeOrGroup.contentsLength + getEscherElementTailLength(shapeOrGroup.type), librevenge::RVNG_SEEK_SET); |
1602 | 53.3k | } |
1603 | 5.09k | } |
1604 | | |
1605 | | void MSPUBParser::parseEscherShape(librevenge::RVNGInputStream *input, const EscherContainerInfo &sp, Coordinate &parentCoordinateSystem, Coordinate &parentGroupAbsoluteCoord) |
1606 | 52.8k | { |
1607 | 52.8k | Coordinate thisParentCoordinateSystem = parentCoordinateSystem; |
1608 | 52.8k | bool definesRelativeCoordinates = false; |
1609 | 52.8k | EscherContainerInfo cData; |
1610 | 52.8k | EscherContainerInfo cAnchor; |
1611 | 52.8k | EscherContainerInfo cFopt; |
1612 | 52.8k | EscherContainerInfo cTertiaryFopt; |
1613 | 52.8k | EscherContainerInfo cFsp; |
1614 | 52.8k | EscherContainerInfo cFspgr; |
1615 | 52.8k | unsigned shapeFlags = 0; |
1616 | 52.8k | bool isGroupLeader = false; |
1617 | 52.8k | ShapeType st = RECTANGLE; |
1618 | 52.8k | if (findEscherContainer(input, sp, cFspgr, OFFICE_ART_FSPGR)) |
1619 | 4.78k | { |
1620 | 4.78k | input->seek(cFspgr.contentsOffset, librevenge::RVNG_SEEK_SET); |
1621 | 4.78k | parentCoordinateSystem.m_xs = readU32(input); |
1622 | 4.78k | parentCoordinateSystem.m_ys = readU32(input); |
1623 | 4.78k | parentCoordinateSystem.m_xe = readU32(input); |
1624 | 4.78k | parentCoordinateSystem.m_ye = readU32(input); |
1625 | 4.78k | parentCoordinateSystem.arrange(); |
1626 | 4.78k | definesRelativeCoordinates = true; |
1627 | 4.78k | } |
1628 | 52.8k | input->seek(sp.contentsOffset, librevenge::RVNG_SEEK_SET); |
1629 | 52.8k | if (findEscherContainer(input, sp, cFsp, OFFICE_ART_FSP)) |
1630 | 50.7k | { |
1631 | 50.7k | st = (ShapeType)(cFsp.initial >> 4); |
1632 | 50.7k | std::map<unsigned short, unsigned> fspData = extractEscherValues(input, cFsp); |
1633 | 50.7k | input->seek(cFsp.contentsOffset + 4, librevenge::RVNG_SEEK_SET); |
1634 | 50.7k | shapeFlags = readU32(input); |
1635 | 50.7k | isGroupLeader = shapeFlags & SF_GROUP; |
1636 | 50.7k | } |
1637 | 52.8k | input->seek(sp.contentsOffset, librevenge::RVNG_SEEK_SET); |
1638 | 52.8k | if (findEscherContainer(input, sp, cData, OFFICE_ART_CLIENT_DATA)) |
1639 | 22.0k | { |
1640 | 22.0k | std::map<unsigned short, unsigned> dataValues = extractEscherValues(input, cData); |
1641 | 22.0k | unsigned *shapeSeqNum = getIfExists(dataValues, FIELDID_SHAPE_ID); |
1642 | 22.0k | if (shapeSeqNum) |
1643 | 21.7k | { |
1644 | 21.7k | m_collector->setShapeType(*shapeSeqNum, st); |
1645 | 21.7k | m_collector->setShapeFlip(*shapeSeqNum, shapeFlags & SF_FLIP_V, shapeFlags & SF_FLIP_H); |
1646 | 21.7k | input->seek(sp.contentsOffset, librevenge::RVNG_SEEK_SET); |
1647 | 21.7k | if (isGroupLeader) |
1648 | 869 | { |
1649 | 869 | m_collector->setCurrentGroupSeqNum(*shapeSeqNum); |
1650 | 869 | } |
1651 | 20.8k | else |
1652 | 20.8k | { |
1653 | 20.8k | m_collector->setShapeOrder(*shapeSeqNum); |
1654 | 20.8k | } |
1655 | 21.7k | std::set<unsigned short> anchorTypes; |
1656 | 21.7k | anchorTypes.insert(OFFICE_ART_CLIENT_ANCHOR); |
1657 | 21.7k | anchorTypes.insert(OFFICE_ART_CHILD_ANCHOR); |
1658 | 21.7k | bool foundAnchor; |
1659 | 21.7k | if ((foundAnchor = findEscherContainerWithTypeInSet(input, sp, cAnchor, anchorTypes)) || isGroupLeader) |
1660 | 21.0k | { |
1661 | 21.0k | bool rotated90 = false; |
1662 | 21.0k | MSPUB_DEBUG_MSG(("Found Escher data for %s of seqnum 0x%x\n", isGroupLeader ? "group" : "shape", *shapeSeqNum)); |
1663 | 21.0k | boost::optional<std::map<unsigned short, unsigned> > maybe_tertiaryFoptValues; |
1664 | 21.0k | input->seek(sp.contentsOffset, librevenge::RVNG_SEEK_SET); |
1665 | 21.0k | if (findEscherContainer(input, sp, cTertiaryFopt, OFFICE_ART_TERTIARY_FOPT)) |
1666 | 20.1k | { |
1667 | 20.1k | maybe_tertiaryFoptValues = extractEscherValues(input, cTertiaryFopt); |
1668 | 20.1k | } |
1669 | 21.0k | if (bool(maybe_tertiaryFoptValues)) |
1670 | 20.1k | { |
1671 | 20.1k | const std::map<unsigned short, unsigned> &tertiaryFoptValues = |
1672 | 20.1k | maybe_tertiaryFoptValues.get(); |
1673 | 20.1k | const unsigned *ptr_pictureRecolor = getIfExists_const(tertiaryFoptValues, |
1674 | 20.1k | FIELDID_PICTURE_RECOLOR); |
1675 | 20.1k | if (ptr_pictureRecolor) |
1676 | 101 | { |
1677 | 101 | m_collector->setShapePictureRecolor(*shapeSeqNum, |
1678 | 101 | ColorReference(*ptr_pictureRecolor)); |
1679 | 101 | } |
1680 | 20.1k | } |
1681 | 21.0k | input->seek(sp.contentsOffset, librevenge::RVNG_SEEK_SET); |
1682 | 21.0k | if (findEscherContainer(input, sp, cFopt, OFFICE_ART_FOPT)) |
1683 | 20.3k | { |
1684 | 20.3k | FOPTValues foptValues = extractFOPTValues(input, cFopt); |
1685 | 20.3k | unsigned *pxId = getIfExists(foptValues.m_scalarValues, FIELDID_PXID); |
1686 | 20.3k | if (pxId) |
1687 | 2.79k | { |
1688 | 2.79k | MSPUB_DEBUG_MSG(("Current Escher shape has pxId %d\n", *pxId)); |
1689 | 2.79k | if (*pxId > 0 && *pxId <= m_escherDelayIndices.size() && m_escherDelayIndices[*pxId - 1] >= 0) |
1690 | 2.34k | { |
1691 | 2.34k | m_collector->setShapeImgIndex(*shapeSeqNum, m_escherDelayIndices[*pxId - 1]); |
1692 | 2.34k | } |
1693 | 454 | else |
1694 | 454 | { |
1695 | 454 | MSPUB_DEBUG_MSG(("Couldn't find corresponding escherDelay index\n")); |
1696 | 454 | } |
1697 | 2.79k | unsigned *ptr_pictureBrightness = getIfExists(foptValues.m_scalarValues, FIELDID_PICTURE_BRIGHTNESS); |
1698 | 2.79k | if (ptr_pictureBrightness) |
1699 | 3 | { |
1700 | 3 | m_collector->setShapePictureBrightness(*shapeSeqNum, (int)(*ptr_pictureBrightness)); |
1701 | 3 | } |
1702 | 2.79k | unsigned *ptr_pictureContrast = getIfExists(foptValues.m_scalarValues, FIELDID_PICTURE_CONTRAST); |
1703 | 2.79k | if (ptr_pictureContrast) |
1704 | 17 | { |
1705 | 17 | m_collector->setShapePictureContrast(*shapeSeqNum, (int)(*ptr_pictureContrast)); |
1706 | 17 | } |
1707 | 2.79k | } |
1708 | 20.3k | unsigned *ptr_lineBackColor = |
1709 | 20.3k | getIfExists(foptValues.m_scalarValues, FIELDID_LINE_BACK_COLOR); |
1710 | 20.3k | if (ptr_lineBackColor && |
1711 | 13.1k | static_cast<int>(*ptr_lineBackColor) != -1) |
1712 | 13.1k | { |
1713 | 13.1k | m_collector->setShapeLineBackColor( |
1714 | 13.1k | *shapeSeqNum, ColorReference(*ptr_lineBackColor)); |
1715 | 13.1k | } |
1716 | 20.3k | unsigned *ptr_lineColor = getIfExists(foptValues.m_scalarValues, FIELDID_LINE_COLOR); |
1717 | 20.3k | unsigned *ptr_lineFlags = getIfExists(foptValues.m_scalarValues, FIELDID_LINE_STYLE_BOOL_PROPS); |
1718 | 20.3k | unsigned *ptr_geomFlags = getIfExists( |
1719 | 20.3k | foptValues.m_scalarValues, FIELDID_GEOM_BOOL_PROPS); |
1720 | 20.3k | bool useLine = lineExistsByFlagPointer( |
1721 | 20.3k | ptr_lineFlags, ptr_geomFlags); |
1722 | 20.3k | bool skipIfNotBg = false; |
1723 | 20.3k | std::shared_ptr<Fill> ptr_fill = getNewFill(foptValues.m_scalarValues, skipIfNotBg, foptValues.m_complexValues); |
1724 | 20.3k | unsigned lineWidth = 0; |
1725 | 20.3k | if (useLine) |
1726 | 11.9k | { |
1727 | 11.9k | if (ptr_lineColor) |
1728 | 9.33k | { |
1729 | 9.33k | unsigned *ptr_lineWidth = getIfExists(foptValues.m_scalarValues, FIELDID_LINE_WIDTH); |
1730 | 9.33k | lineWidth = ptr_lineWidth ? *ptr_lineWidth : 9525; |
1731 | 9.33k | m_collector->addShapeLine(*shapeSeqNum, Line(ColorReference(*ptr_lineColor), lineWidth, true)); |
1732 | 9.33k | } |
1733 | 2.58k | else |
1734 | 2.58k | { |
1735 | 2.58k | if (bool(maybe_tertiaryFoptValues)) |
1736 | 2.32k | { |
1737 | 2.32k | std::map<unsigned short, unsigned> &tertiaryFoptValues = |
1738 | 2.32k | maybe_tertiaryFoptValues.get(); |
1739 | 2.32k | unsigned *ptr_tertiaryLineFlags = getIfExists(tertiaryFoptValues, FIELDID_LINE_STYLE_BOOL_PROPS); |
1740 | 2.32k | if (lineExistsByFlagPointer(ptr_tertiaryLineFlags)) |
1741 | 1.03k | { |
1742 | 1.03k | unsigned *ptr_topColor = getIfExists(tertiaryFoptValues, FIELDID_LINE_TOP_COLOR); |
1743 | 1.03k | unsigned *ptr_topWidth = getIfExists(tertiaryFoptValues, FIELDID_LINE_TOP_WIDTH); |
1744 | 1.03k | unsigned *ptr_topFlags = getIfExists(tertiaryFoptValues, FIELDID_LINE_TOP_BOOL_PROPS); |
1745 | 1.03k | unsigned *ptr_rightColor = getIfExists(tertiaryFoptValues, FIELDID_LINE_RIGHT_COLOR); |
1746 | 1.03k | unsigned *ptr_rightWidth = getIfExists(tertiaryFoptValues, FIELDID_LINE_RIGHT_WIDTH); |
1747 | 1.03k | unsigned *ptr_rightFlags = getIfExists(tertiaryFoptValues, FIELDID_LINE_RIGHT_BOOL_PROPS); |
1748 | 1.03k | unsigned *ptr_bottomColor = getIfExists(tertiaryFoptValues, FIELDID_LINE_BOTTOM_COLOR); |
1749 | 1.03k | unsigned *ptr_bottomWidth = getIfExists(tertiaryFoptValues, FIELDID_LINE_BOTTOM_WIDTH); |
1750 | 1.03k | unsigned *ptr_bottomFlags = getIfExists(tertiaryFoptValues, FIELDID_LINE_BOTTOM_BOOL_PROPS); |
1751 | 1.03k | unsigned *ptr_leftColor = getIfExists(tertiaryFoptValues, FIELDID_LINE_LEFT_COLOR); |
1752 | 1.03k | unsigned *ptr_leftWidth = getIfExists(tertiaryFoptValues, FIELDID_LINE_LEFT_WIDTH); |
1753 | 1.03k | unsigned *ptr_leftFlags = getIfExists(tertiaryFoptValues, FIELDID_LINE_LEFT_BOOL_PROPS); |
1754 | | |
1755 | 1.03k | bool topExists = ptr_topColor && lineExistsByFlagPointer(ptr_topFlags); |
1756 | 1.03k | bool rightExists = ptr_rightColor && lineExistsByFlagPointer(ptr_rightFlags); |
1757 | 1.03k | bool bottomExists = ptr_bottomColor && lineExistsByFlagPointer(ptr_bottomFlags); |
1758 | 1.03k | bool leftExists = ptr_leftColor && lineExistsByFlagPointer(ptr_leftFlags); |
1759 | 1.03k | if (ptr_topWidth) |
1760 | 79 | { |
1761 | 79 | lineWidth = *ptr_topWidth; |
1762 | 79 | } |
1763 | | |
1764 | 1.03k | m_collector->addShapeLine(*shapeSeqNum, |
1765 | 1.03k | topExists ? Line(ColorReference(*ptr_topColor), ptr_topWidth ? *ptr_topWidth : 9525, true) : |
1766 | 1.03k | Line(ColorReference(0), 0, false)); |
1767 | 1.03k | m_collector->addShapeLine(*shapeSeqNum, |
1768 | 1.03k | rightExists ? Line(ColorReference(*ptr_rightColor), ptr_rightWidth ? *ptr_rightWidth : 9525, true) : |
1769 | 1.03k | Line(ColorReference(0), 0, false)); |
1770 | 1.03k | m_collector->addShapeLine(*shapeSeqNum, |
1771 | 1.03k | bottomExists ? Line(ColorReference(*ptr_bottomColor), ptr_bottomWidth ? *ptr_bottomWidth : 9525, true) : |
1772 | 1.03k | Line(ColorReference(0), 0, false)); |
1773 | 1.03k | m_collector->addShapeLine(*shapeSeqNum, |
1774 | 1.03k | leftExists ? Line(ColorReference(*ptr_leftColor), ptr_leftWidth ? *ptr_leftWidth : 9525, true) : |
1775 | 1.03k | Line(ColorReference(0), 0, false)); |
1776 | | |
1777 | | // Amazing feat of Microsoft engineering: |
1778 | | // The detailed interaction of four flags describes ONE true/false property! |
1779 | | |
1780 | 1.03k | if (ptr_leftFlags && |
1781 | 86 | (*ptr_leftFlags & FLAG_USE_LEFT_INSET_PEN) && |
1782 | 27 | (!(*ptr_leftFlags & FLAG_USE_LEFT_INSET_PEN_OK) || (*ptr_leftFlags & FLAG_LEFT_INSET_PEN_OK)) && |
1783 | 21 | (*ptr_leftFlags & FLAG_LEFT_INSET_PEN)) |
1784 | 11 | { |
1785 | 11 | m_collector->setShapeBorderPosition(*shapeSeqNum, INSIDE_SHAPE); |
1786 | 11 | } |
1787 | 1.02k | else |
1788 | 1.02k | { |
1789 | 1.02k | m_collector->setShapeBorderPosition(*shapeSeqNum, HALF_INSIDE_SHAPE); |
1790 | 1.02k | } |
1791 | 1.03k | } |
1792 | 2.32k | } |
1793 | 2.58k | } |
1794 | 11.9k | } |
1795 | 20.3k | if (ptr_fill) |
1796 | 6.18k | { |
1797 | 6.18k | m_collector->setShapeFill(*shapeSeqNum, ptr_fill, skipIfNotBg); |
1798 | 6.18k | } |
1799 | 20.3k | int *ptr_adjust1 = (int *)getIfExists(foptValues.m_scalarValues, FIELDID_ADJUST_VALUE_1); |
1800 | 20.3k | int *ptr_adjust2 = (int *)getIfExists(foptValues.m_scalarValues, FIELDID_ADJUST_VALUE_2); |
1801 | 20.3k | int *ptr_adjust3 = (int *)getIfExists(foptValues.m_scalarValues, FIELDID_ADJUST_VALUE_3); |
1802 | 20.3k | if (ptr_adjust1) |
1803 | 2.30k | { |
1804 | 2.30k | m_collector->setAdjustValue(*shapeSeqNum, 0, *ptr_adjust1); |
1805 | 2.30k | } |
1806 | 20.3k | if (ptr_adjust2) |
1807 | 591 | { |
1808 | 591 | m_collector->setAdjustValue(*shapeSeqNum, 1, *ptr_adjust2); |
1809 | 591 | } |
1810 | 20.3k | if (ptr_adjust3) |
1811 | 70 | { |
1812 | 70 | m_collector->setAdjustValue(*shapeSeqNum, 2, *ptr_adjust3); |
1813 | 70 | } |
1814 | 20.3k | int *ptr_rotation = (int *)getIfExists(foptValues.m_scalarValues, FIELDID_ROTATION); |
1815 | 20.3k | if (ptr_rotation) |
1816 | 1.26k | { |
1817 | 1.26k | double rotation = doubleModulo(toFixedPoint(*ptr_rotation), 360); |
1818 | 1.26k | m_collector->setShapeRotation(*shapeSeqNum, short(rotation)); |
1819 | | //FIXME : make MSPUBCollector handle double shape rotations |
1820 | 1.26k | rotated90 = (rotation >= 45 && rotation < 135) || (rotation >= 225 && rotation < 315); |
1821 | | |
1822 | 1.26k | } |
1823 | 20.3k | unsigned *ptr_left = getIfExists(foptValues.m_scalarValues, FIELDID_DY_TEXT_LEFT); |
1824 | 20.3k | unsigned *ptr_top = getIfExists(foptValues.m_scalarValues, FIELDID_DY_TEXT_TOP); |
1825 | 20.3k | unsigned *ptr_right = getIfExists(foptValues.m_scalarValues, FIELDID_DY_TEXT_RIGHT); |
1826 | 20.3k | unsigned *ptr_bottom = getIfExists(foptValues.m_scalarValues, FIELDID_DY_TEXT_BOTTOM); |
1827 | 20.3k | m_collector->setShapeMargins(*shapeSeqNum, ptr_left ? *ptr_left : DEFAULT_MARGIN, |
1828 | 20.3k | ptr_top ? *ptr_top : DEFAULT_MARGIN, |
1829 | 20.3k | ptr_right ? *ptr_right : DEFAULT_MARGIN, |
1830 | 20.3k | ptr_bottom ? *ptr_bottom : DEFAULT_MARGIN); |
1831 | 20.3k | unsigned *ptr_lineDashing = getIfExists(foptValues.m_scalarValues, FIELDID_LINE_DASHING); |
1832 | 20.3k | unsigned *ptr_lineEndcapStyle = getIfExists(foptValues.m_scalarValues, FIELDID_LINE_ENDCAP_STYLE); |
1833 | 20.3k | DotStyle dotStyle = RECT_DOT; |
1834 | 20.3k | if (ptr_lineEndcapStyle) |
1835 | 115 | { |
1836 | 115 | switch (*ptr_lineEndcapStyle) |
1837 | 115 | { |
1838 | 3 | case 0: |
1839 | 3 | dotStyle = ROUND_DOT; |
1840 | 3 | break; |
1841 | 112 | default: |
1842 | 112 | break; |
1843 | 115 | } |
1844 | 115 | } |
1845 | 20.3k | if (ptr_lineDashing) |
1846 | 312 | { |
1847 | 312 | m_collector->setShapeDash(*shapeSeqNum, getDash( |
1848 | 312 | static_cast<MSPUBDashStyle>(*ptr_lineDashing), lineWidth, |
1849 | 312 | dotStyle)); |
1850 | 312 | } |
1851 | | |
1852 | 20.3k | if (bool(maybe_tertiaryFoptValues)) |
1853 | 19.5k | { |
1854 | 19.5k | std::map<unsigned short, unsigned> &tertiaryFoptValues = maybe_tertiaryFoptValues.get(); |
1855 | 19.5k | unsigned *ptr_numColumns = getIfExists(tertiaryFoptValues, FIELDID_NUM_COLUMNS); |
1856 | 19.5k | if (ptr_numColumns) |
1857 | 12 | { |
1858 | 12 | m_collector->setShapeNumColumns(*shapeSeqNum, *ptr_numColumns); |
1859 | 12 | } |
1860 | 19.5k | unsigned *ptr_columnSpacing = getIfExists(tertiaryFoptValues, FIELDID_COLUMN_SPACING); |
1861 | 19.5k | if (ptr_columnSpacing) |
1862 | 2.63k | { |
1863 | 2.63k | m_collector->setShapeColumnSpacing(*shapeSeqNum, *ptr_columnSpacing); |
1864 | 2.63k | } |
1865 | 19.5k | } |
1866 | 20.3k | unsigned *ptr_beginArrowStyle = getIfExists(foptValues.m_scalarValues, |
1867 | 20.3k | FIELDID_BEGIN_ARROW_STYLE); |
1868 | 20.3k | unsigned *ptr_beginArrowWidth = getIfExists(foptValues.m_scalarValues, |
1869 | 20.3k | FIELDID_BEGIN_ARROW_WIDTH); |
1870 | 20.3k | unsigned *ptr_beginArrowHeight = getIfExists(foptValues.m_scalarValues, |
1871 | 20.3k | FIELDID_BEGIN_ARROW_HEIGHT); |
1872 | 20.3k | m_collector->setShapeBeginArrow(*shapeSeqNum, Arrow( |
1873 | 20.3k | ptr_beginArrowStyle ? (ArrowStyle)(*ptr_beginArrowStyle) : |
1874 | 20.3k | NO_ARROW, |
1875 | 20.3k | ptr_beginArrowWidth ? (ArrowSize)(*ptr_beginArrowWidth) : |
1876 | 20.3k | MEDIUM, |
1877 | 20.3k | ptr_beginArrowHeight ? (ArrowSize)(*ptr_beginArrowHeight) : |
1878 | 20.3k | MEDIUM)); |
1879 | 20.3k | unsigned *ptr_endArrowStyle = getIfExists(foptValues.m_scalarValues, |
1880 | 20.3k | FIELDID_END_ARROW_STYLE); |
1881 | 20.3k | unsigned *ptr_endArrowWidth = getIfExists(foptValues.m_scalarValues, |
1882 | 20.3k | FIELDID_END_ARROW_WIDTH); |
1883 | 20.3k | unsigned *ptr_endArrowHeight = getIfExists(foptValues.m_scalarValues, |
1884 | 20.3k | FIELDID_END_ARROW_HEIGHT); |
1885 | 20.3k | m_collector->setShapeEndArrow(*shapeSeqNum, Arrow( |
1886 | 20.3k | ptr_endArrowStyle ? (ArrowStyle)(*ptr_endArrowStyle) : |
1887 | 20.3k | NO_ARROW, |
1888 | 20.3k | ptr_endArrowWidth ? (ArrowSize)(*ptr_endArrowWidth) : |
1889 | 20.3k | MEDIUM, |
1890 | 20.3k | ptr_endArrowHeight ? (ArrowSize)(*ptr_endArrowHeight) : |
1891 | 20.3k | MEDIUM)); |
1892 | | |
1893 | 20.3k | unsigned *shadowBoolProps = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_BOOL_PROPS); |
1894 | 20.3k | if (shadowBoolProps) |
1895 | 5.25k | { |
1896 | 5.25k | unsigned shadowProps = *shadowBoolProps; |
1897 | 5.25k | if ((shadowProps & FLAG_USE_FSHADOW) && (shadowProps & FLAG_USE_SHADOW)) |
1898 | 1.88k | { |
1899 | 1.88k | unsigned *ptr_shadowType = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_TYPE); |
1900 | 1.88k | auto shadowType = static_cast<ShadowType>(ptr_shadowType ? *ptr_shadowType : 0); |
1901 | 1.88k | unsigned *shadowColor = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_COLOR); |
1902 | 1.88k | unsigned *shadowHColor = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_HIGHLIGHT); |
1903 | 1.88k | unsigned *shadowOpacity = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_OPACITY); |
1904 | 1.88k | unsigned *shadowOffsetX = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_OFFSET_X); |
1905 | 1.88k | unsigned *shadowOffsetY = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_OFFSET_Y); |
1906 | 1.88k | unsigned *shadowOffsetX2 = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_SECOND_OFFSET_X); |
1907 | 1.88k | unsigned *shadowOffsetY2 = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_SECOND_OFFSET_Y); |
1908 | 1.88k | unsigned *shadowOriginX = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_ORIGIN_X); |
1909 | 1.88k | unsigned *shadowOriginY = getIfExists(foptValues.m_scalarValues, FIELDID_SHADOW_ORIGIN_Y); |
1910 | 1.88k | m_collector->setShapeShadow(*shapeSeqNum, Shadow(shadowType, |
1911 | 1.88k | shadowOffsetX ? static_cast<int>(*shadowOffsetX) : 0x6338, |
1912 | 1.88k | shadowOffsetY ? static_cast<int>(*shadowOffsetY) : 0x6338, |
1913 | 1.88k | shadowOffsetX2 ? static_cast<int>(*shadowOffsetX2) : 0, |
1914 | 1.88k | shadowOffsetY2 ? static_cast<int>(*shadowOffsetY2) : 0, |
1915 | 1.88k | shadowOriginX ? toFixedPoint(static_cast<int>(*shadowOriginX)) : 0, |
1916 | 1.88k | shadowOriginY ? toFixedPoint(static_cast<int>(*shadowOriginY)) : 0, |
1917 | 1.88k | toFixedPoint(shadowOpacity ? static_cast<int>(*shadowOpacity) : 0x10000), |
1918 | 1.88k | ColorReference(shadowColor ? *shadowColor : 0x00808080), |
1919 | 1.88k | ColorReference(shadowHColor ? *shadowHColor : 0x00CBCBCB) |
1920 | 1.88k | )); |
1921 | | |
1922 | 1.88k | } |
1923 | 5.25k | } |
1924 | | |
1925 | 20.3k | const std::vector<unsigned char> vertexData = foptValues.m_complexValues[FIELDID_P_VERTICES]; |
1926 | 20.3k | if (!vertexData.empty()) |
1927 | 654 | { |
1928 | 654 | unsigned *p_geoRight = getIfExists(foptValues.m_scalarValues, |
1929 | 654 | FIELDID_GEO_RIGHT); |
1930 | 654 | unsigned *p_geoBottom = getIfExists(foptValues.m_scalarValues, |
1931 | 654 | FIELDID_GEO_BOTTOM); |
1932 | 654 | const std::vector<unsigned char> segmentData = foptValues.m_complexValues[FIELDID_P_SEGMENTS]; |
1933 | 654 | const std::vector<unsigned char> guideData = foptValues.m_complexValues[FIELDID_P_GUIDES]; |
1934 | 654 | m_collector->setShapeCustomPath(*shapeSeqNum, getDynamicCustomShape(vertexData, segmentData, |
1935 | 654 | guideData, p_geoRight ? *p_geoRight : 21600, |
1936 | 654 | p_geoBottom ? *p_geoBottom : 21600)); |
1937 | 654 | } |
1938 | 20.3k | const std::vector<unsigned char> wrapVertexData = foptValues.m_complexValues[FIELDID_P_WRAPPOLYGONVERTICES]; |
1939 | 20.3k | if (!wrapVertexData.empty()) |
1940 | 136 | { |
1941 | 136 | std::vector<Vertex> ret = parseVertices(wrapVertexData); |
1942 | 136 | m_collector->setShapeClipPath(*shapeSeqNum, ret); |
1943 | 136 | } |
1944 | 20.3k | } |
1945 | 21.0k | if (foundAnchor) |
1946 | 20.9k | { |
1947 | 20.9k | Coordinate absolute; |
1948 | 20.9k | if (cAnchor.type == OFFICE_ART_CLIENT_ANCHOR) |
1949 | 19.9k | { |
1950 | 19.9k | std::map<unsigned short, unsigned> anchorData = extractEscherValues(input, cAnchor); |
1951 | 19.9k | absolute = Coordinate(anchorData[FIELDID_XS], |
1952 | 19.9k | anchorData[FIELDID_YS], anchorData[FIELDID_XE], |
1953 | 19.9k | anchorData[FIELDID_YE]); |
1954 | 19.9k | } |
1955 | 945 | else if (cAnchor.type == OFFICE_ART_CHILD_ANCHOR) |
1956 | 945 | { |
1957 | 945 | input->seek(cAnchor.contentsOffset, librevenge::RVNG_SEEK_SET); |
1958 | 945 | int coordSystemWidth = int64_t(thisParentCoordinateSystem.m_xe) - thisParentCoordinateSystem.m_xs; |
1959 | 945 | if (coordSystemWidth == 0) |
1960 | 218 | coordSystemWidth = 1; |
1961 | 945 | int coordSystemHeight = thisParentCoordinateSystem.m_ye - thisParentCoordinateSystem.m_ys; |
1962 | 945 | if (coordSystemHeight == 0) |
1963 | 107 | coordSystemHeight = 1; |
1964 | 945 | int groupWidth = int64_t(parentGroupAbsoluteCoord.m_xe) - parentGroupAbsoluteCoord.m_xs; |
1965 | 945 | int groupHeight = int64_t(parentGroupAbsoluteCoord.m_ye) - parentGroupAbsoluteCoord.m_ys; |
1966 | 945 | double widthScale = (double)groupWidth / coordSystemWidth; |
1967 | 945 | double heightScale = (double)groupHeight / coordSystemHeight; |
1968 | 945 | int xs = (readU32(input) - thisParentCoordinateSystem.m_xs) * widthScale + parentGroupAbsoluteCoord.m_xs; |
1969 | 945 | int ys = (readU32(input) - thisParentCoordinateSystem.m_ys) * heightScale + parentGroupAbsoluteCoord.m_ys; |
1970 | 945 | int xe = (readU32(input) - thisParentCoordinateSystem.m_xs) * widthScale + parentGroupAbsoluteCoord.m_xs; |
1971 | 945 | int ye = (readU32(input) - thisParentCoordinateSystem.m_ys) * heightScale + parentGroupAbsoluteCoord.m_ys; |
1972 | | |
1973 | 945 | absolute = Coordinate(xs, ys, xe, ye); |
1974 | 945 | } |
1975 | 20.9k | if (rotated90) |
1976 | 859 | { |
1977 | 859 | int initialWidth = int64_t(absolute.m_xe) - absolute.m_xs; |
1978 | 859 | int initialHeight = int64_t(absolute.m_ye) - absolute.m_ys; |
1979 | 859 | int centerX = int64_t(absolute.m_xs) + initialWidth / 2; |
1980 | 859 | int centerY = int64_t(absolute.m_ys) + initialHeight / 2; |
1981 | 859 | int xs = centerX - initialHeight / 2; |
1982 | 859 | int ys = centerY - initialWidth / 2; |
1983 | 859 | int xe = xs + initialHeight; |
1984 | 859 | int ye = ys + initialWidth; |
1985 | 859 | absolute = Coordinate(xs, ys, xe, ye); |
1986 | 859 | } |
1987 | 20.9k | m_collector->setShapeCoordinatesInEmu(*shapeSeqNum, |
1988 | 20.9k | absolute.m_xs, |
1989 | 20.9k | absolute.m_ys, |
1990 | 20.9k | absolute.m_xe, |
1991 | 20.9k | absolute.m_ye); |
1992 | 20.9k | if (definesRelativeCoordinates) |
1993 | 480 | { |
1994 | 480 | parentGroupAbsoluteCoord = absolute; |
1995 | 480 | } |
1996 | 20.9k | } |
1997 | 21.0k | } |
1998 | 21.7k | } |
1999 | 22.0k | } |
2000 | 52.8k | } |
2001 | | |
2002 | | std::shared_ptr<Fill> MSPUBParser::getNewFill(const std::map<unsigned short, unsigned> &foptProperties, |
2003 | | bool &skipIfNotBg, std::map<unsigned short, std::vector<unsigned char> > &foptValues) |
2004 | 20.3k | { |
2005 | 20.3k | const FillType *ptr_fillType = (FillType *)getIfExists_const(foptProperties, FIELDID_FILL_TYPE); |
2006 | 20.3k | FillType fillType = ptr_fillType ? *ptr_fillType : SOLID; |
2007 | 20.3k | switch (fillType) |
2008 | 20.3k | { |
2009 | 18.6k | case SOLID: |
2010 | 18.6k | { |
2011 | 18.6k | const unsigned *ptr_fillColor = getIfExists_const(foptProperties, FIELDID_FILL_COLOR); |
2012 | 18.6k | const unsigned *ptr_fieldStyleProps = getIfExists_const(foptProperties, FIELDID_FIELD_STYLE_BOOL_PROPS); |
2013 | 18.6k | skipIfNotBg = ptr_fieldStyleProps && (*ptr_fieldStyleProps & 0xF0) == 0; |
2014 | 18.6k | if (ptr_fillColor && !skipIfNotBg) |
2015 | 4.83k | { |
2016 | 4.83k | const unsigned *ptr_fillOpacity = getIfExists_const(foptProperties, FIELDID_FILL_OPACITY); |
2017 | 4.83k | return std::shared_ptr<Fill>(new SolidFill(ColorReference(*ptr_fillColor), ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1, m_collector)); |
2018 | 4.83k | } |
2019 | 13.8k | return std::shared_ptr<Fill>(); |
2020 | 18.6k | } |
2021 | 11 | case SHADE_SHAPE: |
2022 | 241 | case SHADE_CENTER: |
2023 | 242 | case SHADE: |
2024 | 1.10k | case SHADE_SCALE: |
2025 | 1.10k | { |
2026 | 1.10k | int angle; |
2027 | 1.10k | const int *ptr_angle = (const int *)getIfExists_const(foptProperties, FIELDID_FILL_ANGLE); |
2028 | 1.10k | const unsigned *ptr_fillColor = getIfExists_const(foptProperties, FIELDID_FILL_COLOR); |
2029 | 1.10k | const unsigned *ptr_fillBackColor = getIfExists_const(foptProperties, FIELDID_FILL_BACK_COLOR); |
2030 | 1.10k | unsigned fill = ptr_fillColor ? *ptr_fillColor : 0x00FFFFFFF; |
2031 | 1.10k | unsigned fillBack = ptr_fillBackColor ? *ptr_fillBackColor : 0x00FFFFFF; |
2032 | 1.10k | ColorReference firstColor(fill, fill); |
2033 | 1.10k | ColorReference secondColor(fill, fillBack); |
2034 | 1.10k | const unsigned *ptr_fillOpacity = getIfExists_const(foptProperties, FIELDID_FILL_OPACITY); |
2035 | 1.10k | const unsigned *ptr_fillBackOpacity = getIfExists_const(foptProperties, FIELDID_FILL_BACK_OPACITY); |
2036 | 1.10k | const unsigned *ptr_fillFocus = getIfExists_const(foptProperties, FIELDID_FILL_FOCUS); |
2037 | 1.10k | short fillFocus = ptr_fillFocus ? int((*ptr_fillFocus << 16) >> 16) : 0; |
2038 | 1.10k | angle = ptr_angle ? *ptr_angle : 0; |
2039 | 1.10k | angle >>= 16; //it's actually only 16 bits |
2040 | | // Don't try to figure out what sense the following switch statement makes. |
2041 | | // The angles are just offset by 90 degrees in the file format in some cases. |
2042 | | // It seems totally arbitrary -- maybe an MS bug ? |
2043 | 1.10k | switch (angle) |
2044 | 1.10k | { |
2045 | 207 | case -135: |
2046 | 207 | angle = -45; |
2047 | 207 | break; |
2048 | 0 | case -45: |
2049 | 0 | angle = 225; |
2050 | 0 | break; |
2051 | 893 | default: |
2052 | 893 | break; |
2053 | 1.10k | } |
2054 | 1.10k | double fillLeftVal = 0.0; |
2055 | 1.10k | const unsigned *ptr_fillLeft = getIfExists_const(foptProperties, FIELDID_FILL_TO_LEFT); |
2056 | 1.10k | if (ptr_fillLeft) |
2057 | 107 | fillLeftVal = toFixedPoint(*ptr_fillLeft); |
2058 | 1.10k | double fillTopVal = 0.0; |
2059 | 1.10k | const unsigned *ptr_fillTop = getIfExists_const(foptProperties, FIELDID_FILL_TO_TOP); |
2060 | 1.10k | if (ptr_fillTop) |
2061 | 172 | fillTopVal = toFixedPoint(*ptr_fillTop); |
2062 | 1.10k | double fillRightVal = 0.0; |
2063 | 1.10k | const unsigned *ptr_fillRight = getIfExists_const(foptProperties, FIELDID_FILL_TO_RIGHT); |
2064 | 1.10k | if (ptr_fillRight) |
2065 | 121 | fillRightVal = toFixedPoint(*ptr_fillRight); |
2066 | 1.10k | double fillBottomVal = 0.0; |
2067 | 1.10k | const unsigned *ptr_fillBottom = getIfExists_const(foptProperties, FIELDID_FILL_TO_BOTTOM); |
2068 | 1.10k | if (ptr_fillBottom) |
2069 | 122 | fillBottomVal = toFixedPoint(*ptr_fillBottom); |
2070 | | |
2071 | 1.10k | std::shared_ptr<GradientFill> ret(new GradientFill(m_collector, angle, (int)fillType)); |
2072 | 1.10k | ret->setFillCenter(fillLeftVal, fillTopVal, fillRightVal, fillBottomVal); |
2073 | | |
2074 | 1.10k | const unsigned *ptr_fillGrad = getIfExists_const(foptProperties, FIELDID_FILL_SHADE_COMPLEX); |
2075 | 1.10k | if (ptr_fillGrad) |
2076 | 146 | { |
2077 | 146 | const std::vector<unsigned char> gradientData = foptValues[FIELDID_FILL_SHADE_COMPLEX]; |
2078 | 146 | if (gradientData.size() > 6) |
2079 | 39 | { |
2080 | 39 | unsigned short numEntries = gradientData[0] | (gradientData[1] << 8); |
2081 | 39 | unsigned offs = 6; |
2082 | 12.6k | for (unsigned i = 0; i < numEntries; ++i) |
2083 | 12.6k | { |
2084 | 12.6k | unsigned color = gradientData[offs] | (unsigned(gradientData[offs + 1]) << 8) | (unsigned(gradientData[offs + 2]) << 16) | (unsigned(gradientData[offs + 3]) << 24); |
2085 | 12.6k | offs += 4; |
2086 | 12.6k | auto posi = (int)(toFixedPoint(gradientData[offs] | (unsigned(gradientData[offs + 1]) << 8) | (unsigned(gradientData[offs + 2]) << 16) | (unsigned(gradientData[offs + 3]) << 24)) * 100); |
2087 | 12.6k | offs += 4; |
2088 | 12.6k | ColorReference sColor(color, color); |
2089 | 12.6k | if (fillFocus == 0) |
2090 | 980 | ret->addColor(sColor, posi, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2091 | 11.6k | else if (fillFocus == 100) |
2092 | 772 | ret->addColorReverse(sColor, 100 - posi, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2093 | 10.9k | else if (fillFocus > 0) |
2094 | 3.94k | ret->addColor(sColor, posi / 2, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2095 | 6.96k | else if (fillFocus < 0) |
2096 | 6.96k | ret->addColorReverse(sColor, (100 - posi) / 2, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2097 | 12.6k | } |
2098 | 39 | if ((fillFocus < 0) || ((fillFocus > 0) && (fillFocus < 100))) |
2099 | 27 | ret->completeComplexFill(); |
2100 | 39 | } |
2101 | 146 | } |
2102 | 954 | else |
2103 | 954 | { |
2104 | 954 | if (fillFocus == 0) |
2105 | 287 | { |
2106 | 287 | ret->addColor(firstColor, 0, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2107 | 287 | ret->addColor(secondColor, 100, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1); |
2108 | 287 | } |
2109 | 667 | else if (fillFocus == 100) |
2110 | 486 | { |
2111 | 486 | ret->addColor(secondColor, 0, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1); |
2112 | 486 | ret->addColor(firstColor, 100, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2113 | 486 | } |
2114 | 181 | else if (fillFocus > 0) |
2115 | 71 | { |
2116 | 71 | ret->addColor(secondColor, 0, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1); |
2117 | 71 | ret->addColor(firstColor, fillFocus, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2118 | 71 | ret->addColor(secondColor, 100, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1); |
2119 | | |
2120 | | // ret->addColor(firstColor, 0, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2121 | | // ret->addColor(secondColor, fillFocus, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1); |
2122 | | // ret->addColor(firstColor, 100, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2123 | 71 | } |
2124 | 110 | else if (fillFocus < 0) |
2125 | 110 | { |
2126 | 110 | ret->addColor(firstColor, 0, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2127 | 110 | ret->addColor(secondColor, 100 + fillFocus, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1); |
2128 | 110 | ret->addColor(firstColor, 100, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2129 | | |
2130 | | // ret->addColor(secondColor, 0, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1); |
2131 | | // ret->addColor(firstColor, 100 + fillFocus, ptr_fillOpacity ? (double)(*ptr_fillOpacity) / 0xFFFF : 1); |
2132 | | // ret->addColor(secondColor, 100, ptr_fillBackOpacity ? (double)(*ptr_fillBackOpacity) / 0xFFFF : 1); |
2133 | 110 | } |
2134 | 954 | } |
2135 | 1.10k | return ret; |
2136 | 1.10k | } |
2137 | 245 | case TEXTURE: |
2138 | 247 | case BITMAP: |
2139 | 247 | { |
2140 | | // in the case the shape is rotated we must rotate the image too |
2141 | 247 | int rotation = 0; |
2142 | 247 | const int *ptr_rotation = (const int *)getIfExists_const(foptProperties, FIELDID_ROTATION); |
2143 | 247 | if (ptr_rotation) |
2144 | 74 | rotation = (int)doubleModulo(toFixedPoint(*ptr_rotation), 360); |
2145 | 247 | const unsigned *ptr_bgPxId = getIfExists_const(foptProperties, FIELDID_BG_PXID); |
2146 | 247 | if (ptr_bgPxId && *ptr_bgPxId > 0 && *ptr_bgPxId <= m_escherDelayIndices.size() && m_escherDelayIndices[*ptr_bgPxId - 1] >= 0) |
2147 | 177 | { |
2148 | 177 | return std::shared_ptr<Fill>(new ImgFill(m_escherDelayIndices[*ptr_bgPxId - 1], m_collector, fillType == TEXTURE, rotation)); |
2149 | 177 | } |
2150 | 70 | return std::shared_ptr<Fill>(); |
2151 | 247 | } |
2152 | 175 | case PATTERN: |
2153 | 175 | { |
2154 | 175 | const unsigned *ptr_bgPxId = getIfExists_const(foptProperties, FIELDID_BG_PXID); |
2155 | 175 | const unsigned *ptr_fillColor = getIfExists_const(foptProperties, FIELDID_FILL_COLOR); |
2156 | 175 | const unsigned *ptr_fillBackColor = getIfExists_const(foptProperties, FIELDID_FILL_BACK_COLOR); |
2157 | 175 | ColorReference fill = ptr_fillColor ? ColorReference(*ptr_fillColor) : ColorReference(0x00FFFFFF); |
2158 | | // ColorReference back = ptr_fillBackColor ? ColorReference(*ptr_fillBackColor) : ColorReference(0x08000000); |
2159 | 175 | ColorReference back = ptr_fillBackColor ? ColorReference(*ptr_fillBackColor) : ColorReference(0x00FFFFFF); |
2160 | 175 | if (ptr_bgPxId && *ptr_bgPxId > 0 && *ptr_bgPxId <= m_escherDelayIndices.size() && m_escherDelayIndices[*ptr_bgPxId - 1] >= 0) |
2161 | 74 | { |
2162 | 74 | return std::shared_ptr<Fill>(new PatternFill(m_escherDelayIndices[*ptr_bgPxId - 1], m_collector, fill, back)); |
2163 | 74 | } |
2164 | 101 | return std::shared_ptr<Fill>(); |
2165 | 175 | } |
2166 | 0 | case BACKGROUND: |
2167 | 166 | default: |
2168 | 166 | return std::shared_ptr<Fill>(); |
2169 | 20.3k | } |
2170 | 20.3k | } |
2171 | | |
2172 | | DynamicCustomShape MSPUBParser::getDynamicCustomShape( |
2173 | | const std::vector<unsigned char> &vertexData, const std::vector<unsigned char> &segmentData, |
2174 | | const std::vector<unsigned char> &guideData, unsigned geoWidth, |
2175 | | unsigned geoHeight) |
2176 | 654 | { |
2177 | 654 | DynamicCustomShape ret(geoWidth, geoHeight); |
2178 | 654 | ret.m_vertices = parseVertices(vertexData); |
2179 | 654 | ret.m_elements = parseSegments(segmentData); |
2180 | 654 | ret.m_calculations = parseGuides(guideData); |
2181 | 654 | return ret; |
2182 | 654 | } |
2183 | | |
2184 | | std::vector<unsigned short> MSPUBParser::parseSegments( |
2185 | | const std::vector<unsigned char> &segmentData) |
2186 | 654 | { |
2187 | 654 | std::vector<unsigned short> ret; |
2188 | 654 | if (segmentData.size() < 6) |
2189 | 105 | { |
2190 | 105 | return ret; |
2191 | 105 | } |
2192 | | // assume that the entry size is 2. |
2193 | 549 | unsigned short numEntries = segmentData[0] | (segmentData[1] << 8); |
2194 | 549 | unsigned offset = 6; |
2195 | 208k | for (unsigned i = 0; i < numEntries; ++i) |
2196 | 208k | { |
2197 | 208k | if (offset + 2 > segmentData.size()) |
2198 | 18 | { |
2199 | 18 | break; |
2200 | 18 | } |
2201 | 208k | ret.push_back(segmentData[offset] | (segmentData[offset + 1] << 8)); |
2202 | 208k | offset += 2; |
2203 | 208k | } |
2204 | 549 | return ret; |
2205 | 654 | } |
2206 | | |
2207 | | std::vector<Calculation> MSPUBParser::parseGuides( |
2208 | | const std::vector<unsigned char> &/* guideData */) |
2209 | 654 | { |
2210 | 654 | std::vector<Calculation> ret; |
2211 | | |
2212 | | //FIXME : implement this function. |
2213 | | |
2214 | 654 | return ret; |
2215 | 654 | } |
2216 | | |
2217 | | std::vector<Vertex> MSPUBParser::parseVertices( |
2218 | | const std::vector<unsigned char> &vertexData) |
2219 | 790 | { |
2220 | 790 | std::vector<Vertex> ret; |
2221 | 790 | if (vertexData.size() < 6) |
2222 | 0 | { |
2223 | 0 | return ret; |
2224 | 0 | } |
2225 | 790 | unsigned short numVertices = vertexData[0] | (vertexData[1] << 8); |
2226 | 790 | unsigned short entrySize = vertexData[4] | (vertexData[5] << 8); |
2227 | 790 | if (entrySize == 0xFFF0) |
2228 | 27 | { |
2229 | 27 | entrySize = 4; |
2230 | 27 | } |
2231 | 790 | if (!(entrySize == 2 || entrySize == 4 || entrySize == 8)) |
2232 | 56 | { |
2233 | 56 | MSPUB_DEBUG_MSG(("Incomprehensible entry size %u in vertex complex data!\n", entrySize)); |
2234 | 56 | return ret; |
2235 | 56 | } |
2236 | 734 | unsigned offset = 6; |
2237 | 734 | ret.reserve(numVertices); |
2238 | 16.8k | for (unsigned i = 0; i < numVertices; ++i) |
2239 | 16.1k | { |
2240 | 16.1k | if (offset + entrySize > vertexData.size()) |
2241 | 0 | { |
2242 | 0 | break; |
2243 | 0 | } |
2244 | 16.1k | int x, y; |
2245 | 16.1k | switch (entrySize) |
2246 | 16.1k | { |
2247 | 2.55k | case 2: |
2248 | 2.55k | x = vertexData[offset]; |
2249 | 2.55k | y = vertexData[offset + 1]; |
2250 | 2.55k | break; |
2251 | 1.65k | case 4: |
2252 | 1.65k | x = vertexData[offset] | (unsigned(vertexData[offset + 1]) << 8); |
2253 | 1.65k | y = vertexData[offset + 2] | (unsigned(vertexData[offset + 3]) << 8); |
2254 | 1.65k | break; |
2255 | 11.8k | case 8: |
2256 | 11.8k | x = vertexData[offset] | (unsigned(vertexData[offset + 1]) << 8) | |
2257 | 11.8k | (unsigned(vertexData[offset + 2]) << 16) | (unsigned(vertexData[offset + 3]) << 24); |
2258 | 11.8k | y = vertexData[offset + 4] | (unsigned(vertexData[offset + 5]) << 8) | |
2259 | 11.8k | (unsigned(vertexData[offset + 6]) << 16) | (unsigned(vertexData[offset + 7]) << 24); |
2260 | 11.8k | break; |
2261 | 0 | default: // logically shouldn't be able to get here. |
2262 | 0 | x = 0; |
2263 | 0 | y = 0; |
2264 | 0 | break; |
2265 | 16.1k | } |
2266 | 16.1k | Vertex v = {x, y}; |
2267 | 16.1k | ret.push_back(v); |
2268 | 16.1k | offset += entrySize; |
2269 | 16.1k | } |
2270 | 734 | return ret; |
2271 | 734 | } |
2272 | | |
2273 | | unsigned MSPUBParser::getEscherElementTailLength(unsigned short type) |
2274 | 815k | { |
2275 | 815k | switch (type) |
2276 | 815k | { |
2277 | 1.94k | case OFFICE_ART_DGG_CONTAINER: |
2278 | 6.43k | case OFFICE_ART_DG_CONTAINER: |
2279 | 6.43k | return 4; |
2280 | 808k | default: |
2281 | 808k | return 0; |
2282 | 815k | } |
2283 | 815k | } |
2284 | | |
2285 | | unsigned MSPUBParser::getEscherElementAdditionalHeaderLength(unsigned short type) |
2286 | 112k | { |
2287 | 112k | switch (type) |
2288 | 112k | { |
2289 | 19.9k | case OFFICE_ART_CLIENT_ANCHOR: |
2290 | 42.0k | case OFFICE_ART_CLIENT_DATA: //account for the fact that the length appears twice, for whatever reason |
2291 | 42.0k | return 4; |
2292 | 112k | } |
2293 | 70.8k | return 0; |
2294 | 112k | } |
2295 | | |
2296 | | bool MSPUBParser::findEscherContainerWithTypeInSet(librevenge::RVNGInputStream *input, const EscherContainerInfo &parent, EscherContainerInfo &out, std::set<unsigned short> types) |
2297 | 80.1k | { |
2298 | 155k | while (stillReading(input, parent.contentsOffset + parent.contentsLength)) |
2299 | 149k | { |
2300 | 149k | EscherContainerInfo next = parseEscherContainer(input); |
2301 | 149k | if (types.find(next.type) != types.end()) |
2302 | 74.4k | { |
2303 | 74.4k | out = next; |
2304 | 74.4k | return true; |
2305 | 74.4k | } |
2306 | 75.3k | input->seek(next.contentsOffset + next.contentsLength + getEscherElementTailLength(next.type), librevenge::RVNG_SEEK_SET); |
2307 | 75.3k | } |
2308 | 5.75k | return false; |
2309 | 80.1k | } |
2310 | | |
2311 | | bool MSPUBParser::findEscherContainer(librevenge::RVNGInputStream *input, const EscherContainerInfo &parent, EscherContainerInfo &out, unsigned short desiredType) |
2312 | 219k | { |
2313 | 219k | MSPUB_DEBUG_MSG(("At offset 0x%lx, attempting to find escher container of type 0x%x\n", input->tell(), desiredType)); |
2314 | 899k | while (stillReading(input, parent.contentsOffset + parent.contentsLength)) |
2315 | 810k | { |
2316 | 810k | EscherContainerInfo next = parseEscherContainer(input); |
2317 | 810k | if (next.type == desiredType) |
2318 | 129k | { |
2319 | 129k | out = next; |
2320 | 129k | return true; |
2321 | 129k | } |
2322 | 680k | input->seek(next.contentsOffset + next.contentsLength + getEscherElementTailLength(next.type), librevenge::RVNG_SEEK_SET); |
2323 | 680k | } |
2324 | 89.3k | return false; |
2325 | 219k | } |
2326 | | |
2327 | | FOPTValues MSPUBParser::extractFOPTValues(librevenge::RVNGInputStream *input, const EscherContainerInfo &record) |
2328 | 20.3k | { |
2329 | 20.3k | FOPTValues ret; |
2330 | 20.3k | input->seek(record.contentsOffset, librevenge::RVNG_SEEK_SET); |
2331 | 20.3k | unsigned short numValues = record.initial >> 4; |
2332 | 20.3k | std::vector<unsigned short> complexIds; |
2333 | 392k | for (unsigned short i = 0; i < numValues; ++i) |
2334 | 372k | { |
2335 | 372k | if (!stillReading(input, record.contentsOffset + record.contentsLength)) |
2336 | 296 | { |
2337 | 296 | break; |
2338 | 296 | } |
2339 | 372k | unsigned short id = readU16(input); |
2340 | 372k | unsigned value = readU32(input); |
2341 | 372k | ret.m_scalarValues[id] = value; |
2342 | 372k | bool complex = id & 0x8000; |
2343 | 372k | if (complex) |
2344 | 18.7k | { |
2345 | 18.7k | complexIds.push_back(id); |
2346 | 18.7k | } |
2347 | 372k | } |
2348 | 20.3k | for (unsigned short id : complexIds) |
2349 | 14.6k | { |
2350 | 14.6k | if (!stillReading(input, record.contentsOffset + record.contentsLength)) |
2351 | 5.07k | { |
2352 | 5.07k | break; |
2353 | 5.07k | } |
2354 | 9.60k | unsigned length = ret.m_scalarValues[id]; |
2355 | 9.60k | if (!length) |
2356 | 223 | { |
2357 | 223 | continue; |
2358 | 223 | } |
2359 | 9.37k | unsigned short numEntries = readU16(input); |
2360 | 9.37k | input->seek(2, librevenge::RVNG_SEEK_CUR); |
2361 | 9.37k | unsigned short entryLength = readU16(input); |
2362 | 9.37k | if (entryLength == 0xFFF0) |
2363 | 28 | { |
2364 | 28 | entryLength = 4; |
2365 | 28 | } |
2366 | 9.37k | input->seek(-6, librevenge::RVNG_SEEK_CUR); |
2367 | 9.37k | readNBytes(input, static_cast<unsigned long>(entryLength) * numEntries + 6, ret.m_complexValues[id]); |
2368 | 9.37k | } |
2369 | 20.3k | return ret; |
2370 | 20.3k | } |
2371 | | |
2372 | | std::map<unsigned short, unsigned> MSPUBParser::extractEscherValues(librevenge::RVNGInputStream *input, const EscherContainerInfo &record) |
2373 | 112k | { |
2374 | 112k | std::map<unsigned short, unsigned> ret; |
2375 | 112k | input->seek(record.contentsOffset + getEscherElementAdditionalHeaderLength(record.type), librevenge::RVNG_SEEK_SET); |
2376 | 2.73M | while (stillReading(input, record.contentsOffset + record.contentsLength)) |
2377 | 2.66M | { |
2378 | 2.66M | unsigned short id = readU16(input); |
2379 | 2.66M | if (id == 0) |
2380 | 554k | { |
2381 | 554k | if (!stillReading(input, record.contentsOffset + record.contentsLength)) |
2382 | 49.4k | break; |
2383 | 504k | MSPUB_DEBUG_MSG(("found escher value with ID 0!\n")); |
2384 | 504k | } |
2385 | 2.62M | unsigned value = readU32(input); |
2386 | 2.62M | ret[id] = value; |
2387 | 2.62M | } |
2388 | 112k | return ret; |
2389 | 112k | } |
2390 | | |
2391 | | |
2392 | | bool MSPUBParser::parseContentChunkReference(librevenge::RVNGInputStream *input, const MSPUBBlockInfo block) |
2393 | 109k | { |
2394 | | //input should be at block.dataOffset + 4 , that is, at the beginning of the list of sub-blocks |
2395 | 109k | MSPUB_DEBUG_MSG(("Parsing chunk reference 0x%x\n", m_lastSeenSeqNum)); |
2396 | 109k | unsigned type = UNKNOWN_CHUNK; |
2397 | 109k | unsigned long offset = 0; |
2398 | 109k | unsigned parentSeqNum = 0; |
2399 | 109k | bool seenType = false; |
2400 | 109k | bool seenOffset = false; |
2401 | 109k | bool seenParentSeqNum = false; |
2402 | 794k | while (stillReading(input, block.dataOffset + block.dataLength)) |
2403 | 685k | { |
2404 | 685k | MSPUBBlockInfo subBlock = parseBlock(input, true); |
2405 | | //FIXME: Warn if multiple of these blocks seen. |
2406 | 685k | if (subBlock.id == CHUNK_TYPE) |
2407 | 109k | { |
2408 | 109k | type = subBlock.data; |
2409 | 109k | seenType = true; |
2410 | 109k | } |
2411 | 576k | else if (subBlock.id == CHUNK_OFFSET) |
2412 | 109k | { |
2413 | 109k | offset = subBlock.data; |
2414 | 109k | seenOffset = true; |
2415 | 109k | } |
2416 | 466k | else if (subBlock.id == CHUNK_PARENT_SEQNUM) |
2417 | 100k | { |
2418 | 100k | parentSeqNum = subBlock.data; |
2419 | 100k | seenParentSeqNum = true; |
2420 | 100k | } |
2421 | 685k | } |
2422 | 109k | if (seenType && seenOffset) //FIXME: What if there is an offset, but not a type? Should we still set the end of the preceding chunk to that offset? |
2423 | 104k | { |
2424 | 104k | if (type == PAGE) |
2425 | 10.5k | { |
2426 | 10.5k | MSPUB_DEBUG_MSG(("page chunk: offset 0x%lx, seqnum 0x%x\n", offset, m_lastSeenSeqNum)); |
2427 | 10.5k | m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0)); |
2428 | 10.5k | m_pageChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); |
2429 | 10.5k | return true; |
2430 | 10.5k | } |
2431 | 94.1k | else if (type == DOCUMENT) |
2432 | 3.29k | { |
2433 | 3.29k | MSPUB_DEBUG_MSG(("document chunk: offset 0x%lx, seqnum 0x%x\n", offset, m_lastSeenSeqNum)); |
2434 | 3.29k | m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0)); |
2435 | 3.29k | m_documentChunkIndex = unsigned(m_contentChunks.size() - 1); |
2436 | 3.29k | return true; |
2437 | 3.29k | } |
2438 | 90.8k | else if (type == SHAPE || type == ALTSHAPE || type == GROUP || type == TABLE || type == LOGO) |
2439 | 33.0k | { |
2440 | 33.0k | MSPUB_DEBUG_MSG(("shape chunk: offset 0x%lx, seqnum 0x%x, parent seqnum: 0x%x\n", offset, m_lastSeenSeqNum, parentSeqNum)); |
2441 | 33.0k | m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0)); |
2442 | 33.0k | m_shapeChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); |
2443 | 33.0k | if (type == ALTSHAPE) |
2444 | 7.58k | { |
2445 | 7.58k | m_alternateShapeSeqNums.push_back(m_lastSeenSeqNum); |
2446 | 7.58k | } |
2447 | 33.0k | return true; |
2448 | 33.0k | } |
2449 | 57.7k | else if (type == CELLS) |
2450 | 1.32k | { |
2451 | 1.32k | m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0)); |
2452 | 1.32k | m_cellsChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); |
2453 | 1.32k | return true; |
2454 | 1.32k | } |
2455 | 56.4k | else if (type == PALETTE) |
2456 | 2.41k | { |
2457 | 2.41k | m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0)); |
2458 | 2.41k | m_paletteChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); |
2459 | 2.41k | return true; |
2460 | 2.41k | } |
2461 | 54.0k | else if (type == BORDER_ART) |
2462 | 2.31k | { |
2463 | 2.31k | m_contentChunks.push_back(ContentChunkReference(type, offset, 0, |
2464 | 2.31k | m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0)); |
2465 | 2.31k | m_borderArtChunkIndices.push_back( |
2466 | 2.31k | unsigned(m_contentChunks.size() - 1)); |
2467 | 2.31k | return true; |
2468 | 2.31k | } |
2469 | 51.7k | else if (type == FONT) |
2470 | 1.44k | { |
2471 | 1.44k | m_contentChunks.push_back(ContentChunkReference(type, offset, 0, |
2472 | 1.44k | m_lastSeenSeqNum, |
2473 | 1.44k | seenParentSeqNum ? parentSeqNum : 0)); |
2474 | 1.44k | m_fontChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); |
2475 | 1.44k | return true; |
2476 | 1.44k | } |
2477 | 50.2k | m_contentChunks.push_back(ContentChunkReference(type, offset, 0, m_lastSeenSeqNum, seenParentSeqNum ? parentSeqNum : 0)); |
2478 | 50.2k | m_unknownChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); |
2479 | 50.2k | } |
2480 | 54.8k | return false; |
2481 | 109k | } |
2482 | | |
2483 | | bool MSPUBParser::isBlockDataString(unsigned type) |
2484 | 1.21M | { |
2485 | 1.21M | return type == STRING_CONTAINER; |
2486 | 1.21M | } |
2487 | | void MSPUBParser::skipBlock(librevenge::RVNGInputStream *input, MSPUBBlockInfo block) |
2488 | 8.89M | { |
2489 | 8.89M | input->seek(block.dataOffset + block.dataLength, librevenge::RVNG_SEEK_SET); |
2490 | 8.89M | } |
2491 | | |
2492 | | EscherContainerInfo MSPUBParser::parseEscherContainer(librevenge::RVNGInputStream *input) |
2493 | 1.02M | { |
2494 | 1.02M | EscherContainerInfo info; |
2495 | 1.02M | info.initial = readU16(input); |
2496 | 1.02M | info.type = readU16(input); |
2497 | 1.02M | info.contentsLength = readU32(input); |
2498 | 1.02M | info.contentsOffset = input->tell(); |
2499 | 1.02M | MSPUB_DEBUG_MSG(("Parsed escher container: type 0x%x, contentsOffset 0x%lx, contentsLength 0x%lx\n", info.type, info.contentsOffset, info.contentsLength)); |
2500 | 1.02M | return info; |
2501 | 1.02M | } |
2502 | | |
2503 | | MSPUBBlockInfo MSPUBParser::parseBlock(librevenge::RVNGInputStream *input, bool skipHierarchicalData) |
2504 | 70.0M | { |
2505 | 70.0M | MSPUBBlockInfo info; |
2506 | 70.0M | info.startPosition = input->tell(); |
2507 | 70.0M | info.id = readU8(input); |
2508 | 70.0M | info.type = readU8(input); |
2509 | 70.0M | info.dataOffset = input->tell(); |
2510 | 70.0M | int len = getBlockDataLength(info.type); |
2511 | 70.0M | bool varLen = len < 0; |
2512 | 70.0M | if (varLen) |
2513 | 1.21M | { |
2514 | 1.21M | info.dataLength = readU32(input); |
2515 | 1.21M | if (isBlockDataString(info.type)) |
2516 | 163k | { |
2517 | 163k | info.stringData = std::vector<unsigned char>(); |
2518 | 163k | readNBytes(input, info.dataLength - 4, info.stringData); |
2519 | 163k | } |
2520 | 1.05M | else if (skipHierarchicalData) |
2521 | 459k | { |
2522 | 459k | skipBlock(input, info); |
2523 | 459k | } |
2524 | 1.21M | info.data = 0; |
2525 | 1.21M | } |
2526 | 68.7M | else |
2527 | 68.7M | { |
2528 | 68.7M | info.dataLength = len; |
2529 | 68.7M | switch (info.dataLength) |
2530 | 68.7M | { |
2531 | 0 | case 1: |
2532 | 0 | info.data = readU8(input); |
2533 | 0 | break; |
2534 | 2.86M | case 2: |
2535 | 2.86M | info.data = readU16(input); |
2536 | 2.86M | break; |
2537 | 4.01M | case 4: |
2538 | 4.01M | info.data = readU32(input); |
2539 | 4.01M | break; |
2540 | 330k | case 8: |
2541 | 382k | case 16: |
2542 | 417k | case 24: |
2543 | | //FIXME: Not doing anything with this data for now. |
2544 | 417k | skipBlock(input, info); |
2545 | 417k | MSPUB_FALLTHROUGH; |
2546 | 61.9M | default: |
2547 | 61.9M | info.data = 0; |
2548 | 68.7M | } |
2549 | 68.7M | } |
2550 | 70.0M | MSPUB_DEBUG_MSG(("parseBlock dataOffset 0x%lx, id 0x%x, type 0x%x, dataLength 0x%lx, integral data 0x%x\n", info.dataOffset, info.id, info.type, info.dataLength, info.data)); |
2551 | 70.0M | return info; |
2552 | 70.0M | } |
2553 | | |
2554 | | PageType MSPUBParser::getPageTypeBySeqNum(unsigned seqNum) |
2555 | 10.3k | { |
2556 | 10.3k | switch (seqNum) |
2557 | 10.3k | { |
2558 | 592 | case 0x10d: |
2559 | 610 | case 0x110: |
2560 | 640 | case 0x113: |
2561 | 680 | case 0x117: |
2562 | 680 | return DUMMY_PAGE; |
2563 | 9.70k | default: |
2564 | 9.70k | return NORMAL; |
2565 | 10.3k | } |
2566 | 10.3k | } |
2567 | | |
2568 | | bool MSPUBParser::parsePaletteChunk(librevenge::RVNGInputStream *input, const ContentChunkReference &chunk) |
2569 | 2.53k | { |
2570 | 2.53k | unsigned length = readU32(input); |
2571 | 1.16M | while (stillReading(input, chunk.offset + length)) |
2572 | 1.15M | { |
2573 | 1.15M | MSPUBBlockInfo info = parseBlock(input); |
2574 | 1.15M | if (info.type == 0xA0) |
2575 | 1.73k | { |
2576 | 2.77M | while (stillReading(input, info.dataOffset + info.dataLength)) |
2577 | 2.76M | { |
2578 | 2.76M | MSPUBBlockInfo subInfo = parseBlock(input); |
2579 | 2.76M | if (subInfo.type == GENERAL_CONTAINER) |
2580 | 361k | { |
2581 | 361k | parsePaletteEntry(input, subInfo); |
2582 | 361k | } |
2583 | 2.40M | else if (subInfo.type == DUMMY) |
2584 | 26.4k | { |
2585 | 26.4k | m_collector->addPaletteColor(Color()); |
2586 | 26.4k | } |
2587 | 2.76M | skipBlock(input, subInfo); |
2588 | 2.76M | } |
2589 | 1.73k | } |
2590 | 1.15M | skipBlock(input, info); |
2591 | 1.15M | } |
2592 | 2.53k | return true; |
2593 | 2.53k | } |
2594 | | |
2595 | | void MSPUBParser::parsePaletteEntry(librevenge::RVNGInputStream *input, MSPUBBlockInfo info) |
2596 | 361k | { |
2597 | 2.17M | while (stillReading(input, info.dataOffset + info.dataLength)) |
2598 | 1.81M | { |
2599 | 1.81M | MSPUBBlockInfo subInfo = parseBlock(input, true); |
2600 | 1.81M | if (subInfo.id == 0x01) |
2601 | 5.52k | { |
2602 | 5.52k | m_collector->addPaletteColor(Color(subInfo.data & 0xFF, (subInfo.data >> 8) & 0xFF, (subInfo.data >> 16) & 0xFF)); |
2603 | 5.52k | } |
2604 | 1.81M | } |
2605 | 361k | } |
2606 | | |
2607 | | bool MSPUBParser::parseMetaData() |
2608 | 4.35k | { |
2609 | 4.35k | m_input->seek(0, librevenge::RVNG_SEEK_SET); |
2610 | 4.35k | MSPUBMetaData metaData; |
2611 | | |
2612 | 4.35k | std::unique_ptr<librevenge::RVNGInputStream> sumaryInfo(m_input->getSubStreamByName("\x05SummaryInformation")); |
2613 | 4.35k | if (sumaryInfo) |
2614 | 1.59k | { |
2615 | 1.59k | metaData.parse(sumaryInfo.get()); |
2616 | 1.59k | } |
2617 | | |
2618 | 4.35k | std::unique_ptr<librevenge::RVNGInputStream> docSumaryInfo(m_input->getSubStreamByName("\005DocumentSummaryInformation")); |
2619 | 4.35k | if (docSumaryInfo) |
2620 | 1.03k | { |
2621 | 1.03k | metaData.parse(docSumaryInfo.get()); |
2622 | 1.03k | } |
2623 | | |
2624 | 4.35k | m_input->seek(0, librevenge::RVNG_SEEK_SET); |
2625 | 4.35k | metaData.parseTimes(m_input); |
2626 | 4.35k | m_collector->collectMetaData(metaData.getMetaData()); |
2627 | | |
2628 | 4.35k | return true; |
2629 | 4.35k | } |
2630 | | |
2631 | | |
2632 | | } |
2633 | | |
2634 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |