/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * This file is part of the libmspub project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MSPUBParser2k.h" #include #include #include #include #include "ColorReference.h" #include "Fill.h" #include "Line.h" #include "MSPUBCollector.h" #include "ShapeType.h" #include "libmspub_utils.h" namespace libmspub { namespace { class ChunkNestingGuard { public: ChunkNestingGuard(std::deque &chunks, const unsigned seqNum) : m_chunks(chunks) { m_chunks.push_front(seqNum); } ~ChunkNestingGuard() { m_chunks.pop_front(); } private: std::deque &m_chunks; }; } MSPUBParser2k::MSPUBParser2k(librevenge::RVNGInputStream *input, MSPUBCollector *collector) : MSPUBParser(input, collector), m_imageDataChunkIndices(), m_quillColorEntries(), m_chunkChildIndicesById(), m_chunksBeingRead() { } unsigned MSPUBParser2k::getColorIndexByQuillEntry(unsigned entry) { unsigned translation = translate2kColorReference(entry); std::vector::const_iterator i_entry = std::find(m_quillColorEntries.begin(), m_quillColorEntries.end(), translation); if (i_entry == m_quillColorEntries.end()) { m_quillColorEntries.push_back(translation); m_collector->addTextColor(ColorReference(translation)); return m_quillColorEntries.size() - 1; } return i_entry - m_quillColorEntries.begin(); } MSPUBParser2k::~MSPUBParser2k() { } // Takes a line width specifier in Pub2k format and translates it into quarter points unsigned short translateLineWidth(unsigned char lineWidth) { if (lineWidth == 0x81) { return 0; } else if (lineWidth > 0x81) { return ((lineWidth - 0x81) / 3) * 4 + ((lineWidth - 0x81) % 3) + 1; } else { return lineWidth * 4; } } Color MSPUBParser2k::getColorBy2kHex(unsigned hex) { switch ((hex >> 24) & 0xFF) { case 0x80: case 0x00: return getColorBy2kIndex(hex & 0xFF); case 0x90: case 0x20: return Color(hex & 0xFF, (hex >> 8) & 0xFF, (hex >> 16) & 0xFF); default: return Color(); } } Color MSPUBParser2k::getColorBy2kIndex(unsigned char index) { switch (index) { case 0x00: return Color(0, 0, 0); case 0x01: return Color(0xff, 0xff, 0xff); case 0x02: return Color(0xff, 0, 0); case 0x03: return Color(0, 0xff, 0); case 0x04: return Color(0, 0, 0xff); case 0x05: return Color(0xff, 0xff, 0); case 0x06: return Color(0, 0xff, 0xff); case 0x07: return Color(0xff, 0, 0xff); case 0x08: return Color(128, 128, 128); case 0x09: return Color(192, 192, 192); case 0x0A: return Color(128, 0, 0); case 0x0B: return Color(0, 128, 0); case 0x0C: return Color(0, 0, 128); case 0x0D: return Color(128, 128, 0); case 0x0E: return Color(0, 128, 128); case 0x0F: return Color(128, 0, 128); case 0x10: return Color(255, 153, 51); case 0x11: return Color(51, 0, 51); case 0x12: return Color(0, 0, 153); case 0x13: return Color(0, 153, 0); case 0x14: return Color(153, 153, 0); case 0x15: return Color(204, 102, 0); case 0x16: return Color(153, 0, 0); case 0x17: return Color(204, 153, 204); case 0x18: return Color(102, 102, 255); case 0x19: return Color(102, 255, 102); case 0x1A: return Color(255, 255, 153); case 0x1B: return Color(255, 204, 153); case 0x1C: return Color(255, 102, 102); case 0x1D: return Color(255, 153, 0); case 0x1E: return Color(0, 102, 255); case 0x1F: return Color(255, 204, 0); case 0x20: return Color(153, 0, 51); case 0x21: return Color(102, 51, 0); case 0x22: return Color(66, 66, 66); case 0x23: return Color(255, 153, 102); case 0x24: return Color(153, 51, 0); case 0x25: return Color(255, 102, 0); case 0x26: return Color(51, 51, 0); case 0x27: return Color(153, 204, 0); case 0x28: return Color(255, 255, 153); case 0x29: return Color(0, 51, 0); case 0x2A: return Color(51, 153, 102); case 0x2B: return Color(204, 255, 204); case 0x2C: return Color(0, 51, 102); case 0x2D: return Color(51, 204, 204); case 0x2E: return Color(204, 255, 255); case 0x2F: return Color(51, 102, 255); case 0x30: return Color(0, 204, 255); case 0x31: return Color(153, 204, 255); case 0x32: return Color(51, 51, 153); case 0x33: return Color(102, 102, 153); case 0x34: return Color(153, 51, 102); case 0x35: return Color(204, 153, 255); case 0x36: return Color(51, 51, 51); case 0x37: return Color(150, 150, 150); default: return Color(); } } // takes a color reference in 2k format and translates it into 2k2 format that collector understands. unsigned MSPUBParser2k::translate2kColorReference(unsigned ref2k) { switch ((ref2k >> 24) & 0xFF) { case 0xC0: //index into user palette case 0xE0: return (ref2k & 0xFF) | (0x08 << 24); default: { Color c = getColorBy2kHex(ref2k); return (c.r) | (c.g << 8) | (c.b << 16); } } } //FIXME: Valek found different values; what does this depend on? ShapeType MSPUBParser2k::getShapeType(unsigned char shapeSpecifier) { switch (shapeSpecifier) { case 0x1: return RIGHT_TRIANGLE; /* case 0x2: return GENERAL_TRIANGLE; */ case 0x3: return UP_ARROW; case 0x4: return STAR; case 0x5: return HEART; case 0x6: return ISOCELES_TRIANGLE; case 0x7: return PARALLELOGRAM; /* case 0x8: return TILTED_TRAPEZOID; */ case 0x9: return UP_DOWN_ARROW; case 0xA: return SEAL_16; case 0xB: return WAVE; case 0xC: return DIAMOND; case 0xD: return TRAPEZOID; case 0xE: return CHEVRON; case 0xF: return BENT_ARROW; case 0x10: return SEAL_24; /* case 0x11: return PIE; */ case 0x12: return PENTAGON; case 0x13: return HOME_PLATE; /* case 0x14: return NOTCHED_TRIANGLE; */ case 0x15: return U_TURN_ARROW; case 0x16: return IRREGULAR_SEAL_1; /* case 0x17: return CHORD; */ case 0x18: return HEXAGON; /* case 0x19: return NOTCHED_RECTANGLE; */ /* case 0x1A: return W_SHAPE; //This is a bizarre shape; the number of vertices depends on one of the adjust values. //We need to refactor our escher shape drawing routines before we can handle it. */ /* case 0x1B: return ROUND_RECT_CALLOUT_2K; //This is not quite the same as the round rect. found in 2k2 and above. */ case 0x1C: return IRREGULAR_SEAL_2; case 0x1D: return BLOCK_ARC; case 0x1E: return OCTAGON; case 0x1F: return PLUS; case 0x20: return CUBE; /* case 0x21: return OVAL_CALLOUT_2K; //Not sure yet if this is the same as the 2k2 one. */ case 0x22: return LIGHTNING_BOLT; default: return UNKNOWN_SHAPE; } } void MSPUBParser2k::parseContentsTextIfNecessary(librevenge::RVNGInputStream *) { } bool MSPUBParser2k::parseContents(librevenge::RVNGInputStream *input) { parseContentsTextIfNecessary(input); input->seek(0x16, librevenge::RVNG_SEEK_SET); unsigned trailerOffset = readU32(input); input->seek(trailerOffset, librevenge::RVNG_SEEK_SET); unsigned numBlocks = readU16(input); unsigned chunkOffset = 0; for (unsigned i = 0; i < numBlocks; ++i) { input->seek(input->tell() + 2, librevenge::RVNG_SEEK_SET); unsigned short id = readU16(input); unsigned short parent = readU16(input); chunkOffset = readU32(input); if (m_contentChunks.size() > 0) { m_contentChunks.back().end = chunkOffset; } unsigned offset = input->tell(); input->seek(chunkOffset, librevenge::RVNG_SEEK_SET); unsigned short typeMarker = readU16(input); input->seek(offset, librevenge::RVNG_SEEK_SET); switch (typeMarker) { case 0x0014: MSPUB_DEBUG_MSG(("Found page chunk of id 0x%x and parent 0x%x\n", id, parent)); m_contentChunks.push_back(ContentChunkReference(PAGE, chunkOffset, 0, id, parent)); m_pageChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1)); break; case 0x0015: MSPUB_DEBUG_MSG(("Found document chunk of id 0x%x and parent 0x%x\n", id, parent)); m_contentChunks.push_back(ContentChunkReference(DOCUMENT, chunkOffset, 0, id, parent)); m_documentChunkIndex = unsigned(m_contentChunks.size() - 1); m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1)); break; case 0x0002: MSPUB_DEBUG_MSG(("Found image_2k chunk of id 0x%x and parent 0x%x\n", id, parent)); m_contentChunks.push_back(ContentChunkReference(IMAGE_2K, chunkOffset, 0, id, parent)); m_shapeChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1)); break; case 0x0021: MSPUB_DEBUG_MSG(("Found image_2k_data chunk of id 0x%x and parent 0x%x\n", id, parent)); m_contentChunks.push_back(ContentChunkReference(IMAGE_2K_DATA, chunkOffset, 0, id, parent)); m_imageDataChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1)); break; case 0x0000: case 0x0004: case 0x0005: case 0x0006: case 0x0007: case 0x0008: MSPUB_DEBUG_MSG(("Found shape chunk of id 0x%x and parent 0x%x\n", id, parent)); m_contentChunks.push_back(ContentChunkReference(SHAPE, chunkOffset, 0, id, parent)); m_shapeChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1)); break; case 0x0047: MSPUB_DEBUG_MSG(("Found palette chunk of id 0x%x and parent 0x%x\n", id, parent)); m_contentChunks.push_back(ContentChunkReference(PALETTE, chunkOffset, 0, id, parent)); m_paletteChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1)); break; case 0x000F: MSPUB_DEBUG_MSG(("Found group chunk of id 0x%x and parent 0x%x\n", id, parent)); m_contentChunks.push_back(ContentChunkReference(GROUP, chunkOffset, 0, id, parent)); m_shapeChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1)); break; default: MSPUB_DEBUG_MSG(("Found unknown chunk of id 0x%x and parent 0x%x\n", id, parent)); m_contentChunks.push_back(ContentChunkReference(UNKNOWN_CHUNK, chunkOffset, 0, id, parent)); m_unknownChunkIndices.push_back(unsigned(m_contentChunks.size() - 1)); m_chunkChildIndicesById[parent].push_back(unsigned(m_contentChunks.size() - 1)); break; } } if (m_contentChunks.size() > 0) { m_contentChunks.back().end = chunkOffset; } if (!parseDocument(input)) { MSPUB_DEBUG_MSG(("No document chunk found.\n")); return false; } for (unsigned int paletteChunkIndex : m_paletteChunkIndices) { const ContentChunkReference &chunk = m_contentChunks.at(paletteChunkIndex); input->seek(chunk.offset, librevenge::RVNG_SEEK_SET); input->seek(0xA0, librevenge::RVNG_SEEK_CUR); for (unsigned j = 0; j < 8; ++j) { unsigned hex = readU32(input); Color color = getColorBy2kHex(hex); m_collector->addPaletteColor(color); } } for (unsigned int imageDataChunkIndex : m_imageDataChunkIndices) { const ContentChunkReference &chunk = m_contentChunks.at(imageDataChunkIndex); input->seek(chunk.offset + 4, librevenge::RVNG_SEEK_SET); unsigned toRead = readU32(input); librevenge::RVNGBinaryData img; while (toRead > 0 && stillReading(input, (unsigned long)-1)) { unsigned long howManyRead = 0; const unsigned char *buf = input->read(toRead, howManyRead); img.append(buf, howManyRead); toRead -= howManyRead; } m_collector->addImage(++m_lastAddedImage, WMF, img); } for (unsigned int shapeChunkIndex : m_shapeChunkIndices) { parse2kShapeChunk(m_contentChunks.at(shapeChunkIndex), input); } return true; } bool MSPUBParser2k::parseDocument(librevenge::RVNGInputStream *input) { if (bool(m_documentChunkIndex)) { input->seek(m_contentChunks[m_documentChunkIndex.get()].offset, librevenge::RVNG_SEEK_SET); input->seek(0x14, librevenge::RVNG_SEEK_CUR); unsigned width = readU32(input); unsigned height = readU32(input); m_collector->setWidthInEmu(width); m_collector->setHeightInEmu(height); return true; } return false; } void MSPUBParser2k::parseShapeRotation(librevenge::RVNGInputStream *input, bool isGroup, bool isLine, unsigned seqNum, unsigned chunkOffset) { input->seek(chunkOffset + 4, librevenge::RVNG_SEEK_SET); // shape transforms are NOT compounded with group transforms. They are equal to what they would be // if the shape were not part of a group at all. This is different from how MSPUBCollector handles rotations; // we work around the issue by simply not setting the rotation of any group, thereby letting it default to zero. // // Furthermore, line rotations are redundant and need to be treated as zero. unsigned short counterRotationInDegreeTenths = readU16(input); if (!isGroup && !isLine) { m_collector->setShapeRotation(seqNum, 360. - double(counterRotationInDegreeTenths) / 10); } } bool MSPUBParser2k::parse2kShapeChunk(const ContentChunkReference &chunk, librevenge::RVNGInputStream *input, boost::optional pageSeqNum, bool topLevelCall) { if (find(m_chunksBeingRead.begin(), m_chunksBeingRead.end(), chunk.seqNum) != m_chunksBeingRead.end()) { MSPUB_DEBUG_MSG(("chunk %u is nested in itself", chunk.seqNum)); return false; } const ChunkNestingGuard guard(m_chunksBeingRead, chunk.seqNum); unsigned page = pageSeqNum.get_value_or(chunk.parentSeqNum); input->seek(chunk.offset, librevenge::RVNG_SEEK_SET); if (topLevelCall) { // ignore non top level shapes int i_page = -1; for (unsigned int pageIndex : m_pageChunkIndices) { if (m_contentChunks.at(pageIndex).seqNum == chunk.parentSeqNum) { i_page = pageIndex; break; } } if (i_page == -1) { return false; } if (getPageTypeBySeqNum(m_contentChunks[i_page].seqNum) != NORMAL) { return false; } // if the parent of this is a page and hasn't yet been added, then add it. if (!m_collector->hasPage(chunk.parentSeqNum)) { m_collector->addPage(chunk.parentSeqNum); } } m_collector->setShapePage(chunk.seqNum, page); m_collector->setShapeBorderPosition(chunk.seqNum, INSIDE_SHAPE); // This appears to be the only possibility for MSPUB2k bool isImage = false; bool isRectangle = false; bool isGroup = false; bool isLine = false; unsigned flagsOffset(0); // ? why was this changed from boost::optional ? parseShapeType(input, chunk.seqNum, chunk.offset, isGroup, isLine, isImage, isRectangle, flagsOffset); parseShapeRotation(input, isGroup, isLine, chunk.seqNum, chunk.offset); parseShapeCoordinates(input, chunk.seqNum, chunk.offset); parseShapeFlips(input, flagsOffset, chunk.seqNum, chunk.offset); if (isGroup) { return parseGroup(input, chunk.seqNum, page); } if (isImage) { assignShapeImgIndex(chunk.seqNum); } else { parseShapeFill(input, chunk.seqNum, chunk.offset); } parseShapeLine(input, isRectangle, chunk.offset, chunk.seqNum); m_collector->setShapeOrder(chunk.seqNum); return true; } unsigned MSPUBParser2k::getShapeFillTypeOffset() const { return 0x2A; } unsigned MSPUBParser2k::getShapeFillColorOffset() const { return 0x22; } void MSPUBParser2k::parseShapeFill(librevenge::RVNGInputStream *input, unsigned seqNum, unsigned chunkOffset) { input->seek(chunkOffset + getShapeFillTypeOffset(), librevenge::RVNG_SEEK_SET); unsigned char fillType = readU8(input); if (fillType == 2) // other types are gradients and patterns which are not implemented yet. 0 is no fill. { input->seek(chunkOffset + getShapeFillColorOffset(), librevenge::RVNG_SEEK_SET); unsigned fillColorReference = readU32(input); unsigned translatedFillColorReference = translate2kColorReference(fillColorReference); m_collector->setShapeFill(seqNum, std::shared_ptr(new SolidFill(ColorReference(translatedFillColorReference), 1, m_collector)), false); } } bool MSPUBParser2k::parseGroup(librevenge::RVNGInputStream *input, unsigned seqNum, unsigned page) { bool retVal = true; m_collector->beginGroup(); m_collector->setCurrentGroupSeqNum(seqNum); const std::map >::const_iterator it = m_chunkChildIndicesById.find(seqNum); if (it != m_chunkChildIndicesById.end()) { const std::vector &chunkChildIndices = it->second; for (unsigned int chunkChildIndex : chunkChildIndices) { const ContentChunkReference &childChunk = m_contentChunks.at(chunkChildIndex); if (childChunk.type == SHAPE || childChunk.type == GROUP) { retVal = retVal && parse2kShapeChunk(childChunk, input, page, false); } } } m_collector->endGroup(); return retVal; } void MSPUBParser2k::assignShapeImgIndex(unsigned seqNum) { int i_dataIndex = -1; for (unsigned j = 0; j < m_imageDataChunkIndices.size(); ++j) { if (m_contentChunks.at(m_imageDataChunkIndices[j]).parentSeqNum == seqNum) { i_dataIndex = j; break; } } if (i_dataIndex >= 0) { m_collector->setShapeImgIndex(seqNum, i_dataIndex + 1); } } void MSPUBParser2k::parseShapeCoordinates(librevenge::RVNGInputStream *input, unsigned seqNum, unsigned chunkOffset) { input->seek(chunkOffset + 6, librevenge::RVNG_SEEK_SET); int xs = translateCoordinateIfNecessary(readS32(input)); int ys = translateCoordinateIfNecessary(readS32(input)); int xe = translateCoordinateIfNecessary(readS32(input)); int ye = translateCoordinateIfNecessary(readS32(input)); m_collector->setShapeCoordinatesInEmu(seqNum, xs, ys, xe, ye); } int MSPUBParser2k::translateCoordinateIfNecessary(int coordinate) const { return coordinate; } void MSPUBParser2k::parseShapeFlips(librevenge::RVNGInputStream *input, unsigned flagsOffset, unsigned seqNum, unsigned chunkOffset) { if (flagsOffset) { input->seek(chunkOffset + flagsOffset, librevenge::RVNG_SEEK_SET); unsigned char flags = readU8(input); bool flipV = flags & 0x1; bool flipH = flags & (0x2 | 0x10); // FIXME: this is a guess m_collector->setShapeFlip(seqNum, flipV, flipH); } } void MSPUBParser2k::parseShapeType(librevenge::RVNGInputStream *input, unsigned seqNum, unsigned chunkOffset, bool &isGroup, bool &isLine, bool &isImage, bool &isRectangle, unsigned &flagsOffset) { input->seek(chunkOffset, librevenge::RVNG_SEEK_SET); unsigned short typeMarker = readU16(input); if (typeMarker == 0x000f) { isGroup = true; } else if (typeMarker == 0x0004) { isLine = true; flagsOffset = 0x41; m_collector->setShapeType(seqNum, LINE); } else if (typeMarker == 0x0002) { isImage = true; m_collector->setShapeType(seqNum, RECTANGLE); isRectangle = true; } else if (typeMarker == 0x0005) { m_collector->setShapeType(seqNum, RECTANGLE); isRectangle = true; } else if (typeMarker == 0x0006) { input->seek(chunkOffset + 0x31, librevenge::RVNG_SEEK_SET); ShapeType shapeType = getShapeType(readU8(input)); flagsOffset = 0x33; if (shapeType != UNKNOWN_SHAPE) { m_collector->setShapeType(seqNum, shapeType); } } else if (typeMarker == 0x0007) { m_collector->setShapeType(seqNum, ELLIPSE); } else if (typeMarker == getTextMarker()) { m_collector->setShapeType(seqNum, RECTANGLE); isRectangle = true; input->seek(chunkOffset + getTextIdOffset(), librevenge::RVNG_SEEK_SET); unsigned txtId = readU16(input); m_collector->addTextShape(txtId, seqNum); } } unsigned MSPUBParser2k::getTextIdOffset() const { return 0x58; } unsigned short MSPUBParser2k::getTextMarker() const { return 0x0008; } unsigned MSPUBParser2k::getFirstLineOffset() const { return 0x2C; } unsigned MSPUBParser2k::getSecondLineOffset() const { return 0x35; } void MSPUBParser2k::parseShapeLine(librevenge::RVNGInputStream *input, bool isRectangle, unsigned offset, unsigned seqNum) { input->seek(offset + getFirstLineOffset(), librevenge::RVNG_SEEK_SET); unsigned short leftLineWidth = readU8(input); bool leftLineExists = leftLineWidth != 0; unsigned leftColorReference = readU32(input); unsigned translatedLeftColorReference = translate2kColorReference(leftColorReference); if (isRectangle) { input->seek(offset + getSecondLineOffset(), librevenge::RVNG_SEEK_SET); unsigned char topLineWidth = readU8(input); bool topLineExists = topLineWidth != 0; unsigned topColorReference = readU32(input); unsigned translatedTopColorReference = translate2kColorReference(topColorReference); m_collector->addShapeLine(seqNum, Line(ColorReference(translatedTopColorReference), translateLineWidth(topLineWidth) * EMUS_IN_INCH / (4 * POINTS_IN_INCH), topLineExists)); input->seek(1, librevenge::RVNG_SEEK_CUR); unsigned char rightLineWidth = readU8(input); bool rightLineExists = rightLineWidth != 0; unsigned rightColorReference = readU32(input); unsigned translatedRightColorReference = translate2kColorReference(rightColorReference); m_collector->addShapeLine(seqNum, Line(ColorReference(translatedRightColorReference), translateLineWidth(rightLineWidth) * EMUS_IN_INCH / (4 * POINTS_IN_INCH), rightLineExists)); input->seek(1, librevenge::RVNG_SEEK_CUR); unsigned char bottomLineWidth = readU8(input); bool bottomLineExists = bottomLineWidth != 0; unsigned bottomColorReference = readU32(input); unsigned translatedBottomColorReference = translate2kColorReference(bottomColorReference); m_collector->addShapeLine(seqNum, Line(ColorReference(translatedBottomColorReference), translateLineWidth(bottomLineWidth) * EMUS_IN_INCH / (4 * POINTS_IN_INCH), bottomLineExists)); } m_collector->addShapeLine(seqNum, Line(ColorReference(translatedLeftColorReference), translateLineWidth(leftLineWidth) * EMUS_IN_INCH / (4 * POINTS_IN_INCH), leftLineExists)); } bool MSPUBParser2k::parse() { std::unique_ptr contents(m_input->getSubStreamByName("Contents")); if (!contents) { MSPUB_DEBUG_MSG(("Couldn't get contents stream.\n")); return false; } if (!parseContents(contents.get())) { MSPUB_DEBUG_MSG(("Couldn't parse contents stream.\n")); return false; } std::unique_ptr quill(m_input->getSubStreamByName("Quill/QuillSub/CONTENTS")); if (!quill) { MSPUB_DEBUG_MSG(("Couldn't get quill stream.\n")); return false; } if (!parseQuill(quill.get())) { MSPUB_DEBUG_MSG(("Couldn't parse quill stream.\n")); return false; } return m_collector->go(); } PageType MSPUBParser2k::getPageTypeBySeqNum(unsigned seqNum) { switch (seqNum) { case 0x116: case 0x108: case 0x10B: case 0x10D: case 0x119: return DUMMY_PAGE; case 0x109: return MASTER; default: return NORMAL; } } } /* vim:set shiftwidth=2 softtabstop=2 expandtab: */