Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* libmspub
3 : * Version: MPL 1.1 / GPLv2+ / LGPLv2+
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License or as specified alternatively below. You may obtain a copy of
8 : * the License at http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * Major Contributor(s):
16 : * Copyright (C) 2012 Brennan Vincent <brennanv@email.arizona.edu>
17 : * Copyright (C) 2012 Fridrich Strba <fridrich.strba@bluewin.ch>
18 : *
19 : * All Rights Reserved.
20 : *
21 : * For minor contributions see the git repository.
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPLv2+"), or
25 : * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
26 : * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable
27 : * instead of those above.
28 : */
29 :
30 : #include <algorithm>
31 :
32 : #include <boost/shared_ptr.hpp>
33 :
34 : #include <libwpd-stream/libwpd-stream.h>
35 :
36 : #include "MSPUBParser2k.h"
37 : #include "ColorReference.h"
38 : #include "ShapeType.h"
39 : #include "libmspub_utils.h"
40 : #include "MSPUBCollector.h"
41 :
42 0 : libmspub::MSPUBParser2k::MSPUBParser2k(WPXInputStream *input, MSPUBCollector *collector)
43 : : MSPUBParser(input, collector),
44 : m_imageDataChunkIndices(),
45 : m_quillColorEntries(),
46 0 : m_chunkChildIndicesById()
47 : {
48 0 : }
49 :
50 0 : unsigned libmspub::MSPUBParser2k::getColorIndexByQuillEntry(unsigned entry)
51 : {
52 0 : unsigned translation = translate2kColorReference(entry);
53 0 : std::vector<unsigned>::const_iterator i_entry = std::find(m_quillColorEntries.begin(), m_quillColorEntries.end(), translation);
54 0 : if (i_entry == m_quillColorEntries.end())
55 : {
56 0 : m_quillColorEntries.push_back(translation);
57 0 : m_collector->addTextColor(ColorReference(translation));
58 0 : return m_quillColorEntries.size() - 1;
59 : }
60 0 : return i_entry - m_quillColorEntries.begin();
61 : }
62 :
63 0 : libmspub::MSPUBParser2k::~MSPUBParser2k()
64 : {
65 0 : }
66 :
67 : // Takes a line width specifier in Pub2k format and translates it into quarter points
68 0 : unsigned short translateLineWidth(unsigned char lineWidth)
69 : {
70 0 : if (lineWidth == 0x81)
71 : {
72 0 : return 0;
73 : }
74 0 : else if (lineWidth > 0x81)
75 : {
76 0 : return ((lineWidth - 0x81) / 3) * 4 + ((lineWidth - 0x81) % 3) + 1;
77 : }
78 : else
79 : {
80 0 : return lineWidth * 4;
81 : }
82 : }
83 :
84 0 : libmspub::Color libmspub::MSPUBParser2k::getColorBy2kHex(unsigned hex)
85 : {
86 0 : switch ((hex >> 24) & 0xFF)
87 : {
88 : case 0x80:
89 : case 0x00:
90 0 : return getColorBy2kIndex(hex & 0xFF);
91 : case 0x90:
92 : case 0x20:
93 0 : return Color(hex & 0xFF, (hex >> 8) & 0xFF, (hex >> 16) & 0xFF);
94 : default:
95 0 : return Color();
96 : }
97 : }
98 :
99 0 : libmspub::Color libmspub::MSPUBParser2k::getColorBy2kIndex(unsigned char index)
100 : {
101 0 : switch(index)
102 : {
103 : case 0x00:
104 0 : return Color(0, 0, 0);
105 : case 0x01:
106 0 : return Color(0xff, 0xff, 0xff);
107 : case 0x02:
108 0 : return Color(0xff, 0, 0);
109 : case 0x03:
110 0 : return Color(0, 0xff, 0);
111 : case 0x04:
112 0 : return Color(0, 0, 0xff);
113 : case 0x05:
114 0 : return Color(0xff, 0xff, 0);
115 : case 0x06:
116 0 : return Color(0, 0xff, 0xff);
117 : case 0x07:
118 0 : return Color(0xff, 0, 0xff);
119 : case 0x08:
120 0 : return Color(128, 128, 128);
121 : case 0x09:
122 0 : return Color(192, 192, 192);
123 : case 0x0A:
124 0 : return Color(128, 0, 0);
125 : case 0x0B:
126 0 : return Color(0, 128, 0);
127 : case 0x0C:
128 0 : return Color(0, 0, 128);
129 : case 0x0D:
130 0 : return Color(128, 128, 0);
131 : case 0x0E:
132 0 : return Color(0, 128, 128);
133 : case 0x0F:
134 0 : return Color(128, 0, 128);
135 : case 0x10:
136 0 : return Color(255, 153, 51);
137 : case 0x11:
138 0 : return Color(51, 0, 51);
139 : case 0x12:
140 0 : return Color(0, 0, 153);
141 : case 0x13:
142 0 : return Color(0, 153, 0);
143 : case 0x14:
144 0 : return Color(153, 153, 0);
145 : case 0x15:
146 0 : return Color(204, 102, 0);
147 : case 0x16:
148 0 : return Color(153, 0, 0);
149 : case 0x17:
150 0 : return Color(204, 153, 204);
151 : case 0x18:
152 0 : return Color(102, 102, 255);
153 : case 0x19:
154 0 : return Color(102, 255, 102);
155 : case 0x1A:
156 0 : return Color(255, 255, 153);
157 : case 0x1B:
158 0 : return Color(255, 204, 153);
159 : case 0x1C:
160 0 : return Color(255, 102, 102);
161 : case 0x1D:
162 0 : return Color(255, 153, 0);
163 : case 0x1E:
164 0 : return Color(0, 102, 255);
165 : case 0x1F:
166 0 : return Color(255, 204, 0);
167 : case 0x20:
168 0 : return Color(153, 0, 51);
169 : case 0x21:
170 0 : return Color(102, 51, 0);
171 : case 0x22:
172 0 : return Color(66, 66, 66);
173 : case 0x23:
174 0 : return Color(255, 153, 102);
175 : case 0x24:
176 0 : return Color(153, 51, 0);
177 : case 0x25:
178 0 : return Color(255, 102, 0);
179 : case 0x26:
180 0 : return Color(51, 51, 0);
181 : case 0x27:
182 0 : return Color(153, 204, 0);
183 : case 0x28:
184 0 : return Color(255, 255, 153);
185 : case 0x29:
186 0 : return Color(0, 51, 0);
187 : case 0x2A:
188 0 : return Color(51, 153, 102);
189 : case 0x2B:
190 0 : return Color(204, 255, 204);
191 : case 0x2C:
192 0 : return Color(0, 51, 102);
193 : case 0x2D:
194 0 : return Color(51, 204, 204);
195 : case 0x2E:
196 0 : return Color(204, 255, 255);
197 : case 0x2F:
198 0 : return Color(51, 102, 255);
199 : case 0x30:
200 0 : return Color(0, 204, 255);
201 : case 0x31:
202 0 : return Color(153, 204, 255);
203 : case 0x32:
204 0 : return Color(51, 51, 153);
205 : case 0x33:
206 0 : return Color(102, 102, 153);
207 : case 0x34:
208 0 : return Color(153, 51, 102);
209 : case 0x35:
210 0 : return Color(204, 153, 255);
211 : case 0x36:
212 0 : return Color(51, 51, 51);
213 : case 0x37:
214 0 : return Color(150, 150, 150);
215 : default:
216 0 : return Color();
217 : }
218 : }
219 :
220 : // takes a color reference in 2k format and translates it into 2k2 format that collector understands.
221 0 : unsigned libmspub::MSPUBParser2k::translate2kColorReference(unsigned ref2k)
222 : {
223 0 : switch ((ref2k >> 24) & 0xFF)
224 : {
225 : case 0xC0: //index into user palette
226 : case 0xE0:
227 0 : return (ref2k & 0xFF) | (0x08 << 24);
228 : default:
229 : {
230 0 : Color c = getColorBy2kHex(ref2k);
231 0 : return (c.r) | (c.g << 8) | (c.b << 16);
232 : }
233 : }
234 : }
235 :
236 : //FIXME: Valek found different values; what does this depend on?
237 0 : libmspub::ShapeType libmspub::MSPUBParser2k::getShapeType(unsigned char shapeSpecifier)
238 : {
239 0 : switch (shapeSpecifier)
240 : {
241 : case 0x1:
242 0 : return RIGHT_TRIANGLE;
243 : /*
244 : case 0x2:
245 : return GENERAL_TRIANGLE;
246 : */
247 : case 0x3:
248 0 : return UP_ARROW;
249 : case 0x4:
250 0 : return STAR;
251 : case 0x5:
252 0 : return HEART;
253 : case 0x6:
254 0 : return ISOCELES_TRIANGLE;
255 : case 0x7:
256 0 : return PARALLELOGRAM;
257 : /*
258 : case 0x8:
259 : return TILTED_TRAPEZOID;
260 : */
261 : case 0x9:
262 0 : return UP_DOWN_ARROW;
263 : case 0xA:
264 0 : return SEAL_16;
265 : case 0xB:
266 0 : return WAVE;
267 : case 0xC:
268 0 : return DIAMOND;
269 : case 0xD:
270 0 : return TRAPEZOID;
271 : case 0xE:
272 0 : return CHEVRON;
273 : case 0xF:
274 0 : return BENT_ARROW;
275 : case 0x10:
276 0 : return SEAL_24;
277 : /*
278 : case 0x11:
279 : return PIE;
280 : */
281 : case 0x12:
282 0 : return PENTAGON;
283 : case 0x13:
284 0 : return HOME_PLATE;
285 : /*
286 : case 0x14:
287 : return NOTCHED_TRIANGLE;
288 : */
289 : case 0x15:
290 0 : return U_TURN_ARROW;
291 : case 0x16:
292 0 : return IRREGULAR_SEAL_1;
293 : /*
294 : case 0x17:
295 : return CHORD;
296 : */
297 : case 0x18:
298 0 : return HEXAGON;
299 : /*
300 : case 0x19:
301 : return NOTCHED_RECTANGLE;
302 : */
303 : /*
304 : case 0x1A:
305 : return W_SHAPE; //This is a bizarre shape; the number of vertices depends on one of the adjust values.
306 : //We need to refactor our escher shape drawing routines before we can handle it.
307 : */
308 : /*
309 : case 0x1B:
310 : return ROUND_RECT_CALLOUT_2K; //This is not quite the same as the round rect. found in 2k2 and above.
311 : */
312 : case 0x1C:
313 0 : return IRREGULAR_SEAL_2;
314 : case 0x1D:
315 0 : return BLOCK_ARC;
316 : case 0x1E:
317 0 : return OCTAGON;
318 : case 0x1F:
319 0 : return PLUS;
320 : case 0x20:
321 0 : return CUBE;
322 : /*
323 : case 0x21:
324 : return OVAL_CALLOUT_2K; //Not sure yet if this is the same as the 2k2 one.
325 : */
326 : case 0x22:
327 0 : return LIGHTNING_BOLT;
328 : default:
329 0 : return UNKNOWN_SHAPE;
330 : }
331 : }
332 :
333 0 : void libmspub::MSPUBParser2k::parseContentsTextIfNecessary(WPXInputStream *)
334 : {
335 0 : }
336 :
337 0 : bool libmspub::MSPUBParser2k::parseContents(WPXInputStream *input)
338 : {
339 0 : parseContentsTextIfNecessary(input);
340 0 : input->seek(0x16, WPX_SEEK_SET);
341 0 : unsigned trailerOffset = readU32(input);
342 0 : input->seek(trailerOffset, WPX_SEEK_SET);
343 0 : unsigned numBlocks = readU16(input);
344 0 : unsigned chunkOffset = 0;
345 0 : for (unsigned i = 0; i < numBlocks; ++i)
346 : {
347 0 : input->seek(input->tell() + 2, WPX_SEEK_SET);
348 0 : unsigned short id = readU16(input);
349 0 : unsigned short parent = readU16(input);
350 0 : chunkOffset = readU32(input);
351 0 : if (m_contentChunks.size() > 0)
352 : {
353 0 : m_contentChunks.back().end = chunkOffset;
354 : }
355 0 : unsigned offset = input->tell();
356 0 : input->seek(chunkOffset, WPX_SEEK_SET);
357 0 : unsigned short typeMarker = readU16(input);
358 0 : input->seek(offset, WPX_SEEK_SET);
359 0 : switch (typeMarker)
360 : {
361 : case 0x0014:
362 : MSPUB_DEBUG_MSG(("Found page chunk of id 0x%x and parent 0x%x\n", id, parent));
363 0 : m_contentChunks.push_back(ContentChunkReference(PAGE, chunkOffset, 0, id, parent));
364 0 : m_pageChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
365 0 : m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1));
366 0 : break;
367 : case 0x0015:
368 : MSPUB_DEBUG_MSG(("Found document chunk of id 0x%x and parent 0x%x\n", id, parent));
369 0 : m_contentChunks.push_back(ContentChunkReference(DOCUMENT, chunkOffset, 0, id, parent));
370 0 : m_documentChunkIndex = unsigned(m_contentChunks.size() - 1);
371 0 : m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1));
372 0 : break;
373 : case 0x0002:
374 : MSPUB_DEBUG_MSG(("Found image_2k chunk of id 0x%x and parent 0x%x\n", id, parent));
375 0 : m_contentChunks.push_back(ContentChunkReference(IMAGE_2K, chunkOffset, 0, id, parent));
376 0 : m_shapeChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
377 0 : m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1));
378 0 : break;
379 : case 0x0021:
380 : MSPUB_DEBUG_MSG(("Found image_2k_data chunk of id 0x%x and parent 0x%x\n", id, parent));
381 0 : m_contentChunks.push_back(ContentChunkReference(IMAGE_2K_DATA, chunkOffset, 0, id, parent));
382 0 : m_imageDataChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
383 0 : m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1));
384 0 : break;
385 : case 0x0000:
386 : case 0x0004:
387 : case 0x0005:
388 : case 0x0006:
389 : case 0x0007:
390 : case 0x0008:
391 : MSPUB_DEBUG_MSG(("Found shape chunk of id 0x%x and parent 0x%x\n", id, parent));
392 0 : m_contentChunks.push_back(ContentChunkReference(SHAPE, chunkOffset, 0, id, parent));
393 0 : m_shapeChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
394 0 : m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1));
395 0 : break;
396 : case 0x0047:
397 : MSPUB_DEBUG_MSG(("Found palette chunk of id 0x%x and parent 0x%x\n", id, parent));
398 0 : m_contentChunks.push_back(ContentChunkReference(PALETTE, chunkOffset, 0, id, parent));
399 0 : m_paletteChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
400 0 : m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1));
401 0 : break;
402 : case 0x000F:
403 : MSPUB_DEBUG_MSG(("Found group chunk of id 0x%x and parent 0x%x\n", id, parent));
404 0 : m_contentChunks.push_back(ContentChunkReference(GROUP, chunkOffset, 0, id, parent));
405 0 : m_shapeChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
406 0 : m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1));
407 0 : break;
408 : default:
409 : MSPUB_DEBUG_MSG(("Found unknown chunk of id 0x%x and parent 0x%x\n", id, parent));
410 0 : m_contentChunks.push_back(ContentChunkReference(UNKNOWN_CHUNK, chunkOffset, 0, id, parent));
411 0 : m_unknownChunkIndices.push_back(unsigned(m_contentChunks.size() - 1));
412 0 : m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1));
413 0 : break;
414 : }
415 : }
416 0 : if (m_contentChunks.size() > 0)
417 : {
418 0 : m_contentChunks.back().end = chunkOffset;
419 : }
420 :
421 0 : if (!parseDocument(input))
422 : {
423 : MSPUB_DEBUG_MSG(("No document chunk found.\n"));
424 0 : return false;
425 : }
426 :
427 :
428 0 : for (unsigned i = 0; i < m_paletteChunkIndices.size(); ++i)
429 : {
430 0 : const ContentChunkReference &chunk = m_contentChunks.at(m_paletteChunkIndices[i]);
431 0 : input->seek(chunk.offset, WPX_SEEK_SET);
432 0 : input->seek(0xA0, WPX_SEEK_CUR);
433 0 : for (unsigned j = 0; j < 8; ++j)
434 : {
435 0 : unsigned hex = readU32(input);
436 0 : Color color = getColorBy2kHex(hex);
437 0 : m_collector->addPaletteColor(color);
438 : }
439 : }
440 :
441 0 : for (unsigned i = 0; i < m_imageDataChunkIndices.size(); ++i)
442 : {
443 0 : const ContentChunkReference &chunk = m_contentChunks.at(m_imageDataChunkIndices[i]);
444 0 : input->seek(chunk.offset + 4, WPX_SEEK_SET);
445 0 : unsigned toRead = readU32(input);
446 0 : WPXBinaryData img;
447 0 : while (toRead > 0 && stillReading(input, (unsigned long)-1))
448 : {
449 0 : unsigned long howManyRead = 0;
450 0 : const unsigned char *buf = input->read(toRead, howManyRead);
451 0 : img.append(buf, howManyRead);
452 0 : toRead -= howManyRead;
453 : }
454 0 : m_collector->addImage(++m_lastAddedImage, WMF, img);
455 0 : }
456 :
457 0 : for (unsigned i = 0; i < m_shapeChunkIndices.size(); ++i)
458 : {
459 0 : parse2kShapeChunk(m_contentChunks.at(m_shapeChunkIndices[i]), input);
460 : }
461 :
462 0 : return true;
463 : }
464 :
465 0 : bool libmspub::MSPUBParser2k::parseDocument(WPXInputStream *input)
466 : {
467 0 : if (m_documentChunkIndex.is_initialized())
468 : {
469 0 : input->seek(m_contentChunks[m_documentChunkIndex.get()].offset, WPX_SEEK_SET);
470 0 : input->seek(0x14, WPX_SEEK_CUR);
471 0 : unsigned width = readU32(input);
472 0 : unsigned height = readU32(input);
473 0 : m_collector->setWidthInEmu(width);
474 0 : m_collector->setHeightInEmu(height);
475 0 : return true;
476 : }
477 0 : return false;
478 : }
479 :
480 0 : void libmspub::MSPUBParser2k::parseShapeRotation(WPXInputStream *input, bool isGroup, bool isLine,
481 : unsigned seqNum, unsigned chunkOffset)
482 : {
483 0 : input->seek(chunkOffset + 4, WPX_SEEK_SET);
484 : // shape transforms are NOT compounded with group transforms. They are equal to what they would be
485 : // if the shape were not part of a group at all. This is different from how MSPUBCollector handles rotations;
486 : // we work around the issue by simply not setting the rotation of any group, thereby letting it default to zero.
487 : //
488 : // Furthermore, line rotations are redundant and need to be treated as zero.
489 0 : unsigned short counterRotationInDegreeTenths = readU16(input);
490 0 : if (!isGroup && !isLine)
491 : {
492 0 : m_collector->setShapeRotation(seqNum, 360. - double(counterRotationInDegreeTenths) / 10);
493 : }
494 0 : }
495 :
496 0 : bool libmspub::MSPUBParser2k::parse2kShapeChunk(const ContentChunkReference &chunk, WPXInputStream *input,
497 : boost::optional<unsigned> pageSeqNum, bool topLevelCall)
498 : {
499 0 : unsigned page = pageSeqNum.get_value_or(chunk.parentSeqNum);
500 0 : input->seek(chunk.offset, WPX_SEEK_SET);
501 0 : if (topLevelCall)
502 : {
503 : // ignore non top level shapes
504 0 : int i_page = -1;
505 0 : for (unsigned j = 0; j < m_pageChunkIndices.size(); ++j)
506 : {
507 0 : unsigned pageIndex = m_pageChunkIndices[j];
508 0 : if (m_contentChunks.at(pageIndex).seqNum == chunk.parentSeqNum)
509 : {
510 0 : i_page = pageIndex;
511 0 : break;
512 : }
513 : }
514 0 : if (i_page == -1)
515 : {
516 0 : return false;
517 : }
518 0 : if (getPageTypeBySeqNum(m_contentChunks[i_page].seqNum) != NORMAL)
519 : {
520 0 : return false;
521 : }
522 : // if the parent of this is a page and hasn't yet been added, then add it.
523 0 : if (!m_collector->hasPage(chunk.parentSeqNum))
524 : {
525 0 : m_collector->addPage(chunk.parentSeqNum);
526 : }
527 : }
528 0 : m_collector->setShapePage(chunk.seqNum, page);
529 0 : m_collector->setShapeBorderPosition(chunk.seqNum, INSIDE_SHAPE); // This appears to be the only possibility for MSPUB2k
530 0 : bool isImage = false;
531 0 : bool isRectangle = false;
532 0 : bool isGroup = false;
533 0 : bool isLine = false;
534 0 : unsigned flagsOffset(0); // ? why was this changed from boost::optional ?
535 0 : parseShapeType(input, chunk.seqNum, chunk.offset, isGroup, isLine, isImage, isRectangle, flagsOffset);
536 0 : parseShapeRotation(input, isGroup, isLine, chunk.seqNum, chunk.offset);
537 0 : parseShapeCoordinates(input, chunk.seqNum, chunk.offset);
538 0 : parseShapeFlips(input, flagsOffset, chunk.seqNum, chunk.offset);
539 0 : if (isGroup)
540 : {
541 0 : return parseGroup(input, chunk.seqNum, page);
542 : }
543 0 : if (isImage)
544 : {
545 0 : assignShapeImgIndex(chunk.seqNum);
546 : }
547 : else
548 : {
549 0 : parseShapeFill(input, chunk.seqNum, chunk.offset);
550 : }
551 0 : parseShapeLine(input, isRectangle, chunk.offset, chunk.seqNum);
552 0 : m_collector->setShapeOrder(chunk.seqNum);
553 0 : return true;
554 : }
555 :
556 0 : unsigned libmspub::MSPUBParser2k::getShapeFillTypeOffset() const
557 : {
558 0 : return 0x2A;
559 : }
560 :
561 0 : unsigned libmspub::MSPUBParser2k::getShapeFillColorOffset() const
562 : {
563 0 : return 0x22;
564 : }
565 :
566 0 : void libmspub::MSPUBParser2k::parseShapeFill(WPXInputStream *input, unsigned seqNum, unsigned chunkOffset)
567 : {
568 0 : input->seek(chunkOffset + getShapeFillTypeOffset(), WPX_SEEK_SET);
569 0 : unsigned char fillType = readU8(input);
570 0 : if (fillType == 2) // other types are gradients and patterns which are not implemented yet. 0 is no fill.
571 : {
572 0 : input->seek(chunkOffset + getShapeFillColorOffset(), WPX_SEEK_SET);
573 0 : unsigned fillColorReference = readU32(input);
574 0 : unsigned translatedFillColorReference = translate2kColorReference(fillColorReference);
575 0 : m_collector->setShapeFill(seqNum, boost::shared_ptr<Fill>(new SolidFill(ColorReference(translatedFillColorReference), 1, m_collector)), false);
576 : }
577 0 : }
578 :
579 0 : bool libmspub::MSPUBParser2k::parseGroup(WPXInputStream *input, unsigned seqNum, unsigned page)
580 : {
581 0 : bool retVal = true;
582 0 : m_collector->beginGroup();
583 0 : m_collector->setCurrentGroupSeqNum(seqNum);
584 0 : for (unsigned i = 0; i < m_chunkChildIndicesById[seqNum].size(); ++i)
585 : {
586 0 : const ContentChunkReference &childChunk = m_contentChunks.at(m_chunkChildIndicesById[seqNum][i]);
587 0 : if (childChunk.type == SHAPE || childChunk.type == GROUP)
588 : {
589 0 : retVal = retVal && parse2kShapeChunk(childChunk, input, page, false);
590 : }
591 : }
592 0 : m_collector->endGroup();
593 0 : return retVal;
594 : }
595 :
596 0 : void libmspub::MSPUBParser2k::assignShapeImgIndex(unsigned seqNum)
597 : {
598 0 : int i_dataIndex = -1;
599 0 : for (unsigned j = 0; j < m_imageDataChunkIndices.size(); ++j)
600 : {
601 0 : if (m_contentChunks.at(m_imageDataChunkIndices[j]).parentSeqNum == seqNum)
602 : {
603 0 : i_dataIndex = j;
604 0 : break;
605 : }
606 : }
607 0 : if (i_dataIndex >= 0)
608 : {
609 0 : m_collector->setShapeImgIndex(seqNum, i_dataIndex + 1);
610 : }
611 0 : }
612 :
613 0 : void libmspub::MSPUBParser2k::parseShapeCoordinates(WPXInputStream *input, unsigned seqNum,
614 : unsigned chunkOffset)
615 : {
616 0 : input->seek(chunkOffset + 6, WPX_SEEK_SET);
617 0 : int xs = translateCoordinateIfNecessary(readS32(input));
618 0 : int ys = translateCoordinateIfNecessary(readS32(input));
619 0 : int xe = translateCoordinateIfNecessary(readS32(input));
620 0 : int ye = translateCoordinateIfNecessary(readS32(input));
621 0 : m_collector->setShapeCoordinatesInEmu(seqNum, xs, ys, xe, ye);
622 0 : }
623 :
624 0 : int libmspub::MSPUBParser2k::translateCoordinateIfNecessary(int coordinate) const
625 : {
626 0 : return coordinate;
627 : }
628 :
629 0 : void libmspub::MSPUBParser2k::parseShapeFlips(WPXInputStream *input, unsigned flagsOffset, unsigned seqNum,
630 : unsigned chunkOffset)
631 : {
632 0 : if (flagsOffset)
633 : {
634 0 : input->seek(chunkOffset + flagsOffset, WPX_SEEK_SET);
635 0 : unsigned char flags = readU8(input);
636 0 : bool flipV = flags & 0x1;
637 0 : bool flipH = flags & (0x2 | 0x10); // FIXME: this is a guess
638 0 : m_collector->setShapeFlip(seqNum, flipV, flipH);
639 : }
640 0 : }
641 :
642 0 : void libmspub::MSPUBParser2k::parseShapeType(WPXInputStream *input,
643 : unsigned seqNum, unsigned chunkOffset,
644 : bool &isGroup, bool &isLine, bool &isImage, bool &isRectangle,
645 : unsigned &flagsOffset)
646 : {
647 0 : input->seek(chunkOffset, WPX_SEEK_SET);
648 0 : unsigned short typeMarker = readU16(input);
649 0 : if (typeMarker == 0x000f)
650 : {
651 0 : isGroup = true;
652 : }
653 0 : else if (typeMarker == 0x0004)
654 : {
655 0 : isLine = true;
656 0 : flagsOffset = 0x41;
657 0 : m_collector->setShapeType(seqNum, LINE);
658 : }
659 0 : else if (typeMarker == 0x0002)
660 : {
661 0 : isImage = true;
662 0 : m_collector->setShapeType(seqNum, RECTANGLE);
663 0 : isRectangle = true;
664 : }
665 0 : else if (typeMarker == 0x0005)
666 : {
667 0 : m_collector->setShapeType(seqNum, RECTANGLE);
668 0 : isRectangle = true;
669 : }
670 0 : else if (typeMarker == 0x0006)
671 : {
672 0 : input->seek(chunkOffset + 0x31, WPX_SEEK_SET);
673 0 : ShapeType shapeType = getShapeType(readU8(input));
674 0 : flagsOffset = 0x33;
675 0 : if (shapeType != UNKNOWN_SHAPE)
676 : {
677 0 : m_collector->setShapeType(seqNum, shapeType);
678 : }
679 : }
680 0 : else if (typeMarker == 0x0007)
681 : {
682 0 : m_collector->setShapeType(seqNum, ELLIPSE);
683 : }
684 0 : else if (typeMarker == getTextMarker())
685 : {
686 0 : m_collector->setShapeType(seqNum, RECTANGLE);
687 0 : isRectangle = true;
688 0 : input->seek(chunkOffset + getTextIdOffset(), WPX_SEEK_SET);
689 0 : unsigned txtId = readU16(input);
690 0 : m_collector->addTextShape(txtId, seqNum);
691 : }
692 0 : }
693 :
694 0 : unsigned libmspub::MSPUBParser2k::getTextIdOffset() const
695 : {
696 0 : return 0x58;
697 : }
698 :
699 0 : unsigned short libmspub::MSPUBParser2k::getTextMarker() const
700 : {
701 0 : return 0x0008;
702 : }
703 :
704 0 : unsigned libmspub::MSPUBParser2k::getFirstLineOffset() const
705 : {
706 0 : return 0x2C;
707 : }
708 :
709 0 : unsigned libmspub::MSPUBParser2k::getSecondLineOffset() const
710 : {
711 0 : return 0x35;
712 : }
713 :
714 0 : void libmspub::MSPUBParser2k::parseShapeLine(WPXInputStream *input, bool isRectangle, unsigned offset,
715 : unsigned seqNum)
716 : {
717 0 : input->seek(offset + getFirstLineOffset(), WPX_SEEK_SET);
718 0 : unsigned short leftLineWidth = readU8(input);
719 0 : bool leftLineExists = leftLineWidth != 0;
720 0 : unsigned leftColorReference = readU32(input);
721 0 : unsigned translatedLeftColorReference = translate2kColorReference(leftColorReference);
722 0 : if (isRectangle)
723 : {
724 0 : input->seek(offset + getSecondLineOffset(), WPX_SEEK_SET);
725 0 : unsigned char topLineWidth = readU8(input);
726 0 : bool topLineExists = topLineWidth != 0;
727 0 : unsigned topColorReference = readU32(input);
728 0 : unsigned translatedTopColorReference = translate2kColorReference(topColorReference);
729 : m_collector->addShapeLine(seqNum, Line(ColorReference(translatedTopColorReference),
730 0 : translateLineWidth(topLineWidth) * EMUS_IN_INCH / (4 * POINTS_IN_INCH), topLineExists));
731 :
732 0 : input->seek(1, WPX_SEEK_CUR);
733 0 : unsigned char rightLineWidth = readU8(input);
734 0 : bool rightLineExists = rightLineWidth != 0;
735 0 : unsigned rightColorReference = readU32(input);
736 0 : unsigned translatedRightColorReference = translate2kColorReference(rightColorReference);
737 : m_collector->addShapeLine(seqNum, Line(ColorReference(translatedRightColorReference),
738 0 : translateLineWidth(rightLineWidth) * EMUS_IN_INCH / (4 * POINTS_IN_INCH), rightLineExists));
739 :
740 0 : input->seek(1, WPX_SEEK_CUR);
741 0 : unsigned char bottomLineWidth = readU8(input);
742 0 : bool bottomLineExists = bottomLineWidth != 0;
743 0 : unsigned bottomColorReference = readU32(input);
744 0 : unsigned translatedBottomColorReference = translate2kColorReference(bottomColorReference);
745 : m_collector->addShapeLine(seqNum, Line(ColorReference(translatedBottomColorReference),
746 0 : translateLineWidth(bottomLineWidth) * EMUS_IN_INCH / (4 * POINTS_IN_INCH), bottomLineExists));
747 : }
748 : m_collector->addShapeLine(seqNum, Line(ColorReference(translatedLeftColorReference),
749 0 : translateLineWidth(leftLineWidth) * EMUS_IN_INCH / (4 * POINTS_IN_INCH), leftLineExists));
750 0 : }
751 :
752 0 : bool libmspub::MSPUBParser2k::parse()
753 : {
754 0 : WPXInputStream *contents = m_input->getDocumentOLEStream("Contents");
755 0 : if (!contents)
756 : {
757 : MSPUB_DEBUG_MSG(("Couldn't get contents stream.\n"));
758 0 : return false;
759 : }
760 0 : if (!parseContents(contents))
761 : {
762 : MSPUB_DEBUG_MSG(("Couldn't parse contents stream.\n"));
763 0 : delete contents;
764 0 : return false;
765 : }
766 0 : WPXInputStream *quill = m_input->getDocumentOLEStream("Quill/QuillSub/CONTENTS");
767 0 : if (!quill)
768 : {
769 : MSPUB_DEBUG_MSG(("Couldn't get quill stream.\n"));
770 0 : return false;
771 : }
772 0 : if (!parseQuill(quill))
773 : {
774 : MSPUB_DEBUG_MSG(("Couldn't parse quill stream.\n"));
775 0 : delete quill;
776 0 : return false;
777 : }
778 0 : return m_collector->go();
779 : }
780 :
781 0 : libmspub::PageType libmspub::MSPUBParser2k::getPageTypeBySeqNum(unsigned seqNum)
782 : {
783 0 : switch(seqNum)
784 : {
785 : case 0x116:
786 : case 0x108:
787 : case 0x10B:
788 : case 0x10D:
789 : case 0x119:
790 0 : return DUMMY_PAGE;
791 : case 0x109:
792 0 : return MASTER;
793 : default:
794 0 : return NORMAL;
795 : }
796 0 : }
797 :
798 : /* vim:set shiftwidth=2 softtabstop=2 expandtab: */
|