diff options
Diffstat (limited to 'fbreader/src/formats/pdb')
41 files changed, 4958 insertions, 0 deletions
diff --git a/fbreader/src/formats/pdb/BitReader.cpp b/fbreader/src/formats/pdb/BitReader.cpp new file mode 100644 index 0000000..551aaf3 --- /dev/null +++ b/fbreader/src/formats/pdb/BitReader.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <cstring> +#include <string> + +#include "BitReader.h" + +BitReader::BitReader(const unsigned char* data, std::size_t size) : myOffset(0), myLength(size * 8) { + myData = new unsigned char[size + 4]; + std::memcpy(myData, data, size); + std::memset(myData + size, 0x00, 4); +} + +BitReader::~BitReader() { + delete[] myData; +} + +unsigned long long BitReader::peek(std::size_t n) { + if (n > 32) { + return 0; + } + unsigned long long r = 0; + std::size_t g = 0; + while (g < n) { + r = (r << 8) | myData[(myOffset + g) >> 3]; + g = g + 8 - ((myOffset+g) & 7); + } + unsigned long long mask = 1; + mask = (mask << n) - 1; + return (r >> (g - n)) & mask; +} + +bool BitReader::eat(std::size_t n) { + myOffset += n; + return myOffset <= myLength; +} + +std::size_t BitReader::left() const { + return myLength - myOffset; +} diff --git a/fbreader/src/formats/pdb/BitReader.h b/fbreader/src/formats/pdb/BitReader.h new file mode 100644 index 0000000..a8a3d2d --- /dev/null +++ b/fbreader/src/formats/pdb/BitReader.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __BITREADER_H__ +#define __BITREADER_H__ + +class BitReader { + +public: + BitReader(const unsigned char* data, std::size_t size); + ~BitReader(); + + unsigned long long peek(std::size_t n); + bool eat(std::size_t n); + std::size_t left() const; + +private: + unsigned char* myData; + std::size_t myOffset; + std::size_t myLength; +}; + +#endif //__BITREADER_H__ diff --git a/fbreader/src/formats/pdb/DocDecompressor.cpp b/fbreader/src/formats/pdb/DocDecompressor.cpp new file mode 100644 index 0000000..9175bc9 --- /dev/null +++ b/fbreader/src/formats/pdb/DocDecompressor.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <cstring> + +#include <ZLInputStream.h> + +#include "DocDecompressor.h" + +static unsigned char TOKEN_CODE[256] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + +std::size_t DocDecompressor::decompress(ZLInputStream &stream, char *targetBuffer, std::size_t compressedSize, std::size_t maxUncompressedSize) { + const unsigned char *sourceBuffer = new unsigned char[compressedSize]; + const unsigned char *sourceBufferEnd = sourceBuffer + compressedSize; + const unsigned char *sourcePtr = sourceBuffer; + + unsigned char *targetBufferEnd = (unsigned char*)targetBuffer + maxUncompressedSize; + unsigned char *targetPtr = (unsigned char*)targetBuffer; + + if (stream.read((char*)sourceBuffer, compressedSize) == compressedSize) { + unsigned char token; + unsigned short copyLength, N, shift; + unsigned char *shifted; + + while ((sourcePtr < sourceBufferEnd) && (targetPtr < targetBufferEnd)) { + token = *(sourcePtr++); + switch (TOKEN_CODE[token]) { + case 0: + *(targetPtr++) = token; + break; + case 1: + if ((sourcePtr + token > sourceBufferEnd) || (targetPtr + token > targetBufferEnd)) { + goto endOfLoop; + } + std::memcpy(targetPtr, sourcePtr, token); + sourcePtr += token; + targetPtr += token; + break; + case 2: + if (targetPtr + 2 > targetBufferEnd) { + goto endOfLoop; + } + *(targetPtr++) = ' '; + *(targetPtr++) = token ^ 0x80; + break; + case 3: + if (sourcePtr + 1 > sourceBufferEnd) { + goto endOfLoop; + } + N = 256 * token + *(sourcePtr++); + copyLength = (N & 7) + 3; + if (targetPtr + copyLength > targetBufferEnd) { + goto endOfLoop; + } + shift = (N & 0x3fff) / 8; + shifted = targetPtr - shift; + if ((char*)shifted >= targetBuffer) { + for (short i = 0; i < copyLength; i++) { + *(targetPtr++) = *(shifted++); + } + } + break; + } + } + } +endOfLoop: + + delete[] sourceBuffer; + return targetPtr - (unsigned char*)targetBuffer; +} diff --git a/fbreader/src/formats/pdb/DocDecompressor.h b/fbreader/src/formats/pdb/DocDecompressor.h new file mode 100644 index 0000000..820bb0a --- /dev/null +++ b/fbreader/src/formats/pdb/DocDecompressor.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __DOCDECOMPRESSOR_H__ +#define __DOCDECOMPRESSOR_H__ + +#include <string> + +class ZLInputStream; + +class DocDecompressor { + +public: + DocDecompressor() {} + ~DocDecompressor() {} + + std::size_t decompress(ZLInputStream &stream, char *buffer, std::size_t compressedSize, std::size_t maxUncompressedSize); +}; + +#endif /* __DOCDECOMPRESSOR_H__ */ diff --git a/fbreader/src/formats/pdb/EReaderPlugin.cpp b/fbreader/src/formats/pdb/EReaderPlugin.cpp new file mode 100644 index 0000000..8420c7f --- /dev/null +++ b/fbreader/src/formats/pdb/EReaderPlugin.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> +#include <ZLInputStream.h> +#include <ZLEncodingConverter.h> +#include <ZLStringUtil.h> +#include <ZLLanguageUtil.h> +#include <ZLFileImage.h> + +#include "PdbPlugin.h" +#include "EReaderStream.h" +#include "PmlBookReader.h" + +#include "../../library/Book.h" + +bool EReaderPlugin::providesMetaInfo() const { + return true; +} + +bool EReaderPlugin::acceptsFile(const ZLFile &file) const { + return PdbPlugin::fileType(file) == "PNRdPPrs"; +} + +void EReaderPlugin::readDocumentInternal(const ZLFile &file, BookModel &model, const PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const { + if (!stream.open()) { + //TODO maybe anything else opens stream + return; + } + BookReader bookReader(model); + PmlBookReader pmlBookReader(bookReader, format, encoding); + bookReader.setMainTextModel(); + pmlBookReader.readDocument(stream); + EReaderStream &estream = (EReaderStream&)stream; + const std::map<std::string, EReaderStream::ImageInfo>& imageIds = estream.images(); + for(std::map<std::string, EReaderStream::ImageInfo>::const_iterator it = imageIds.begin(); it != imageIds.end(); ++it) { + const std::string id = it->first; + bookReader.addImage(id, new ZLFileImage(ZLFile(file.path(), it->second.Type), it->second.Offset, it->second.Size)); + } + const std::map<std::string, unsigned short>& footnoteIds = estream.footnotes(); + for(std::map<std::string, unsigned short>::const_iterator it = footnoteIds.begin(); it != footnoteIds.end(); ++it) { + const std::string id = it->first; + if (estream.switchStreamDestination(EReaderStream::FOOTNOTE, id)) { + bookReader.setFootnoteTextModel(id); + bookReader.addHyperlinkLabel(id); + pmlBookReader.readDocument(estream); + } + } + stream.close(); +} + +shared_ptr<ZLInputStream> EReaderPlugin::createStream(const ZLFile &file) const { + return new EReaderStream(file); +} + +const std::string &EReaderPlugin::tryOpen(const ZLFile &file) const { + EReaderStream stream(file); + stream.open(); + return stream.error(); +} + +bool EReaderPlugin::readMetaInfo(Book &book) const { + shared_ptr<ZLInputStream> stream = book.file().inputStream(); + if (stream.isNull() || ! stream->open()) { + return false; + } + PdbHeader header; + if (!header.read(stream)) { + return false; + } + stream->seek(header.Offsets[0] + 46, true); + unsigned short metaInfoOffset; + PdbUtil::readUnsignedShort(*stream, metaInfoOffset); + if (metaInfoOffset == 0 || metaInfoOffset >= header.Offsets.size()) { + return false; + } + std::size_t currentOffset = header.Offsets[metaInfoOffset]; + std::size_t nextOffset = + (metaInfoOffset + 1 < (unsigned short)header.Offsets.size()) ? + header.Offsets[metaInfoOffset + 1] : stream->sizeOfOpened(); + if (nextOffset <= currentOffset) { + return false; + } + std::size_t length = nextOffset - currentOffset; + + char* metaInfoBuffer = new char[length]; + stream->seek(currentOffset, true); + stream->read(metaInfoBuffer, length); + std::string metaInfoStr(metaInfoBuffer, length); + delete[] metaInfoBuffer; + + std::string metaInfoData[5]; // Title; Author; Rights; Publisher; isbn; + for (std::size_t i = 0; i < 5; ++i) { + const std::size_t index = metaInfoStr.find('\0'); + metaInfoData[i] = metaInfoStr.substr(0,index); + metaInfoStr = metaInfoStr.substr(index + 1); + } + + if (!metaInfoData[0].empty()) { + book.setTitle(metaInfoData[0]); + } + + if (!metaInfoData[1].empty()) { + book.addAuthor(metaInfoData[1]); + } + + stream->close(); + return SimplePdbPlugin::readMetaInfo(book); +} diff --git a/fbreader/src/formats/pdb/EReaderStream.cpp b/fbreader/src/formats/pdb/EReaderStream.cpp new file mode 100644 index 0000000..9775773 --- /dev/null +++ b/fbreader/src/formats/pdb/EReaderStream.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <algorithm> +#include <cctype> + +#include <ZLFile.h> +#include <ZLResource.h> +#include <ZLZDecompressor.h> + +#include "EReaderStream.h" +#include "DocDecompressor.h" + + +EReaderStream::EReaderStream(const ZLFile &file) : PalmDocLikeStream(file) { + myDestination = TEXT; +} + +EReaderStream::~EReaderStream() { + close(); +} + +bool EReaderStream::switchStreamDestination(StreamDestination destination, const std::string& id) { + bool result = true; + switch(destination) { + case TEXT: + myDestination = TEXT; + myRecordIndex = 1; + break; + case FOOTNOTE: + std::map<std::string, unsigned short>::const_iterator footnoteIt = myFootnotes.find(id); + if (footnoteIt != myFootnotes.end()) { + myDestination = FOOTNOTE; + myRecordIndex = footnoteIt->second; + } else { + result = false; + } + break; + } + return result; +} + +bool EReaderStream::fillBuffer() { + if (myDestination == TEXT) { + return PalmDocLikeStream::fillBuffer(); + } else { + while (myBufferOffset == myBufferLength) { + if (!processRecord()) { + return false; + } + } + return true; + } +} + +bool EReaderStream::processRecord() { + const std::size_t currentOffset = recordOffset(myRecordIndex); + if (currentOffset < myBase->offset()) { + return false; + } + myBase->seek(currentOffset, true); + const std::size_t nextOffset = recordOffset(myRecordIndex + 1); + if (nextOffset < currentOffset) { + return false; + } + + unsigned short myCompressedSize = nextOffset - currentOffset; + + switch (myCompressionVersion) { + case 10: // Inflate compression + myBase->seek(2, false); + myBufferLength = ZLZDecompressor(myCompressedSize - 2).decompress(*myBase, myBuffer, myMaxRecordSize); + break; + case 2: // PalmDoc compression + myBufferLength = DocDecompressor().decompress(*myBase, myBuffer, myCompressedSize, myMaxRecordSize); + break; + } + clearBuffer('\0'); + myBufferOffset = 0; + return true; +} + +bool EReaderStream::processZeroRecord() { + // Use it with offset presetting to zero record offset value + PdbUtil::readUnsignedShort(*myBase, myCompressionVersion); // myBase offset: ^ + 2 + if (myCompressionVersion > 255) { + myErrorCode = ERROR_ENCRYPTION; + return false; + } else { + switch (myCompressionVersion) { + case 2: + case 10: + break; + default: + myErrorCode = ERROR_COMPRESSION; + return false; + } + } + myBase->seek(10, false); // myBase offset: ^ + 12 + PdbUtil::readUnsignedShort(*myBase, myNonTextOffset); // myBase offset: ^ + 14 + PdbUtil::readUnsignedShort(*myBase, myNonTextOffsetReserved); // myBase offset: ^ + 16 + myBase->seek(12, false); // myBase offset: ^ + 28 + PdbUtil::readUnsignedShort(*myBase, myFootnoteRecords); // myBase offset: ^ + 30 + PdbUtil::readUnsignedShort(*myBase, mySidebarRecords); // myBase offset: ^ + 32 + PdbUtil::readUnsignedShort(*myBase, myBookmarksOffset); // myBase offset: ^ + 34 + myBase->seek(2, false); // myBase offset: ^ + 36 + PdbUtil::readUnsignedShort(*myBase, myNonTextOffsetExtraReserved); // myBase offset: ^ + 38 + myBase->seek(2, false); // myBase offset: ^ + 40 + PdbUtil::readUnsignedShort(*myBase, myImagedataOffset); // myBase offset: ^ + 42 + PdbUtil::readUnsignedShort(*myBase, myImagedataOffsetReserved); // myBase offset: ^ + 44 + PdbUtil::readUnsignedShort(*myBase, myMetadataOffset); // myBase offset: ^ + 46 + PdbUtil::readUnsignedShort(*myBase, myMetadataOffsetReserved); // myBase offset: ^ + 48 + PdbUtil::readUnsignedShort(*myBase, myFootnoteOffset); // myBase offset: ^ + 50 + PdbUtil::readUnsignedShort(*myBase, mySidebarOffset); // myBase offset: ^ + 52 + PdbUtil::readUnsignedShort(*myBase, myLastdataOffset); // myBase offset: ^ + 54 + + unsigned short endSectionIndex = header().Offsets.size(); + myMaxRecordIndex = std::min((unsigned short) (myNonTextOffset - 1), (unsigned short) (endSectionIndex - 1)); + + myMaxRecordSize = 65535; // Maximum size of addressable space in PalmOS + // not more than 8192 bytes happens in the tested examples + + if (myFootnoteRecords) { + bool isSuccess = processFootnoteIdsRecord(); + if (!isSuccess) { + //TODO take in account returned bool value + //false if wrong footnotes amount anounced in zero record + //or corrupted or wrong footnote ids record + } + } + + if (myImagedataOffset != myMetadataOffset) { + bool isSuccess = processImageHeaders(); + if (!isSuccess) { + //TODO take in account returned bool value + //false if one of image record is corrupted + } + } + + myBase->seek(header().Offsets[1], true); + + /* + std::cerr << "EReaderStream::processZeroRecord():\n"; + std::cerr << "PDB header indentificator : " << header().Id << "\n"; + std::cerr << "PDB file system: sizeof opened : " << myBaseSize << "\n"; + std::cerr << "PDB header/record[0] max index : " << myMaxRecordIndex << "\n"; + std::cerr << "PDB record[0][0..2] compression : " << myCompressionVersion << "\n"; + std::cerr << "EReader record[0] myNonTextOffset : " << myNonTextOffset << std::endl; + std::cerr << "EReader record[0] myNonTextOffset2 : " << myNonTextOffsetReserved << std::endl; + std::cerr << "EReader record[0] myFootnoteRecords : " << myFootnoteRecords << std::endl; + std::cerr << "EReader record[0] mySidebarRecords : " << mySidebarRecords << std::endl; + std::cerr << "EReader record[0] myBookmarksOffset : " << myBookmarksOffset << std::endl; + std::cerr << "EReader record[0] myNonTextOffset3 : " << myNonTextOffsetExtraReserved << std::endl; + std::cerr << "EReader record[0] myImagedataOffset : " << myImagedataOffset << std::endl; + std::cerr << "EReader record[0] myImagedataOffset2 : " << myImagedataOffsetReserved << std::endl; + std::cerr << "EReader record[0] myMetadataOffset : " << myMetadataOffset << std::endl; + std::cerr << "EReader record[0] myMetadataOffset2 : " << myMetadataOffsetReserved << std::endl; + std::cerr << "EReader record[0] myFootnoteOffset : " << myFootnoteOffset << std::endl; + std::cerr << "EReader record[0] mySidebarOffset : " << mySidebarOffset << std::endl; + std::cerr << "EReader record[0] myLastdataOffset : " << myLastdataOffset << std::endl; + std::cerr << "PDB header lastSectionIndex : " << endSectionIndex - 1 << "\n"; + */ + return true; +} + +void EReaderStream::clearBuffer(unsigned char symbol) { + myBufferLength = std::remove(myBuffer, myBuffer + myBufferLength, symbol) - myBuffer; +} + +bool EReaderStream::processFootnoteIdsRecord() { + char* footnoteIdBuffer = new char[myMaxRecordSize]; + myBase->seek(header().Offsets[myFootnoteOffset], true); + const std::size_t currentOffset = recordOffset(myFootnoteOffset); + const std::size_t nextOffset = recordOffset(myFootnoteOffset + 1); + const std::size_t length = nextOffset - currentOffset; + myBase->read(footnoteIdBuffer, length); + std::string footnoteIdStr(footnoteIdBuffer, length); + unsigned short footnoteIndex = myFootnoteOffset + 1; + while (!footnoteIdStr.empty() && (footnoteIndex < myLastdataOffset)) { + std::string id = findFootnoteId(footnoteIdStr); + if (!id.empty()) { + myFootnotes[id] = footnoteIndex; + ++footnoteIndex; + } + } + delete[] footnoteIdBuffer; + return (myFootnoteRecords - 1 == (unsigned short)myFootnotes.size()); +} + +std::string EReaderStream::findFootnoteId(std::string &footnoteIdStr) const { + std::string resultStr; + if (!footnoteIdStr.empty()) { + std::size_t counter = 0; + for (; counter < footnoteIdStr.length(); ++counter) { + if (std::isalnum(footnoteIdStr[counter])) { + break; + } + } + const std::size_t startIdIndex = counter; + for (; counter < footnoteIdStr.length(); ++counter) { + if (footnoteIdStr[counter] == '\0') { + break; + } + } + const std::size_t endIdIndex = counter; + resultStr = footnoteIdStr.substr(startIdIndex, endIdIndex - startIdIndex); + footnoteIdStr = footnoteIdStr.substr(endIdIndex); + } + return resultStr; +} + +const std::map<std::string, unsigned short>& EReaderStream::footnotes() const { + return myFootnotes; +} + +bool EReaderStream::processImageHeaders() { + unsigned short recordIndex = myImagedataOffset; + bool result = true; + myBase->seek(header().Offsets[recordIndex], true); + while (recordIndex < myMetadataOffset && recordIndex < myLastdataOffset) { + result = result && addImageInfo(recordIndex); + ++recordIndex; + } + return result; +} + +bool EReaderStream::addImageInfo(const unsigned short recordIndex) { + const std::size_t bufferLength = 128; + char *buffer = new char[bufferLength]; //TODO may be it's needed here more bytes + ImageInfo image; + const std::size_t currentOffset = recordOffset(recordIndex); + const std::size_t nextOffset = recordOffset(recordIndex + 1); + + myBase->read(buffer, bufferLength); + std::string header(buffer, bufferLength); + delete[] buffer; + + image.Offset = currentOffset + header.find("\x89PNG"); //TODO treat situation when there isn't PNG in first 128 bytes + image.Size = nextOffset - image.Offset; + const int endType = header.find(' '); + image.Type = ZLMimeType::get(header.substr(0, endType)); + header = header.substr(endType + 1); + const int endId = header.find('\0'); + const std::string id = header.substr(0, endId); + myBase->seek(nextOffset - currentOffset - bufferLength, false); + if (id.empty()) { + return false; + } + myImages[id] = image; + return true; +} + + +/*bool EReaderStream::hasExtraSections() const { + return false; + //return myMaxRecordIndex < header().Offsets.size() - 1; +}*/ + +EReaderStream::ImageInfo EReaderStream::imageLocation(const std::string& id) { + if (myImagedataOffset != myMetadataOffset && myImages.empty()) { + processImageHeaders(); + } + const std::map<std::string, ImageInfo>::const_iterator it = myImages.find(id); + if (it != myImages.end()) { + return it->second; + } else { + return ImageInfo(); + } +} + +const std::map<std::string, EReaderStream::ImageInfo>& EReaderStream::images() const { + return myImages; +} diff --git a/fbreader/src/formats/pdb/EReaderStream.h b/fbreader/src/formats/pdb/EReaderStream.h new file mode 100644 index 0000000..990c6ba --- /dev/null +++ b/fbreader/src/formats/pdb/EReaderStream.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __EREADERSTREAM_H__ +#define __EREADERSTREAM_H__ + +#include <map> + +#include "PalmDocLikeStream.h" +#include <ZLMimeType.h> + +class ZLFile; + +class EReaderStream : public PalmDocLikeStream { + +public: + EReaderStream(const ZLFile &file); + ~EReaderStream(); + + enum StreamDestination { + TEXT, + FOOTNOTE, + }; + + struct ImageInfo { + unsigned long Offset; + unsigned short Size; + shared_ptr<ZLMimeType> Type; + }; + + ImageInfo imageLocation(const std::string& id); + //bool hasExtraSections() const; + bool switchStreamDestination(StreamDestination destination, const std::string &footnoteId); + const std::map<std::string, unsigned short>& footnotes() const; + const std::map<std::string, ImageInfo>& images() const; + +private: + bool processRecord(); + bool processZeroRecord(); + bool processFootnoteIdsRecord(); + bool processImageHeaders(); + + void clearBuffer(unsigned char symbol); + std::string findFootnoteId(std::string &footnoteIdStr) const; + bool addImageInfo(const unsigned short recordIndex); + + bool fillBuffer(); + +private: + unsigned short myCompressionVersion; + unsigned short myNonTextOffset; + unsigned short myNonTextOffsetReserved; //TODO: Warning: isn't used + unsigned short myFootnoteRecords; + unsigned short mySidebarRecords; + unsigned short myBookmarksOffset; + unsigned short myNonTextOffsetExtraReserved; //TODO: Warning: isn't used + unsigned short myImagedataOffset; + unsigned short myImagedataOffsetReserved; //TODO: Warning: isn't used + unsigned short myMetadataOffset; + unsigned short myMetadataOffsetReserved; //TODO: Warning: isn't used + unsigned short myFootnoteOffset; + unsigned short mySidebarOffset; + unsigned short myLastdataOffset; + + + StreamDestination myDestination; + std::map<std::string, unsigned short> myFootnotes; + std::map<std::string, ImageInfo> myImages; + +}; + +#endif /* __EREADERSTREAM_H__ */ diff --git a/fbreader/src/formats/pdb/HtmlMetainfoReader.cpp b/fbreader/src/formats/pdb/HtmlMetainfoReader.cpp new file mode 100644 index 0000000..8829591 --- /dev/null +++ b/fbreader/src/formats/pdb/HtmlMetainfoReader.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLUnicodeUtil.h> + +#include "HtmlMetainfoReader.h" + +#include "../../library/Book.h" + +HtmlMetainfoReader::HtmlMetainfoReader(Book &book, ReadType readType) : + HtmlReader(book.encoding()), myBook(book), myReadType(readType) { +} + +bool HtmlMetainfoReader::tagHandler(const HtmlReader::HtmlTag &tag) { + if (tag.Name == "BODY") { + return false; + } else if (((myReadType & TAGS) == TAGS) && (tag.Name == "DC:SUBJECT")) { + myReadTags = tag.Start; + if (!tag.Start && !myBuffer.empty()) { + myBook.addTag(myBuffer); + myBuffer.erase(); + } + } else if (((myReadType & TITLE) == TITLE) && (tag.Name == "DC:TITLE")) { + myReadTitle = tag.Start; + if (!tag.Start && !myBuffer.empty()) { + myBook.setTitle(myBuffer); + myBuffer.erase(); + } + } else if (((myReadType & AUTHOR) == AUTHOR) && (tag.Name == "DC:CREATOR")) { + if (tag.Start) { + bool flag = false; + for (std::size_t i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "ROLE") { + flag = ZLUnicodeUtil::toUpper(tag.Attributes[i].Value) == "AUT"; + break; + } + } + if (flag) { + if (!myBuffer.empty()) { + myBuffer += ", "; + } + myReadAuthor = true; + } + } else { + myReadAuthor = false; + if (!myBuffer.empty()) { + myBook.addAuthor(myBuffer); + } + myBuffer.erase(); + } + } + return true; +} + +void HtmlMetainfoReader::startDocumentHandler() { + myReadAuthor = false; + myReadTitle = false; + myReadTags = false; +} + +void HtmlMetainfoReader::endDocumentHandler() { +} + +bool HtmlMetainfoReader::characterDataHandler(const char *text, std::size_t len, bool convert) { + if (myReadTitle || myReadAuthor || myReadTags) { + if (convert) { + myConverter->convert(myBuffer, text, text + len); + } else { + myBuffer.append(text, len); + } + } + return true; +} diff --git a/fbreader/src/formats/pdb/HtmlMetainfoReader.h b/fbreader/src/formats/pdb/HtmlMetainfoReader.h new file mode 100644 index 0000000..119c72e --- /dev/null +++ b/fbreader/src/formats/pdb/HtmlMetainfoReader.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __HTMLMETAINFOREADER_H__ +#define __HTMLMETAINFOREADER_H__ + +#include "../html/HtmlReader.h" + +class Book; + +class HtmlMetainfoReader : public HtmlReader { + +public: + enum ReadType { + NONE = 0, + TITLE = 1, + AUTHOR = 2, + TITLE_AND_AUTHOR = TITLE | AUTHOR, + TAGS = 4, + ALL = TITLE | AUTHOR | TAGS + }; + +public: + HtmlMetainfoReader(Book &book, ReadType readType); + +private: + void startDocumentHandler(); + void endDocumentHandler(); + + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char *text, std::size_t len, bool convert); + +private: + Book &myBook; + const ReadType myReadType; + + bool myReadTitle; + bool myReadAuthor; + bool myReadTags; + + std::string myBuffer; +}; + +#endif /* __HTMLMETAINFOREADER_H__ */ diff --git a/fbreader/src/formats/pdb/HuffDecompressor.cpp b/fbreader/src/formats/pdb/HuffDecompressor.cpp new file mode 100644 index 0000000..9b6f285 --- /dev/null +++ b/fbreader/src/formats/pdb/HuffDecompressor.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <cstring> + +#include <ZLInputStream.h> + +#include "PdbReader.h" +#include "BitReader.h" +#include "HuffDecompressor.h" + +HuffDecompressor::HuffDecompressor(ZLInputStream& stream, + const std::vector<unsigned long>::const_iterator beginIt, + const std::vector<unsigned long>::const_iterator endIt, + const unsigned long endHuffDataOffset, const unsigned long extraFlags) : myExtraFlags(extraFlags), myErrorCode(ERROR_NONE) { + + + const unsigned long huffHeaderOffset = *beginIt; + const unsigned long huffRecordsNumber = endIt - beginIt; + const unsigned long huffDataOffset = *(beginIt + 1); + + stream.seek(huffHeaderOffset, true); + stream.seek(16, false); + unsigned long cacheTableOffset, baseTableOffset; + PdbUtil::readUnsignedLongBE(stream, cacheTableOffset); + PdbUtil::readUnsignedLongBE(stream, baseTableOffset); + + + myCacheTable = new unsigned long[256]; + stream.seek(huffHeaderOffset + cacheTableOffset, true); + for (std::size_t i = 0; i < 256; ++i) { + PdbUtil::readUnsignedLongLE(stream, myCacheTable[i]); //LE + } + + myBaseTable = new unsigned long[64]; + stream.seek(huffHeaderOffset + baseTableOffset, true); + for (std::size_t i = 0; i < 64; ++i) { + PdbUtil::readUnsignedLongLE(stream, myBaseTable[i]); //LE + } + + stream.seek(huffDataOffset + 12, true); + PdbUtil::readUnsignedLongBE(stream, myEntryBits); + + std::size_t huffDataSize = endHuffDataOffset - huffDataOffset; + myData = new unsigned char[huffDataSize]; + stream.seek(huffDataOffset, true); + if (huffDataSize == stream.read((char*)myData, huffDataSize)) { + myDicts = new unsigned char* [huffRecordsNumber - 1]; + for(std::size_t i = 0; i < huffRecordsNumber - 1; ++i) { + std::size_t shift = *(beginIt + i + 1) - huffDataOffset; + myDicts[i] = myData + shift; + } + } else { + myErrorCode = ERROR_CORRUPTED_FILE; + } + + myTargetBuffer = 0; + myTargetBufferEnd = 0; + myTargetBufferPtr = 0; +} + +HuffDecompressor::~HuffDecompressor() { + delete[] myCacheTable; + delete[] myBaseTable; + delete[] myData; + delete[] myDicts; +} + +bool HuffDecompressor::error() const { + return myErrorCode == ERROR_CORRUPTED_FILE; +} + +std::size_t HuffDecompressor::decompress(ZLInputStream &stream, char *targetBuffer, std::size_t compressedSize, std::size_t maxUncompressedSize) { + if ((compressedSize == 0) || (myErrorCode == ERROR_CORRUPTED_FILE)) { + return 0; + } + if (targetBuffer != 0) { + unsigned char *sourceBuffer = new unsigned char[compressedSize]; + myTargetBuffer = targetBuffer; + myTargetBufferEnd = targetBuffer + maxUncompressedSize; + myTargetBufferPtr = targetBuffer; + if (stream.read((char*)sourceBuffer, compressedSize) == compressedSize) { + std::size_t trailSize = sizeOfTrailingEntries(sourceBuffer, compressedSize); + if (trailSize < compressedSize) { + bitsDecompress(BitReader(sourceBuffer, compressedSize - trailSize)); + } else { + myErrorCode = ERROR_CORRUPTED_FILE; + } + } + delete[] sourceBuffer; + } else { + myTargetBuffer = 0; + myTargetBufferEnd = 0; + myTargetBufferPtr = 0; + } + + return myTargetBufferPtr - myTargetBuffer; +} + +void HuffDecompressor::bitsDecompress(BitReader bits, std::size_t depth) { + if (depth > 32) { + myErrorCode = ERROR_CORRUPTED_FILE; + return; + } + + while (bits.left()) { + const unsigned long dw = (unsigned long)bits.peek(32); + const unsigned long v = myCacheTable[dw >> 24]; + unsigned long codelen = v & 0x1F; + //if ((codelen == 0) || (codelen > 32)) { + // return false; + //} + unsigned long code = dw >> (32 - codelen); + unsigned long r = (v >> 8); + if (!(v & 0x80)) { + while (code < myBaseTable[(codelen - 1) * 2]) { + codelen += 1; + code = dw >> (32 - codelen); + } + r = myBaseTable[(codelen - 1) * 2 + 1]; + } + r -= code; + //if (codelen == 0) { + // return false; + //} + if (!bits.eat(codelen)) { + return; + } + const unsigned long dicno = r >> myEntryBits; + const unsigned long off1 = 16 + (r - (dicno << myEntryBits)) * 2; + const unsigned char* dict = myDicts[dicno]; //TODO need index check + const unsigned long off2 = 16 + dict[off1] * 256 + dict[off1 + 1]; //TODO need index check + const unsigned long blen = dict[off2] * 256 + dict[off2 + 1]; //TODO need index check + const unsigned char* slice = dict + off2 + 2; + const unsigned long sliceSize = blen & 0x7fff; + if (blen & 0x8000) { + if (myTargetBufferPtr + sliceSize < myTargetBufferEnd) { + std::memcpy(myTargetBufferPtr, slice, sliceSize); + myTargetBufferPtr += sliceSize; + } else { + return; + } + } else { + bitsDecompress(BitReader(slice, sliceSize), depth + 1); + } + } +} + +std::size_t HuffDecompressor::sizeOfTrailingEntries(unsigned char* data, std::size_t size) const { + std::size_t num = 0; + std::size_t flags = myExtraFlags >> 1; + while (flags) { + if (flags & 1) { + if (num < size) { + num += readVariableWidthIntegerBE(data, size - num); + } + } + flags >>= 1; + } + return num; +} + + +std::size_t HuffDecompressor::readVariableWidthIntegerBE(unsigned char* ptr, std::size_t psize) const { + unsigned char bitsSaved = 0; + std::size_t result = 0; + while (true) { + const unsigned char oneByte = ptr[psize - 1]; + result |= (oneByte & 0x7F) << bitsSaved; + bitsSaved += 7; + psize -= 1; + if (((oneByte & 0x80) != 0) || (bitsSaved >= 28) || (psize == 0)) { + return result; + } + } +} diff --git a/fbreader/src/formats/pdb/HuffDecompressor.h b/fbreader/src/formats/pdb/HuffDecompressor.h new file mode 100644 index 0000000..76539e9 --- /dev/null +++ b/fbreader/src/formats/pdb/HuffDecompressor.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __HUFFDECOMPRESSOR_H__ +#define __HUFFDECOMPRESSOR_H__ + +#include <string> + +class ZLInputStream; +class BitReader; + +class HuffDecompressor { + +public: + HuffDecompressor(ZLInputStream& stream, + const std::vector<unsigned long>::const_iterator beginHuffRecordOffsetIt, + const std::vector<unsigned long>::const_iterator endHuffRecordOffsetIt, + const unsigned long endHuffDataOffset, const unsigned long extraFlags); + ~HuffDecompressor(); + + std::size_t decompress(ZLInputStream &stream, char *buffer, std::size_t compressedSize, std::size_t maxUncompressedSize); + bool error() const; +private: + std::size_t sizeOfTrailingEntries(unsigned char* data, std::size_t size) const; + std::size_t readVariableWidthIntegerBE(unsigned char* ptr, std::size_t psize) const; + void bitsDecompress(BitReader bits, std::size_t depth = 0); + +private: + unsigned long myEntryBits; + unsigned long myExtraFlags; + + unsigned long* myCacheTable; + unsigned long* myBaseTable; + unsigned char* myData; + unsigned char** myDicts; + + char* myTargetBuffer; + char* myTargetBufferEnd; + char* myTargetBufferPtr; + + enum { + ERROR_NONE, + ERROR_CORRUPTED_FILE + } myErrorCode; +}; + +#endif /* __HUFFDECOMPRESSOR_H__ */ diff --git a/fbreader/src/formats/pdb/MobipocketHtmlBookReader.cpp b/fbreader/src/formats/pdb/MobipocketHtmlBookReader.cpp new file mode 100644 index 0000000..cecbfbc --- /dev/null +++ b/fbreader/src/formats/pdb/MobipocketHtmlBookReader.cpp @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <cstdlib> +#include <algorithm> + +#include <ZLFile.h> +#include <ZLFileImage.h> +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> + +#include "MobipocketHtmlBookReader.h" +#include "PalmDocStream.h" +#include "../html/HtmlTagActions.h" +#include "../../bookmodel/BookModel.h" + +class MobipocketHtmlImageTagAction : public HtmlTagAction { + +public: + MobipocketHtmlImageTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class MobipocketHtmlHrTagAction : public HtmlTagAction { + +public: + MobipocketHtmlHrTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class MobipocketHtmlHrefTagAction : public HtmlHrefTagAction { + +public: + MobipocketHtmlHrefTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class MobipocketHtmlGuideTagAction : public HtmlTagAction { + +public: + MobipocketHtmlGuideTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class MobipocketHtmlReferenceTagAction : public HtmlTagAction { + +public: + MobipocketHtmlReferenceTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class MobipocketHtmlPagebreakTagAction : public HtmlTagAction { + +public: + MobipocketHtmlPagebreakTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +MobipocketHtmlImageTagAction::MobipocketHtmlImageTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void MobipocketHtmlImageTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "RECINDEX") { + int index = std::atoi(tag.Attributes[i].Value.c_str()); + if (index > 0) { + int &imageCounter = ((MobipocketHtmlBookReader&)myReader).myImageCounter; + imageCounter = std::max(imageCounter, index); + bool stopParagraph = bookReader().paragraphIsOpen(); + if (stopParagraph) { + bookReader().endParagraph(); + } + std::string id; + ZLStringUtil::appendNumber(id, index); + bookReader().addImageReference(id); + if (stopParagraph) { + bookReader().beginParagraph(); + } + } + break; + } + } + } +} + +MobipocketHtmlHrTagAction::MobipocketHtmlHrTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void MobipocketHtmlHrTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + if (bookReader().contentsParagraphIsOpen()) { + bookReader().endContentsParagraph(); + bookReader().exitTitle(); + } + bookReader().insertEndOfSectionParagraph(); + } +} + +MobipocketHtmlHrefTagAction::MobipocketHtmlHrefTagAction(HtmlBookReader &reader) : HtmlHrefTagAction(reader) { +} + +MobipocketHtmlPagebreakTagAction::MobipocketHtmlPagebreakTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void MobipocketHtmlPagebreakTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + if (bookReader().contentsParagraphIsOpen()) { + bookReader().endContentsParagraph(); + bookReader().exitTitle(); + } + bookReader().insertEndOfSectionParagraph(); + } +} + +MobipocketHtmlBookReader::TOCReader::TOCReader(MobipocketHtmlBookReader &reader) : myReader(reader) { + reset(); +} + +void MobipocketHtmlBookReader::TOCReader::reset() { + myEntries.clear(); + + myIsActive = false; + myStartOffset = (std::size_t)-1; + myEndOffset = (std::size_t)-1; + myCurrentEntryText.erase(); +} + +bool MobipocketHtmlBookReader::TOCReader::rangeContainsPosition(std::size_t position) { + return (myStartOffset <= position) && (myEndOffset > position); +} + +void MobipocketHtmlBookReader::TOCReader::startReadEntry(std::size_t position) { + myCurrentReference = position; + myIsActive = true; +} + +void MobipocketHtmlBookReader::TOCReader::endReadEntry() { + if (myIsActive && !myCurrentEntryText.empty()) { + std::string converted; + myReader.myConverter->convert(converted, myCurrentEntryText); + myReader.myConverter->reset(); + myEntries[myCurrentReference] = converted; + myCurrentEntryText.erase(); + } + myIsActive = false; +} + +void MobipocketHtmlBookReader::TOCReader::appendText(const char *text, std::size_t len) { + if (myIsActive) { + myCurrentEntryText.append(text, len); + } +} + +void MobipocketHtmlBookReader::TOCReader::addReference(std::size_t position, const std::string &text) { + myEntries[position] = text; + if (rangeContainsPosition(position)) { + setEndOffset(position); + } +} + +void MobipocketHtmlBookReader::TOCReader::setStartOffset(std::size_t position) { + myStartOffset = position; + std::map<std::size_t,std::string>::const_iterator it = myEntries.lower_bound(position); + if (it != myEntries.end()) { + ++it; + if (it != myEntries.end()) { + myEndOffset = it->first; + } + } +} + +void MobipocketHtmlBookReader::TOCReader::setEndOffset(std::size_t position) { + myEndOffset = position; +} + +const std::map<std::size_t,std::string> &MobipocketHtmlBookReader::TOCReader::entries() const { + return myEntries; +} + +void MobipocketHtmlHrefTagAction::run(const HtmlReader::HtmlTag &tag) { + MobipocketHtmlBookReader &reader = (MobipocketHtmlBookReader&)myReader; + if (tag.Start) { + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "FILEPOS") { + const std::string &value = tag.Attributes[i].Value; + if (!value.empty()) { + std::string label = "&"; + int intValue = std::atoi(value.c_str()); + if (intValue > 0) { + if (reader.myTocReader.rangeContainsPosition(tag.Offset)) { + reader.myTocReader.startReadEntry(intValue); + if (reader.myTocReader.rangeContainsPosition(intValue)) { + reader.myTocReader.setEndOffset(intValue); + } + } + reader.myFileposReferences.insert(intValue); + ZLStringUtil::appendNumber(label, intValue); + setHyperlinkType(INTERNAL_HYPERLINK); + bookReader().addHyperlinkControl(INTERNAL_HYPERLINK, label); + return; + } + } + } + } + } else { + reader.myTocReader.endReadEntry(); + } + HtmlHrefTagAction::run(tag); +} + +MobipocketHtmlGuideTagAction::MobipocketHtmlGuideTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void MobipocketHtmlGuideTagAction::run(const HtmlReader::HtmlTag &tag) { + MobipocketHtmlBookReader &reader = (MobipocketHtmlBookReader&)myReader; + reader.myInsideGuide = tag.Start; +} + +MobipocketHtmlReferenceTagAction::MobipocketHtmlReferenceTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void MobipocketHtmlReferenceTagAction::run(const HtmlReader::HtmlTag &tag) { + MobipocketHtmlBookReader &reader = (MobipocketHtmlBookReader&)myReader; + if (reader.myInsideGuide) { + std::string title; + std::string filepos; + bool isTocReference = false; + for (std::size_t i = 0; i < tag.Attributes.size(); ++i) { + const std::string &name = tag.Attributes[i].Name; + const std::string &value = tag.Attributes[i].Value; + if (name == "TITLE") { + title = value; + } else if (name == "FILEPOS") { + filepos = value; + } else if ((name == "TYPE") && (ZLUnicodeUtil::toUpper(value) == "TOC")) { + isTocReference = true; + } + } + if (!title.empty() && !filepos.empty()) { + int position = std::atoi(filepos.c_str()); + if (position > 0) { + reader.myTocReader.addReference(position, title); + if (isTocReference) { + reader.myTocReader.setStartOffset(position); + } + } + } + } +} + +shared_ptr<HtmlTagAction> MobipocketHtmlBookReader::createAction(const std::string &tag) { + if (tag == "IMG") { + return new MobipocketHtmlImageTagAction(*this); + } else if (tag == "HR") { + return new MobipocketHtmlHrTagAction(*this); + } else if (tag == "A") { + return new MobipocketHtmlHrefTagAction(*this); + } else if (tag == "GUIDE") { + return new MobipocketHtmlGuideTagAction(*this); + } else if (tag == "REFERENCE") { + return new MobipocketHtmlReferenceTagAction(*this); + } else if (tag == "MBP:PAGEBREAK") { + return new MobipocketHtmlPagebreakTagAction(*this); + } + return HtmlBookReader::createAction(tag); +} + +void MobipocketHtmlBookReader::startDocumentHandler() { + HtmlBookReader::startDocumentHandler(); + myImageCounter = 0; + myInsideGuide = false; + myFileposReferences.clear(); + myPositionToParagraphMap.clear(); + myTocReader.reset(); +} + +bool MobipocketHtmlBookReader::tagHandler(const HtmlTag &tag) { + std::size_t paragraphNumber = myBookReader.model().bookTextModel()->paragraphsNumber(); + if (myBookReader.paragraphIsOpen()) { + --paragraphNumber; + } + myPositionToParagraphMap.push_back(std::make_pair(tag.Offset, paragraphNumber)); + return HtmlBookReader::tagHandler(tag); +} + +MobipocketHtmlBookReader::MobipocketHtmlBookReader(const ZLFile &file, BookModel &model, const PlainTextFormat &format, const std::string &encoding) : HtmlBookReader("", model, format, encoding), myFileName(file.path()), myTocReader(*this) { + setBuildTableOfContent(false); + setProcessPreTag(false); +} + +bool MobipocketHtmlBookReader::characterDataHandler(const char *text, std::size_t len, bool convert) { + myTocReader.appendText(text, len); + return HtmlBookReader::characterDataHandler(text, len, convert); +} + +void MobipocketHtmlBookReader::readDocument(ZLInputStream &stream) { + HtmlBookReader::readDocument(stream); + + PalmDocStream &pdStream = (PalmDocStream&)stream; + int index = pdStream.firstImageLocationIndex(myFileName); + + if (index >= 0) { + for (int i = 0; i < myImageCounter; i++) { + std::pair<int,int> imageLocation = pdStream.imageLocation(pdStream.header(), i + index); + if ((imageLocation.first > 0) && (imageLocation.second > 0)) { + std::string id; + ZLStringUtil::appendNumber(id, i + 1); + myBookReader.addImage(id, new ZLFileImage(ZLFile(myFileName), imageLocation.first, imageLocation.second)); + } + } + } + + std::vector<std::pair<std::size_t,std::size_t> >::const_iterator jt = myPositionToParagraphMap.begin(); + for (std::set<std::size_t>::const_iterator it = myFileposReferences.begin(); it != myFileposReferences.end(); ++it) { + while (jt != myPositionToParagraphMap.end() && jt->first < *it) { + ++jt; + } + if (jt == myPositionToParagraphMap.end()) { + break; + } + std::string label = "&"; + ZLStringUtil::appendNumber(label, *it); + myBookReader.addHyperlinkLabel(label, jt->second); + } + + jt = myPositionToParagraphMap.begin(); + const std::map<std::size_t,std::string> &entries = myTocReader.entries(); + for (std::map<std::size_t,std::string>::const_iterator it = entries.begin(); it != entries.end(); ++it) { + while (jt != myPositionToParagraphMap.end() && jt->first < it->first) { + ++jt; + } + if (jt == myPositionToParagraphMap.end()) { + break; + } + myBookReader.beginContentsParagraph(jt->second); + myBookReader.addContentsData(it->second); + myBookReader.endContentsParagraph(); + } +} diff --git a/fbreader/src/formats/pdb/MobipocketHtmlBookReader.h b/fbreader/src/formats/pdb/MobipocketHtmlBookReader.h new file mode 100644 index 0000000..7a35523 --- /dev/null +++ b/fbreader/src/formats/pdb/MobipocketHtmlBookReader.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __MOBIPOCKETHTMLBOOKREADER_H__ +#define __MOBIPOCKETHTMLBOOKREADER_H__ + +#include <set> + +#include "../html/HtmlBookReader.h" + +class MobipocketHtmlBookReader : public HtmlBookReader { + +public: + MobipocketHtmlBookReader(const ZLFile &file, BookModel &model, const PlainTextFormat &format, const std::string &encoding); + void readDocument(ZLInputStream &stream); + +private: + void startDocumentHandler(); + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char *text, std::size_t len, bool convert); + shared_ptr<HtmlTagAction> createAction(const std::string &tag); + +public: + class TOCReader { + + public: + TOCReader(MobipocketHtmlBookReader &reader); + void reset(); + + void addReference(std::size_t position, const std::string &text); + + void setStartOffset(std::size_t position); + void setEndOffset(std::size_t position); + + bool rangeContainsPosition(std::size_t position); + + void startReadEntry(std::size_t position); + void endReadEntry(); + void appendText(const char *text, std::size_t len); + + const std::map<std::size_t,std::string> &entries() const; + + private: + MobipocketHtmlBookReader &myReader; + + std::map<std::size_t,std::string> myEntries; + + bool myIsActive; + std::size_t myStartOffset; + std::size_t myEndOffset; + + std::size_t myCurrentReference; + std::string myCurrentEntryText; + }; + +private: + int myImageCounter; + const std::string myFileName; + + std::vector<std::pair<std::size_t,std::size_t> > myPositionToParagraphMap; + std::set<std::size_t> myFileposReferences; + bool myInsideGuide; + TOCReader myTocReader; + +friend class MobipocketHtmlImageTagAction; +friend class MobipocketHtmlHrefTagAction; +friend class MobipocketHtmlGuideTagAction; +friend class MobipocketHtmlReferenceTagAction; +friend class MobipocketHtmlPagebreakTagAction; +friend class TOCReader; +}; + +#endif /* __MOBIPOCKETHTMLBOOKREADER_H__ */ diff --git a/fbreader/src/formats/pdb/MobipocketPlugin.cpp b/fbreader/src/formats/pdb/MobipocketPlugin.cpp new file mode 100644 index 0000000..4832b43 --- /dev/null +++ b/fbreader/src/formats/pdb/MobipocketPlugin.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> +#include <ZLInputStream.h> +#include <ZLEncodingConverter.h> +#include <ZLUnicodeUtil.h> +#include <ZLLanguageUtil.h> +#include <ZLImage.h> +#include <ZLFileImage.h> + +#include "PdbPlugin.h" +#include "PalmDocStream.h" +#include "MobipocketHtmlBookReader.h" + +#include "../../library/Book.h" + +bool MobipocketPlugin::acceptsFile(const ZLFile &file) const { + return PdbPlugin::fileType(file) == "BOOKMOBI"; +} + +void MobipocketPlugin::readDocumentInternal(const ZLFile &file, BookModel &model, const PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const { + MobipocketHtmlBookReader(file, model, format, encoding).readDocument(stream); +} + +bool MobipocketPlugin::readMetaInfo(Book &book) const { + shared_ptr<ZLInputStream> stream = book.file().inputStream(); + if (stream.isNull() || ! stream->open()) { + return false; + } + PdbHeader header; + if (!header.read(stream)) { + return false; + } + stream->seek(header.Offsets[0] + 16, true); + char test[5]; + test[4] = '\0'; + stream->read(test, 4); + static const std::string MOBI = "MOBI"; + if (MOBI != test) { + return PalmDocLikePlugin::readMetaInfo(book); + } + + unsigned long length; + PdbUtil::readUnsignedLongBE(*stream, length); + + stream->seek(4, false); + + unsigned long encodingCode; + PdbUtil::readUnsignedLongBE(*stream, encodingCode); + if (book.encoding().empty()) { + ZLEncodingConverterInfoPtr info = ZLEncodingCollection::Instance().info(encodingCode); + if (!info.isNull()) { + book.setEncoding(info->name()); + } + } + + stream->seek(52, false); + + unsigned long fullNameOffset; + PdbUtil::readUnsignedLongBE(*stream, fullNameOffset); + unsigned long fullNameLength; + PdbUtil::readUnsignedLongBE(*stream, fullNameLength); + + unsigned long languageCode; + PdbUtil::readUnsignedLongBE(*stream, languageCode); + book.setLanguage(ZLLanguageUtil::languageByCode(languageCode & 0xFF, (languageCode >> 8) & 0xFF)); + + stream->seek(32, false); + + unsigned long exthFlags; + PdbUtil::readUnsignedLongBE(*stream, exthFlags); + if (exthFlags & 0x40) { + stream->seek(header.Offsets[0] + 16 + length, true); + + stream->read(test, 4); + static const std::string EXTH = "EXTH"; + if (EXTH == test) { + stream->seek(4, false); + unsigned long recordsNum; + PdbUtil::readUnsignedLongBE(*stream, recordsNum); + for (unsigned long i = 0; i < recordsNum; ++i) { + unsigned long type; + PdbUtil::readUnsignedLongBE(*stream, type); + unsigned long size; + PdbUtil::readUnsignedLongBE(*stream, size); + if (size > 8) { + std::string value(size - 8, '\0'); + stream->read((char*)value.data(), size - 8); + switch (type) { + case 100: // author + { + int index = value.find(','); + if (index != -1) { + std::string part0 = value.substr(0, index); + std::string part1 = value.substr(index + 1); + ZLUnicodeUtil::utf8Trim(part0); + ZLUnicodeUtil::utf8Trim(part1); + value = part1 + ' ' + part0; + } else { + ZLUnicodeUtil::utf8Trim(value); + } + book.addAuthor(value); + break; + } + case 105: // subject + book.addTag(value); + break; + } + } + } + } + } + + stream->seek(header.Offsets[0] + fullNameOffset, true); + std::string title(fullNameLength, '\0'); + stream->read((char*)title.data(), fullNameLength); + book.setTitle(title); + + stream->close(); + return PalmDocLikePlugin::readMetaInfo(book); +} + +shared_ptr<const ZLImage> MobipocketPlugin::coverImage(const ZLFile &file) const { + shared_ptr<ZLInputStream> stream = file.inputStream(); + if (stream.isNull() || ! stream->open()) { + return 0; + } + PdbHeader header; + if (!header.read(stream)) { + return 0; + } + stream->seek(header.Offsets[0] + 16, true); + char test[5]; + test[4] = '\0'; + stream->read(test, 4); + static const std::string MOBI = "MOBI"; + if (MOBI != test) { + return 0; + } + + unsigned long length; + PdbUtil::readUnsignedLongBE(*stream, length); + + stream->seek(104, false); + + unsigned long exthFlags; + unsigned long coverIndex = (unsigned long)-1; + unsigned long thumbIndex = (unsigned long)-1; + PdbUtil::readUnsignedLongBE(*stream, exthFlags); + if (exthFlags & 0x40) { + stream->seek(header.Offsets[0] + 16 + length, true); + + stream->read(test, 4); + static const std::string EXTH = "EXTH"; + if (EXTH != test) { + return 0; + } + stream->seek(4, false); + unsigned long recordsNum; + PdbUtil::readUnsignedLongBE(*stream, recordsNum); + for (unsigned long i = 0; i < recordsNum; ++i) { + unsigned long type; + PdbUtil::readUnsignedLongBE(*stream, type); + unsigned long size; + PdbUtil::readUnsignedLongBE(*stream, size); + switch (type) { + case 201: // coveroffset + if (size == 12) { + PdbUtil::readUnsignedLongBE(*stream, coverIndex); + } else { + stream->seek(size - 8, false); + } + break; + case 202: // thumboffset + if (size == 12) { + PdbUtil::readUnsignedLongBE(*stream, thumbIndex); + } else { + stream->seek(size - 8, false); + } + break; + default: + stream->seek(size - 8, false); + break; + } + } + } + stream->close(); + + if (coverIndex == (unsigned long)-1) { + if (thumbIndex == (unsigned long)-1) { + return 0; + } + coverIndex = thumbIndex; + } + + PalmDocStream pbStream(file); + if (!pbStream.open()) { + return 0; + } + int index = pbStream.firstImageLocationIndex(file.path()); + if (index >= 0) { + std::pair<int,int> imageLocation = pbStream.imageLocation(pbStream.header(), index + coverIndex); + if ((imageLocation.first > 0) && (imageLocation.second > 0)) { + return new ZLFileImage( + file, + imageLocation.first, + imageLocation.second + ); + } + } + return 0; +} diff --git a/fbreader/src/formats/pdb/PalmDocLikePlugin.cpp b/fbreader/src/formats/pdb/PalmDocLikePlugin.cpp new file mode 100644 index 0000000..27c03a1 --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocLikePlugin.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> + +#include "PdbPlugin.h" +#include "PalmDocStream.h" +#include "PalmDocLikeStream.h" + +#include "../../library/Book.h" + +bool PalmDocLikePlugin::providesMetaInfo() const { + return true; +} + +shared_ptr<ZLInputStream> PalmDocLikePlugin::createStream(const ZLFile &file) const { + return new PalmDocStream(file); +} + +const std::string &PalmDocLikePlugin::tryOpen(const ZLFile &file) const { + PalmDocStream stream(file); + stream.open(); + return stream.error(); +} diff --git a/fbreader/src/formats/pdb/PalmDocLikeStream.cpp b/fbreader/src/formats/pdb/PalmDocLikeStream.cpp new file mode 100644 index 0000000..8b99d4d --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocLikeStream.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> +#include <ZLResource.h> + +#include "PalmDocLikeStream.h" + + +PalmDocLikeStream::PalmDocLikeStream(const ZLFile &file) : PdbStream(file) { +} + +PalmDocLikeStream::~PalmDocLikeStream() { + close(); +} + +bool PalmDocLikeStream::open() { + myErrorCode = ERROR_NONE; + if (!PdbStream::open()) { + myErrorCode = ERROR_UNKNOWN; + return false; + } + + if (!processZeroRecord()) { + return false; + } + + myBuffer = new char[myMaxRecordSize]; + myRecordIndex = 0; + return true; +} + +bool PalmDocLikeStream::fillBuffer() { + while (myBufferOffset == myBufferLength) { + if (myRecordIndex + 1 > myMaxRecordIndex) { + return false; + } + ++myRecordIndex; + if (!processRecord()) { + return false; + } + } + //myBufferOffset = 0; + return true; +} + +const std::string &PalmDocLikeStream::error() const { + static const ZLResource &resource = ZLResource::resource("mobipocketPlugin"); + switch (myErrorCode) { + default: + { + static const std::string EMPTY; + return EMPTY; + } + case ERROR_UNKNOWN: + return resource["unknown"].value(); + case ERROR_COMPRESSION: + return resource["unsupportedCompressionMethod"].value(); + case ERROR_ENCRYPTION: + return resource["encryptedFile"].value(); + } +} diff --git a/fbreader/src/formats/pdb/PalmDocLikeStream.h b/fbreader/src/formats/pdb/PalmDocLikeStream.h new file mode 100644 index 0000000..623a493 --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocLikeStream.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __PALMDOCLIKESTREAM_H__ +#define __PALMDOCLIKESTREAM_H__ + +#include "PdbStream.h" + +class ZLFile; + +class PalmDocLikeStream : public PdbStream { + +public: + PalmDocLikeStream(const ZLFile &file); + ~PalmDocLikeStream(); + bool open(); + + const std::string &error() const; + //std::pair<int,int> imageLocation(int index); + //bool hasExtraSections() const; + +protected: + bool fillBuffer(); + +private: + virtual bool processRecord() = 0; + virtual bool processZeroRecord() = 0; + +protected: + unsigned short myMaxRecordSize; + std::size_t myRecordIndex; + std::size_t myMaxRecordIndex; + + enum { + ERROR_NONE, + ERROR_UNKNOWN, + ERROR_COMPRESSION, + ERROR_ENCRYPTION, + } myErrorCode; +}; + +#endif /* __PALMDOCLIKESTREAM_H__ */ diff --git a/fbreader/src/formats/pdb/PalmDocPlugin.cpp b/fbreader/src/formats/pdb/PalmDocPlugin.cpp new file mode 100644 index 0000000..c23f11c --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocPlugin.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> +#include <ZLInputStream.h> + +#include "PdbPlugin.h" +#include "PalmDocStream.h" +#include "MobipocketHtmlBookReader.h" +#include "../txt/PlainTextFormat.h" +#include "../util/TextFormatDetector.h" + +bool PalmDocPlugin::acceptsFile(const ZLFile &file) const { + return PdbPlugin::fileType(file) == "TEXtREAd"; +} + +void PalmDocPlugin::readDocumentInternal(const ZLFile &file, BookModel &model, const PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const { + stream.open(); + bool readAsPalmDoc = ((PalmDocStream&)stream).hasExtraSections(); + stream.close(); + if (readAsPalmDoc) { + MobipocketHtmlBookReader(file, model, format, encoding).readDocument(stream); + } else { + SimplePdbPlugin::readDocumentInternal(file, model, format, encoding, stream); + } +} + +FormatInfoPage *PalmDocPlugin::createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file) { + shared_ptr<ZLInputStream> stream = createStream(file); + stream->open(); + bool readAsPalmDoc = ((PalmDocStream&)*stream).hasExtraSections(); + stream->close(); + if (!readAsPalmDoc) { + return new PlainTextInfoPage(dialog, file, ZLResourceKey("Text"), !TextFormatDetector().isHtml(*stream)); + } else { + return 0; + } +} diff --git a/fbreader/src/formats/pdb/PalmDocStream.cpp b/fbreader/src/formats/pdb/PalmDocStream.cpp new file mode 100644 index 0000000..e699d47 --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocStream.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <cstring> +#include <algorithm> + +#include <ZLFile.h> +#include <ZLResource.h> +#include <ZLZDecompressor.h> + +#include "PalmDocStream.h" +#include "DocDecompressor.h" +#include "HuffDecompressor.h" + +PalmDocStream::PalmDocStream(const ZLFile &file) : PalmDocLikeStream(file) { +} + +PalmDocStream::~PalmDocStream() { + close(); +} + +bool PalmDocStream::processRecord() { + const std::size_t currentOffset = recordOffset(myRecordIndex); + if (currentOffset < myBase->offset()) { + return false; + } + myBase->seek(currentOffset, true); + const std::size_t nextOffset = recordOffset(myRecordIndex + 1); + if (nextOffset < currentOffset) { + return false; + } + const unsigned short recordSize = nextOffset - currentOffset; + switch(myCompressionVersion) { + case 17480://'DH' // HuffCDic compression + myBufferLength = myHuffDecompressorPtr->decompress(*myBase, myBuffer, recordSize, myMaxRecordSize); + //if (myHuffDecompressorPtr->error()) { + // myErrorCode = ERROR_UNKNOWN; + //} + break; + case 2: // PalmDoc compression + myBufferLength = DocDecompressor().decompress(*myBase, myBuffer, recordSize, myMaxRecordSize); + break; + case 1: // No compression + myBufferLength = myBase->read(myBuffer, std::min(recordSize, myMaxRecordSize)); + break; + } + myBufferOffset = 0; + return true; +} + +bool PalmDocStream::processZeroRecord() { + // Uses with offset presetting to zero record offset value + PdbUtil::readUnsignedShort(*myBase, myCompressionVersion); // myBase offset: ^ + 2 + switch (myCompressionVersion) { + case 1: + case 2: + case 17480: + break; + default: + myErrorCode = ERROR_COMPRESSION; + return false; + } + myBase->seek(2, false); // myBase offset: ^ + 4 + PdbUtil::readUnsignedLongBE(*myBase, myTextLength); // myBase offset: ^ + 8 + PdbUtil::readUnsignedShort(*myBase, myTextRecordNumber); // myBase offset: ^ + 10 + + unsigned short endSectionIndex = header().Offsets.size(); + myMaxRecordIndex = std::min(myTextRecordNumber, (unsigned short)(endSectionIndex - 1)); + //TODO Insert in this point error message about uncompatible records and numRecords from Header + + PdbUtil::readUnsignedShort(*myBase, myMaxRecordSize); // myBase offset: ^ + 12 + if (myMaxRecordSize == 0) { + myErrorCode = ERROR_UNKNOWN; + return false; + } + + /* + std::cerr << "PalmDocStream::processRecord0():\n"; + std::cerr << "PDB header indentificator : " << header().Id << "\n"; + std::cerr << "PDB file system: sizeof opened : " << myBaseSize << "\n"; + std::cerr << "PDB header/record[0] max index : " << myMaxRecordIndex << "\n"; + std::cerr << "PDB record[0][0..2] compression : " << myCompressionVersion << "\n"; + std::cerr << "PDB record[0][2..4] spare : " << mySpare << "\n"; + std::cerr << "PDB record[0][4..8] text length : " << myTextLength << "\n"; + std::cerr << "PDB record[0][8..10] text records : " << myTextRecords << "\n"; + std::cerr << "PDB record[0][10..12] max record size: " << myMaxRecordSize << "\n"; + */ + + if (header().Id == "BOOKMOBI") { + unsigned short encrypted = 0; + PdbUtil::readUnsignedShort(*myBase, encrypted); // myBase offset: ^ + 14 + if (encrypted) { //Always = 2, if encrypted + myErrorCode = ERROR_ENCRYPTION; + return false; + } + } else { + myBase->seek(2, false); + } + + + if (myCompressionVersion == 17480) { + unsigned long mobiHeaderLength; + unsigned long huffSectionIndex; + unsigned long huffSectionNumber; + unsigned short extraFlags; + unsigned long initialOffset = header().Offsets[0]; // myBase offset: ^ + + myBase->seek(6, false); // myBase offset: ^ + 20 + PdbUtil::readUnsignedLongBE(*myBase, mobiHeaderLength); // myBase offset: ^ + 24 + + myBase->seek(0x70 - 24, false); // myBase offset: ^ + 102 (0x70) + PdbUtil::readUnsignedLongBE(*myBase, huffSectionIndex); // myBase offset: ^ + 106 (0x74) + PdbUtil::readUnsignedLongBE(*myBase, huffSectionNumber); // myBase offset: ^ + 110 (0x78) + + if (mobiHeaderLength >= 244) { + myBase->seek(0xF2 - 0x78, false); // myBase offset: ^ + 242 (0xF2) + PdbUtil::readUnsignedShort(*myBase, extraFlags); // myBase offset: ^ + 244 (0xF4) + } else { + extraFlags = 0; + } + /* + std::cerr << "mobi header length: " << mobiHeaderLength << "\n"; + std::cerr << "Huff's start record : " << huffSectionIndex << " from " << endSectionIndex - 1 << "\n"; + std::cerr << "Huff's records number: " << huffSectionNumber << "\n"; + std::cerr << "Huff's extraFlags : " << extraFlags << "\n"; + */ + const unsigned long endHuffSectionIndex = huffSectionIndex + huffSectionNumber; + if (endHuffSectionIndex > endSectionIndex || huffSectionNumber <= 1) { + myErrorCode = ERROR_COMPRESSION; + return false; + } + const unsigned long endHuffDataOffset = recordOffset(endHuffSectionIndex); + std::vector<unsigned long>::const_iterator beginHuffSectionOffsetIt = header().Offsets.begin() + huffSectionIndex; + // point to first Huff section + std::vector<unsigned long>::const_iterator endHuffSectionOffsetIt = header().Offsets.begin() + endHuffSectionIndex; + // point behind last Huff section + + + myHuffDecompressorPtr = new HuffDecompressor(*myBase, beginHuffSectionOffsetIt, endHuffSectionOffsetIt, endHuffDataOffset, extraFlags); + myBase->seek(initialOffset, true); // myBase offset: ^ + 14 + } + return true; +} + +bool PalmDocStream::hasExtraSections() const { + return myMaxRecordIndex < header().Offsets.size() - 1; +} + +std::pair<int,int> PalmDocStream::imageLocation(const PdbHeader &header, int index) const { + index += myMaxRecordIndex + 1; + int recordNumber = header.Offsets.size(); + if (index > recordNumber - 1) { + return std::make_pair(-1, -1); + } else { + int start = header.Offsets[index]; + int end = (index < recordNumber - 1) ? + header.Offsets[index + 1] : myBase->offset(); + return std::make_pair(start, end - start); + } +} + +int PalmDocStream::firstImageLocationIndex(const std::string &fileName) { + shared_ptr<ZLInputStream> fileStream = ZLFile(fileName).inputStream(); + if (fileStream.isNull() || !fileStream->open()) { + return -1; + } + + bool found = false; + int index = 0; + char bu[5] = { 0 }; + std::pair<int,int> firstImageLocation = imageLocation(header(), 0); + fileStream->seek(firstImageLocation.first, false); + while ((firstImageLocation.first > 0) && (firstImageLocation.second > 0)) { + if (firstImageLocation.second > 4) { + fileStream->read(bu, 4); + static const char jpegStart[2] = { (char)0xFF, (char)0xd8 }; + if (std::strncmp(bu, "BM", 2) == 0 || + std::strncmp(bu, "GIF8", 4) == 0 || + std::strncmp(bu, jpegStart, 2) == 0) { + found = true; + break; + } + fileStream->seek(firstImageLocation.second - 4, false); + } else { + fileStream->seek(firstImageLocation.second, false); + } + index++; + firstImageLocation = imageLocation(header(), index); + } + + fileStream->close(); + return found ? index : -1; +} diff --git a/fbreader/src/formats/pdb/PalmDocStream.h b/fbreader/src/formats/pdb/PalmDocStream.h new file mode 100644 index 0000000..4782a7b --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocStream.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __PALMDOCSTREAM_H__ +#define __PALMDOCSTREAM_H__ + +#include "PalmDocLikeStream.h" + +class ZLFile; +class HuffDecompressor; + +class PalmDocStream : public PalmDocLikeStream { + +public: + PalmDocStream(const ZLFile &file); + ~PalmDocStream(); + + std::pair<int,int> imageLocation(const PdbHeader &header, int index) const; + bool hasExtraSections() const; + int firstImageLocationIndex(const std::string &fileName); + +private: + bool processRecord(); + bool processZeroRecord(); + +private: + unsigned short myCompressionVersion; + unsigned long myTextLength; //TODO: Warning: isn't used + unsigned short myTextRecordNumber; + + shared_ptr<HuffDecompressor> myHuffDecompressorPtr; +}; + +#endif /* __PALMDOCSTREAM_H__ */ diff --git a/fbreader/src/formats/pdb/PdbPlugin.cpp b/fbreader/src/formats/pdb/PdbPlugin.cpp new file mode 100644 index 0000000..69ef233 --- /dev/null +++ b/fbreader/src/formats/pdb/PdbPlugin.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> +#include <ZLInputStream.h> +#include <ZLOptions.h> + +#include "PdbPlugin.h" +#include "../../options/FBCategoryKey.h" + +#include "../../database/booksdb/BooksDBUtil.h" +#include "../../database/booksdb/BooksDB.h" + +PdbPlugin::~PdbPlugin() { +} + +std::string PdbPlugin::fileType(const ZLFile &file) { + const std::string &extension = file.extension(); + if ((extension != "prc") && (extension != "pdb") && (extension != "mobi")) { + return ""; + } + + const std::string &fileName = file.path(); + //int index = fileName.find(':'); + //ZLFile baseFile = (index == -1) ? file : ZLFile(fileName.substr(0, index)); + ZLFile baseFile(file.physicalFilePath()); + bool upToDate = BooksDBUtil::checkInfo(baseFile); + + //ZLStringOption palmTypeOption(FBCategoryKey::BOOKS, file.path(), "PalmType", ""); + std::string palmType = BooksDB::Instance().getPalmType(fileName); + if ((palmType.length() != 8) || !upToDate) { + shared_ptr<ZLInputStream> stream = file.inputStream(); + if (stream.isNull() || !stream->open()) { + return ""; + } + stream->seek(60, false); + char id[8]; + stream->read(id, 8); + stream->close(); + palmType = std::string(id, 8); + if (!upToDate) { + BooksDBUtil::saveInfo(baseFile); + } + //palmTypeOption.setValue(palmType); + BooksDB::Instance().setPalmType(fileName, palmType); + } + return palmType; +} + +bool PdbPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} diff --git a/fbreader/src/formats/pdb/PdbPlugin.h b/fbreader/src/formats/pdb/PdbPlugin.h new file mode 100644 index 0000000..9f8600b --- /dev/null +++ b/fbreader/src/formats/pdb/PdbPlugin.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __PDBPLUGIN_H__ +#define __PDBPLUGIN_H__ + +#include <shared_ptr.h> + +#include "../FormatPlugin.h" + +class PdbPlugin : public FormatPlugin { + +public: + static std::string fileType(const ZLFile &file); + bool readLanguageAndEncoding(Book &book) const; + +protected: + PdbPlugin(); + +public: + virtual ~PdbPlugin(); +}; + +class PluckerPlugin : public PdbPlugin { + +public: + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readModel(BookModel &model) const; +}; + +class SimplePdbPlugin : public PdbPlugin { + +public: + bool readMetaInfo(Book &book) const; + bool readModel(BookModel &model) const; + +protected: + virtual shared_ptr<ZLInputStream> createStream(const ZLFile &file) const = 0; + virtual void readDocumentInternal(const ZLFile &file, BookModel &model, const class PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const; +}; + +class PalmDocLikePlugin : public SimplePdbPlugin { + +public: + bool providesMetaInfo() const; + const std::string &tryOpen(const ZLFile &file) const; + +protected: + shared_ptr<ZLInputStream> createStream(const ZLFile &file) const; +}; + +class PalmDocPlugin : public PalmDocLikePlugin { + +public: + bool acceptsFile(const ZLFile &file) const; + + void readDocumentInternal(const ZLFile &file, BookModel &model, const class PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const; + +private: + FormatInfoPage *createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file); +}; + +class MobipocketPlugin : public PalmDocLikePlugin { + +private: + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + + void readDocumentInternal(const ZLFile &file, BookModel &model, const class PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const; + shared_ptr<const ZLImage> coverImage(const ZLFile &file) const; +}; + +class EReaderPlugin : public SimplePdbPlugin { + +public: + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + const std::string &tryOpen(const ZLFile &file) const; + + void readDocumentInternal(const ZLFile &file, BookModel &model, const class PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const; +protected: + shared_ptr<ZLInputStream> createStream(const ZLFile &file) const; +}; + +class ZTXTPlugin : public SimplePdbPlugin { + +public: + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + +protected: + shared_ptr<ZLInputStream> createStream(const ZLFile &file) const; + +private: + FormatInfoPage *createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file); +}; + +inline PdbPlugin::PdbPlugin() {} + +#endif /* __PDBPLUGIN_H__ */ diff --git a/fbreader/src/formats/pdb/PdbReader.cpp b/fbreader/src/formats/pdb/PdbReader.cpp new file mode 100644 index 0000000..54dc654 --- /dev/null +++ b/fbreader/src/formats/pdb/PdbReader.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> + +#include "PdbReader.h" + +void PdbUtil::readUnsignedShort(ZLInputStream &stream, unsigned short &N) { + unsigned char data[2]; + stream.read((char*)data, 2); + N = (((unsigned short)data[0]) << 8) + data[1]; +} + +void PdbUtil::readUnsignedLongBE(ZLInputStream &stream, unsigned long &N) { + unsigned char data[4]; + stream.read((char*)data, 4); + N = (((unsigned long)data[0]) << 24) + + (((unsigned long)data[1]) << 16) + + (((unsigned long)data[2]) << 8) + + (unsigned long)data[3]; +} + +void PdbUtil::readUnsignedLongLE(ZLInputStream &stream, unsigned long &N) { + unsigned char data[4]; + stream.read((char*)data, 4); + N = (((unsigned long)data[3]) << 24) + + (((unsigned long)data[2]) << 16) + + (((unsigned long)data[1]) << 8) + + (unsigned long)data[0]; +} + +bool PdbHeader::read(shared_ptr<ZLInputStream> stream) { + const std::size_t startOffset = stream->offset(); + DocName.erase(); + DocName.append(32, '\0'); + stream->read((char*)DocName.data(), 32); // stream offset: +32 + + PdbUtil::readUnsignedShort(*stream, Flags); // stream offset: +34 + + stream->seek(26, false); // stream offset: +60 + + Id.erase(); + Id.append(8, '\0'); + stream->read((char*)Id.data(), 8); // stream offset: +68 + + stream->seek(8, false); // stream offset: +76 + Offsets.clear(); + unsigned short numRecords; + PdbUtil::readUnsignedShort(*stream, numRecords); // stream offset: +78 + Offsets.reserve(numRecords); + + for (int i = 0; i < numRecords; ++i) { // stream offset: +78 + 8 * records number + unsigned long recordOffset; + PdbUtil::readUnsignedLongBE(*stream, recordOffset); + Offsets.push_back(recordOffset); + stream->seek(4, false); + } + return stream->offset() == startOffset + 78 + 8 * numRecords; +} + +/*bool PdbRecord0::read(shared_ptr<ZLInputStream> stream) { + std::size_t startOffset = stream->offset(); + + PdbUtil::readUnsignedShort(*stream, CompressionType); + PdbUtil::readUnsignedShort(*stream, Spare); + PdbUtil::readUnsignedLongBE(*stream, TextLength); + PdbUtil::readUnsignedShort(*stream, TextRecords); + PdbUtil::readUnsignedShort(*stream, MaxRecordSize); + PdbUtil::readUnsignedShort(*stream, NontextOffset); + PdbUtil::readUnsignedShort(*stream, NontextOffset2); + + PdbUtil::readUnsignedLongBE(*stream, MobipocketID); + PdbUtil::readUnsignedLongBE(*stream, MobipocketHeaderSize); + PdbUtil::readUnsignedLongBE(*stream, Unknown24); + PdbUtil::readUnsignedShort(*stream, FootnoteRecs); + PdbUtil::readUnsignedShort(*stream, SidebarRecs); + + PdbUtil::readUnsignedShort(*stream, BookmarkOffset); + PdbUtil::readUnsignedShort(*stream, Unknown34); + PdbUtil::readUnsignedShort(*stream, NontextOffset3); + PdbUtil::readUnsignedShort(*stream, Unknown38); + PdbUtil::readUnsignedShort(*stream, ImagedataOffset); + PdbUtil::readUnsignedShort(*stream, ImagedataOffset2); + PdbUtil::readUnsignedShort(*stream, MetadataOffset); + PdbUtil::readUnsignedShort(*stream, MetadataOffset2); + PdbUtil::readUnsignedShort(*stream, FootnoteOffset); + PdbUtil::readUnsignedShort(*stream, SidebarOffset); + PdbUtil::readUnsignedShort(*stream, LastDataOffset); + PdbUtil::readUnsignedShort(*stream, Unknown54); + + return stream->offset() == startOffset + 56; +}*/ diff --git a/fbreader/src/formats/pdb/PdbReader.h b/fbreader/src/formats/pdb/PdbReader.h new file mode 100644 index 0000000..f32ebf5 --- /dev/null +++ b/fbreader/src/formats/pdb/PdbReader.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __PDBREADER_H__ +#define __PDBREADER_H__ + +#include <vector> + +#include <shared_ptr.h> +#include <ZLInputStream.h> + +//class BookModel; + +class PdbUtil { + +public: + static void readUnsignedShort(ZLInputStream &stream, unsigned short &N); + static void readUnsignedLongBE(ZLInputStream &stream, unsigned long &N); + static void readUnsignedLongLE(ZLInputStream &stream, unsigned long &N); +}; + +struct PdbHeader { + std::string DocName; + unsigned short Flags; + std::string Id; + std::vector<unsigned long> Offsets; + + bool read(shared_ptr<ZLInputStream> stream); +}; + +struct PdbRecord0 { + unsigned short CompressionType; //[0..2] PalmDoc, Mobipocket, Ereader:version + unsigned short Spare; //[2..4] PalmDoc, Mobipocket + unsigned long TextLength; //[4..8] PalmDoc, Mobipocket + unsigned short TextRecords; //[8..10] PalmDoc, Mobipocket + unsigned short MaxRecordSize; //[10..12] PalmDoc, Mobipocket + unsigned short NontextOffset; //[12..14] Ereader + unsigned short NontextOffset2; //[14..16] Ereader //PalmDoc, Mobipocket: encrypted - there is conflict !!!! + + unsigned long MobipocketID; //[16..20] Mobipocket + unsigned long MobipocketHeaderSize;//[20..24] Mobipocket + unsigned long Unknown24; //[24..28] + unsigned short FootnoteRecs; //[28..30] Ereader + unsigned short SidebarRecs; //[30..32] Ereader + +// Following fields are specific for EReader pdb document specification + + unsigned short BookmarkOffset; //[32..34] + unsigned short Unknown34; //[34..36] + unsigned short NontextOffset3; //[36..38] + unsigned short Unknown38; //[38..40] + unsigned short ImagedataOffset; //[40..42] + unsigned short ImagedataOffset2; //[42..44] + unsigned short MetadataOffset; //[44..46] + unsigned short MetadataOffset2; //[46..48] + unsigned short FootnoteOffset; //[48..50] + unsigned short SidebarOffset; //[50..52] + unsigned short LastDataOffset; //[52..54] + unsigned short Unknown54; //[54..56] + + bool read(shared_ptr<ZLInputStream> stream); +//private: +// static bool readNumberBE(unsigned char* buffer, std::size_t offset, std::size_t size); +}; + +#endif /* __PDBREADER_H__ */ diff --git a/fbreader/src/formats/pdb/PdbStream.cpp b/fbreader/src/formats/pdb/PdbStream.cpp new file mode 100644 index 0000000..219a0de --- /dev/null +++ b/fbreader/src/formats/pdb/PdbStream.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <cstring> + +#include <ZLFile.h> + +#include "PdbStream.h" + +PdbStream::PdbStream(const ZLFile &file) : myBase(file.inputStream()) { + myBuffer = 0; +} + +PdbStream::~PdbStream() { +} + +bool PdbStream::open() { + close(); + if (myBase.isNull() || !myBase->open() || !myHeader.read(myBase)) { + return false; + } + // myBase offset: startOffset + 78 + 8 * records number ( myHeader.Offsets.size() ) + + myBase->seek(myHeader.Offsets[0], true); + // myBase offset: Offset[0] - zero record + + myBufferLength = 0; + myBufferOffset = 0; + + myOffset = 0; + + return true; +} + +std::size_t PdbStream::read(char *buffer, std::size_t maxSize) { + std::size_t realSize = 0; + while (realSize < maxSize) { + if (!fillBuffer()) { + break; + } + std::size_t size = std::min((std::size_t)(maxSize - realSize), (std::size_t)(myBufferLength - myBufferOffset)); + + if (size > 0) { + if (buffer != 0) { + std::memcpy(buffer + realSize, myBuffer + myBufferOffset, size); + } + realSize += size; + myBufferOffset += size; + } + } + + myOffset += realSize; + return realSize; +} + +void PdbStream::close() { + if (!myBase.isNull()) { + myBase->close(); + } + if (myBuffer != 0) { + delete[] myBuffer; + myBuffer = 0; + } +} + +void PdbStream::seek(int offset, bool absoluteOffset) { + if (absoluteOffset) { + offset -= this->offset(); + } + if (offset > 0) { + read(0, offset); + } else if (offset < 0) { + offset += this->offset(); + open(); + if (offset >= 0) { + read(0, offset); + } + } +} + +std::size_t PdbStream::offset() const { + return myOffset; +} + +std::size_t PdbStream::sizeOfOpened() { + // TODO: implement + return 0; +} + +std::size_t PdbStream::recordOffset(std::size_t index) const { + return index < myHeader.Offsets.size() ? + myHeader.Offsets[index] : myBase->sizeOfOpened(); +} diff --git a/fbreader/src/formats/pdb/PdbStream.h b/fbreader/src/formats/pdb/PdbStream.h new file mode 100644 index 0000000..f2c58f1 --- /dev/null +++ b/fbreader/src/formats/pdb/PdbStream.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __PDBSTREAM_H__ +#define __PDBSTREAM_H__ + +#include <ZLInputStream.h> + +#include "PdbReader.h" + +class ZLFile; + +class PdbStream : public ZLInputStream { + +public: + PdbStream(const ZLFile &file); + virtual ~PdbStream(); + +protected: + virtual bool open(); + virtual void close(); + +private: + std::size_t read(char *buffer, std::size_t maxSize); + + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +protected: + virtual bool fillBuffer() = 0; + +protected: + std::size_t recordOffset(std::size_t index) const; + +public: + const PdbHeader &header() const; + +protected: + shared_ptr<ZLInputStream> myBase; + std::size_t myOffset; + +private: + PdbHeader myHeader; + +protected: + char *myBuffer; + unsigned short myBufferLength; + unsigned short myBufferOffset; +}; + +inline const PdbHeader &PdbStream::header() const { + return myHeader; +} + +#endif /* __PDBSTREAM_H__ */ diff --git a/fbreader/src/formats/pdb/PluckerBookReader.cpp b/fbreader/src/formats/pdb/PluckerBookReader.cpp new file mode 100644 index 0000000..61bc311 --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerBookReader.cpp @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <algorithm> +#include <vector> +#include <cctype> + +#include <ZLZDecompressor.h> +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLImage.h> +#include <ZLFileImage.h> +#include <ZLFile.h> +#include <ZLTextStyleEntry.h> + +#include "PdbReader.h" +#include "PluckerBookReader.h" +#include "DocDecompressor.h" +#include "PluckerImages.h" +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +PluckerBookReader::PluckerBookReader(BookModel &model) : BookReader(model), EncodedTextReader(model.book()->encoding()), myFile(model.book()->file()), myFont(FT_REGULAR) { + myCharBuffer = new char[65535]; + myForcedEntry = 0; +} + +PluckerBookReader::~PluckerBookReader() { + delete[] myCharBuffer; +} + +void PluckerBookReader::safeAddControl(FBTextKind kind, bool start) { + if (myParagraphStarted) { + addControl(kind, start); + } else { + myDelayedControls.push_back(std::make_pair(kind, start)); + } +} + +void PluckerBookReader::safeAddHyperlinkControl(const std::string &id) { + if (myParagraphStarted) { + addHyperlinkControl(INTERNAL_HYPERLINK, id); + } else { + myDelayedHyperlinks.push_back(id); + } +} + +void PluckerBookReader::safeBeginParagraph() { + if (!myParagraphStarted) { + myParagraphStarted = true; + myBufferIsEmpty = true; + beginParagraph(); + if (!myParagraphStored) { + myParagraphVector->push_back(model().bookTextModel()->paragraphsNumber() - 1); + myParagraphStored = true; + } + for (std::vector<std::pair<FBTextKind,bool> >::const_iterator it = myDelayedControls.begin(); it != myDelayedControls.end(); ++it) { + addControl(it->first, it->second); + } + if (myForcedEntry != 0) { + addStyleEntry(*myForcedEntry); + } else { + addControl(REGULAR, true); + } + for (std::vector<std::string>::const_iterator it = myDelayedHyperlinks.begin(); it != myDelayedHyperlinks.end(); ++it) { + addHyperlinkControl(INTERNAL_HYPERLINK, *it); + } + myDelayedHyperlinks.clear(); + } +} + + +void PluckerBookReader::safeEndParagraph() { + if (myParagraphStarted) { + if (myBufferIsEmpty) { + static const std::string SPACE = " "; + addData(SPACE); + } + endParagraph(); + myParagraphStarted = false; + } +} + +void PluckerBookReader::processHeader(FontType font, bool start) { + if (start) { + enterTitle(); + FBTextKind kind; + switch (font) { + case FT_H1: + kind = H1; + break; + case FT_H2: + kind = H2; + break; + case FT_H3: + kind = H3; + break; + case FT_H4: + kind = H4; + break; + case FT_H5: + kind = H5; + break; + case FT_H6: + default: + kind = H6; + break; + } + pushKind(kind); + } else { + popKind(); + exitTitle(); + } +}; + +void PluckerBookReader::setFont(FontType font, bool start) { + switch (font) { + case FT_REGULAR: + break; + case FT_H1: + case FT_H2: + case FT_H3: + case FT_H4: + case FT_H5: + case FT_H6: + processHeader(font, start); + break; + case FT_BOLD: + safeAddControl(BOLD, start); + break; + case FT_TT: + safeAddControl(CODE, start); + break; + case FT_SMALL: + break; + case FT_SUB: + safeAddControl(SUB, start); + break; + case FT_SUP: + safeAddControl(SUP, start); + break; + } +} + +void PluckerBookReader::changeFont(FontType font) { + if (myFont == font) { + return; + } + setFont(myFont, false); + myFont = font; + setFont(myFont, true); +} + +/* +static void listParameters(char *ptr) { + int argc = ((unsigned char)*ptr) % 8; + std::cerr << (int)(unsigned char)*ptr << "("; + for (int i = 0; i < argc - 1; ++i) { + ++ptr; + std::cerr << (int)*ptr << ", "; + } + if (argc > 0) { + ++ptr; + std::cerr << (int)*ptr; + } + std::cerr << ")\n"; +} +*/ + +static unsigned int twoBytes(char *ptr) { + return 256 * (unsigned char)*ptr + (unsigned char)*(ptr + 1); +} + +static unsigned int fourBytes(char *ptr) { + return 65536 * twoBytes(ptr) + twoBytes(ptr + 2); +} + +static std::string fromNumber(unsigned int num) { + std::string str; + ZLStringUtil::appendNumber(str, num); + return str; +} + +void PluckerBookReader::processTextFunction(char *ptr) { + switch ((unsigned char)*ptr) { + case 0x08: + safeAddControl(INTERNAL_HYPERLINK, false); + break; + case 0x0A: + safeAddHyperlinkControl(fromNumber(twoBytes(ptr + 1))); + break; + case 0x0C: + { + int sectionNum = twoBytes(ptr + 1); + int paragraphNum = twoBytes(ptr + 3); + safeAddHyperlinkControl(fromNumber(sectionNum) + '#' + fromNumber(paragraphNum)); + myReferencedParagraphs.insert(std::make_pair(sectionNum, paragraphNum)); + break; + } + case 0x11: + changeFont((FontType)*(ptr + 1)); + break; + case 0x1A: + safeBeginParagraph(); + addImageReference(fromNumber(twoBytes(ptr + 1))); + break; + case 0x22: + if (!myParagraphStarted) { + if (myForcedEntry == 0) { + myForcedEntry = new ZLTextStyleEntry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + } + myForcedEntry->setLength( + ZLTextStyleEntry::LENGTH_LEFT_INDENT, + *(ptr + 1), ZLTextStyleEntry::SIZE_UNIT_PIXEL + ); + myForcedEntry->setLength( + ZLTextStyleEntry::LENGTH_RIGHT_INDENT, + *(ptr + 2), ZLTextStyleEntry::SIZE_UNIT_PIXEL + ); + } + break; + case 0x29: + if (!myParagraphStarted) { + if (myForcedEntry == 0) { + myForcedEntry = new ZLTextStyleEntry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + } + switch (*(ptr + 1)) { + case 0: myForcedEntry->setAlignmentType(ALIGN_LEFT); break; + case 1: myForcedEntry->setAlignmentType(ALIGN_RIGHT); break; + case 2: myForcedEntry->setAlignmentType(ALIGN_CENTER); break; + case 3: myForcedEntry->setAlignmentType(ALIGN_JUSTIFY); break; + } + } + break; + case 0x33: // just break line instead of horizontal rule (TODO: draw horizontal rule?) + safeEndParagraph(); + break; + case 0x38: + safeEndParagraph(); + break; + case 0x40: + safeAddControl(EMPHASIS, true); + break; + case 0x48: + safeAddControl(EMPHASIS, false); + break; + case 0x53: // color setting is ignored + break; + case 0x5C: + addImageReference(fromNumber(twoBytes(ptr + 3))); + break; + case 0x60: // underlined text is ignored + break; + case 0x68: // underlined text is ignored + break; + case 0x70: // strike-through text is ignored + break; + case 0x78: // strike-through text is ignored + break; + case 0x83: + case 0x85: + { + ZLUnicodeUtil::Ucs4Char symbol = + (((unsigned char)*ptr) == 0x83) ? twoBytes(ptr + 2) : fourBytes(ptr + 2); + char utf8[6]; + int len = ZLUnicodeUtil::ucs4ToUtf8(utf8, symbol); + safeBeginParagraph(); + addData(std::string(utf8, len)); + myBufferIsEmpty = false; + myBytesToSkip = *(ptr + 1); + break; + } + case 0x8E: // custom font operations are ignored + case 0x8C: + case 0x8A: + case 0x88: + break; + case 0x90: // TODO: add table processing + case 0x92: // TODO: process table + case 0x97: // TODO: process table + break; + default: // this should be impossible + //std::cerr << "Oops... function #" << (int)(unsigned char)*ptr << "\n"; + break; + } +} + +void PluckerBookReader::processTextParagraph(char *start, char *end) { + changeFont(FT_REGULAR); + while (popKind()) {} + + myParagraphStarted = false; + myBytesToSkip = 0; + + char *textStart = start; + bool functionFlag = false; + for (char *ptr = start; ptr < end; ++ptr) { + if (*ptr == 0) { + functionFlag = true; + if (ptr > textStart) { + safeBeginParagraph(); + myConvertedTextBuffer.erase(); + myConverter->convert(myConvertedTextBuffer, textStart, ptr); + addData(myConvertedTextBuffer); + myBufferIsEmpty = false; + } + } else if (functionFlag) { + int paramCounter = ((unsigned char)*ptr) % 8; + if (end - ptr > paramCounter) { + processTextFunction(ptr); + ptr += paramCounter; + } else { + ptr = end - 1; + } + functionFlag = false; + if (myBytesToSkip > 0) { + ptr += myBytesToSkip; + myBytesToSkip = 0; + } + textStart = ptr + 1; + } else { + if ((unsigned char)*ptr == 0xA0) { + *ptr = 0x20; + } + if (!myParagraphStarted && textStart == ptr && std::isspace((unsigned char)*ptr)) { + ++textStart; + } + } + } + if (end > textStart) { + safeBeginParagraph(); + myConvertedTextBuffer.erase(); + myConverter->convert(myConvertedTextBuffer, textStart, end); + addData(myConvertedTextBuffer); + myBufferIsEmpty = false; + } + safeEndParagraph(); + if (myForcedEntry != 0) { + delete myForcedEntry; + myForcedEntry = 0; + } + myDelayedControls.clear(); +} + +void PluckerBookReader::processTextRecord(std::size_t size, const std::vector<int> &pars) { + char *start = myCharBuffer; + char *end = myCharBuffer; + + for (std::vector<int>::const_iterator it = pars.begin(); it != pars.end(); ++it) { + start = end; + end = start + *it; + if (end > myCharBuffer + size) { + return; + } + myParagraphStored = false; + processTextParagraph(start, end); + if (!myParagraphStored) { + myParagraphVector->push_back(-1); + } + } +} + +void PluckerBookReader::readRecord(std::size_t recordSize) { + unsigned short uid; + PdbUtil::readUnsignedShort(*myStream, uid); + if (uid == 1) { + PdbUtil::readUnsignedShort(*myStream, myCompressionVersion); + } else { + unsigned short paragraphs; + PdbUtil::readUnsignedShort(*myStream, paragraphs); + + unsigned short size; + PdbUtil::readUnsignedShort(*myStream, size); + + unsigned char type; + myStream->read((char*)&type, 1); + + unsigned char flags; + myStream->read((char*)&flags, 1); + + switch (type) { + case 0: // text (TODO: found sample file and test this code) + case 1: // compressed text + { + std::vector<int> pars; + for (int i = 0; i < paragraphs; ++i) { + unsigned short pSize; + PdbUtil::readUnsignedShort(*myStream, pSize); + pars.push_back(pSize); + myStream->seek(2, false); + } + + bool doProcess = false; + if (type == 0) { + doProcess = myStream->read(myCharBuffer, size) == size; + } else if (myCompressionVersion == 1) { + doProcess = + DocDecompressor().decompress(*myStream, myCharBuffer, recordSize - 8 - 4 * paragraphs, size) == size; + } else if (myCompressionVersion == 2) { + myStream->seek(2, false); + doProcess = + ZLZDecompressor(recordSize - 10 - 4 * paragraphs). + decompress(*myStream, myCharBuffer, size) == size; + } + if (doProcess) { + addHyperlinkLabel(fromNumber(uid)); + myParagraphVector = &myParagraphMap[uid]; + processTextRecord(size, pars); + if ((flags & 0x1) == 0) { + insertEndOfTextParagraph(); + } + } + break; + } + case 2: // image + case 3: // compressed image + { + ZLImage *image = 0; + const ZLFile imageFile(myFile.path(), ZLMimeType::IMAGE_PALM); + if (type == 2) { + image = new ZLFileImage(imageFile, myStream->offset(), recordSize - 8); + } else if (myCompressionVersion == 1) { + image = new DocCompressedFileImage(imageFile, myStream->offset(), recordSize - 8); + } else if (myCompressionVersion == 2) { + image = new ZCompressedFileImage(imageFile, myStream->offset() + 2, recordSize - 10); + } + if (image != 0) { + addImage(fromNumber(uid), image); + } + break; + } + case 9: // category record is ignored + break; + case 10: + unsigned short typeCode; + PdbUtil::readUnsignedShort(*myStream, typeCode); + //std::cerr << "type = " << (int)type << "; "; + //std::cerr << "typeCode = " << typeCode << "\n"; + break; + case 11: // style sheet record is ignored + break; + case 12: // font page record is ignored + break; + case 13: // TODO: process tables + case 14: // TODO: process tables + break; + case 15: // multiimage + { + unsigned short columns; + unsigned short rows; + PdbUtil::readUnsignedShort(*myStream, columns); + PdbUtil::readUnsignedShort(*myStream, rows); + PluckerMultiImage *image = new PluckerMultiImage(rows, columns, model().imageMap()); + for (int i = 0; i < size / 2 - 2; ++i) { + unsigned short us; + PdbUtil::readUnsignedShort(*myStream, us); + image->addId(fromNumber(us)); + } + addImage(fromNumber(uid), image); + break; + } + default: + //std::cerr << "type = " << (int)type << "\n"; + break; + } + } +} + +bool PluckerBookReader::readDocument() { + myStream = myFile.inputStream(); + if (myStream.isNull() || !myStream->open()) { + return false; + } + + PdbHeader header; + if (!header.read(myStream)) { + myStream->close(); + return false; + } + + setMainTextModel(); + myFont = FT_REGULAR; + + for (std::vector<unsigned long>::const_iterator it = header.Offsets.begin(); it != header.Offsets.end(); ++it) { + std::size_t currentOffset = myStream->offset(); + if (currentOffset > *it) { + break; + } + myStream->seek(*it - currentOffset, false); + if (myStream->offset() != *it) { + break; + } + std::size_t recordSize = ((it != header.Offsets.end() - 1) ? *(it + 1) : myStream->sizeOfOpened()) - *it; + readRecord(recordSize); + } + myStream->close(); + + for (std::set<std::pair<int,int> >::const_iterator it = myReferencedParagraphs.begin(); it != myReferencedParagraphs.end(); ++it) { + std::map<int,std::vector<int> >::const_iterator jt = myParagraphMap.find(it->first); + if (jt != myParagraphMap.end()) { + for (unsigned int k = it->second; k < jt->second.size(); ++k) { + if (jt->second[k] != -1) { + addHyperlinkLabel(fromNumber(it->first) + '#' + fromNumber(it->second), jt->second[k]); + break; + } + } + } + } + myReferencedParagraphs.clear(); + myParagraphMap.clear(); + return true; +} diff --git a/fbreader/src/formats/pdb/PluckerBookReader.h b/fbreader/src/formats/pdb/PluckerBookReader.h new file mode 100644 index 0000000..1078f37 --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerBookReader.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __PLUCKERBOOKREADER_H__ +#define __PLUCKERBOOKREADER_H__ + +#include <set> +#include <map> + +#include <ZLEncodingConverter.h> + +#include "../../bookmodel/BookReader.h" +#include "../EncodedTextReader.h" + +class PluckerBookReader : public BookReader, public EncodedTextReader { + +public: + PluckerBookReader(BookModel &model); + ~PluckerBookReader(); + + bool readDocument(); + +private: + enum FontType { + FT_REGULAR = 0, + FT_H1 = 1, + FT_H2 = 2, + FT_H3 = 3, + FT_H4 = 4, + FT_H5 = 5, + FT_H6 = 6, + FT_BOLD = 7, + FT_TT = 8, + FT_SMALL = 9, + FT_SUB = 10, + FT_SUP = 11 + }; + + void readRecord(std::size_t recordSize); + void processTextRecord(std::size_t size, const std::vector<int> &pars); + void processTextParagraph(char *start, char *end); + void processTextFunction(char *ptr); + void setFont(FontType font, bool start); + void changeFont(FontType font); + + void safeAddControl(FBTextKind kind, bool start); + void safeAddHyperlinkControl(const std::string &id); + void safeBeginParagraph(); + void safeEndParagraph(); + + void processHeader(FontType font, bool start); + +private: + const ZLFile myFile; + shared_ptr<ZLInputStream> myStream; + FontType myFont; + char *myCharBuffer; + std::string myConvertedTextBuffer; + bool myParagraphStarted; + bool myBufferIsEmpty; + ZLTextStyleEntry *myForcedEntry; + std::vector<std::pair<FBTextKind,bool> > myDelayedControls; + std::vector<std::string> myDelayedHyperlinks; + unsigned short myCompressionVersion; + unsigned char myBytesToSkip; + + std::set<std::pair<int, int> > myReferencedParagraphs; + std::map<int, std::vector<int> > myParagraphMap; + std::vector<int> *myParagraphVector; + bool myParagraphStored; +}; + +#endif /* __PLUCKERBOOKREADER_H__ */ diff --git a/fbreader/src/formats/pdb/PluckerImages.cpp b/fbreader/src/formats/pdb/PluckerImages.cpp new file mode 100644 index 0000000..db291ab --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerImages.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> +#include <ZLInputStream.h> +#include <ZLZDecompressor.h> +#include <ZLStringUtil.h> + +#include "PluckerImages.h" +#include "DocDecompressor.h" + +const shared_ptr<std::string> ZCompressedFileImage::stringData() const { + shared_ptr<ZLInputStream> stream = myFile.inputStream(); + + shared_ptr<std::string> imageData = new std::string(); + + if (!stream.isNull() && stream->open()) { + stream->seek(myOffset, false); + ZLZDecompressor decompressor(myCompressedSize); + + static const std::size_t charBufferSize = 2048; + char *charBuffer = new char[charBufferSize]; + std::vector<std::string> buffer; + + std::size_t s; + do { + s = decompressor.decompress(*stream, charBuffer, charBufferSize); + if (s != 0) { + buffer.push_back(std::string()); + buffer.back().append(charBuffer, s); + } + } while (s == charBufferSize); + ZLStringUtil::append(*imageData, buffer); + + delete[] charBuffer; + } + + return imageData; +} + +const shared_ptr<std::string> DocCompressedFileImage::stringData() const { + shared_ptr<ZLInputStream> stream = myFile.inputStream(); + + shared_ptr<std::string> imageData = new std::string(); + + if (!stream.isNull() && stream->open()) { + stream->seek(myOffset, false); + char *buffer = new char[65535]; + std::size_t uncompressedSize = DocDecompressor().decompress(*stream, buffer, myCompressedSize, 65535); + imageData->append(buffer, uncompressedSize); + delete[] buffer; + } + + return imageData; +} + +shared_ptr<const ZLImage> PluckerMultiImage::subImage(unsigned int row, unsigned int column) const { + unsigned int index = row * myColumns + column; + if (index >= myIds.size()) { + return 0; + } + ZLImageMap::const_iterator entry = myImageMap.find(myIds[index]); + return (entry != myImageMap.end()) ? entry->second : 0; +} diff --git a/fbreader/src/formats/pdb/PluckerImages.h b/fbreader/src/formats/pdb/PluckerImages.h new file mode 100644 index 0000000..3269a29 --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerImages.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __PLUCKERIMAGES_H__ +#define __PLUCKERIMAGES_H__ + +#include <string> + +#include <ZLImage.h> +#include <ZLFile.h> +#include "../../bookmodel/BookModel.h" + +class ZCompressedFileImage : public ZLSingleImage { + +public: + ZCompressedFileImage(const ZLFile &file, std::size_t offset, std::size_t size); + const shared_ptr<std::string> stringData() const; + +private: + const ZLFile myFile; + const std::size_t myOffset; + const std::size_t myCompressedSize; +}; + +class DocCompressedFileImage : public ZLSingleImage { + +public: + DocCompressedFileImage(const ZLFile &file, std::size_t offset, std::size_t compressedSize); + const shared_ptr<std::string> stringData() const; + +private: + const ZLFile myFile; + const std::size_t myOffset; + const std::size_t myCompressedSize; +}; + +class PluckerMultiImage : public ZLMultiImage { + +public: + PluckerMultiImage(unsigned int rows, unsigned int columns, const ZLImageMap &imageMap); + + void addId(const std::string &id); + + unsigned int rows() const; + unsigned int columns() const; + shared_ptr<const ZLImage> subImage(unsigned int row, unsigned int column) const; + +private: + unsigned int myRows, myColumns; + const ZLImageMap &myImageMap; + std::vector<std::string> myIds; +}; + +inline ZCompressedFileImage::ZCompressedFileImage(const ZLFile &file, std::size_t offset, std::size_t compressedSize) : ZLSingleImage(file.mimeType()), myFile(file), myOffset(offset), myCompressedSize(compressedSize) {} + +inline DocCompressedFileImage::DocCompressedFileImage(const ZLFile &file, std::size_t offset, std::size_t compressedSize) : ZLSingleImage(file.mimeType()), myFile(file), myOffset(offset), myCompressedSize(compressedSize) {} + +inline PluckerMultiImage::PluckerMultiImage(unsigned int rows, unsigned int columns, const ZLImageMap &imageMap) : myRows(rows), myColumns(columns), myImageMap(imageMap) {} +inline void PluckerMultiImage::addId(const std::string &id) { myIds.push_back(id); } +inline unsigned int PluckerMultiImage::rows() const { return myRows; } +inline unsigned int PluckerMultiImage::columns() const { return myColumns; } + +#endif /* __PLUCKERIMAGES_H__ */ diff --git a/fbreader/src/formats/pdb/PluckerPlugin.cpp b/fbreader/src/formats/pdb/PluckerPlugin.cpp new file mode 100644 index 0000000..1ec89ba --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerPlugin.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> + +#include "PdbPlugin.h" +#include "PluckerBookReader.h" +#include "PluckerTextStream.h" + +#include "../../library/Book.h" + +bool PluckerPlugin::providesMetaInfo() const { + return false; +} + +bool PluckerPlugin::acceptsFile(const ZLFile &file) const { + return PdbPlugin::fileType(file) == "DataPlkr"; +} + +bool PluckerPlugin::readMetaInfo(Book &book) const { + shared_ptr<ZLInputStream> stream = new PluckerTextStream(book.file()); + detectEncodingAndLanguage(book, *stream); + if (book.encoding().empty()) { + return false; + } + + return true; +} + +bool PluckerPlugin::readModel(BookModel &model) const { + return PluckerBookReader(model).readDocument(); +} diff --git a/fbreader/src/formats/pdb/PluckerTextStream.cpp b/fbreader/src/formats/pdb/PluckerTextStream.cpp new file mode 100644 index 0000000..01291eb --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerTextStream.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <cstring> + +#include <ZLFile.h> +#include <ZLZDecompressor.h> + +#include "PluckerTextStream.h" +#include "PdbReader.h" +#include "DocDecompressor.h" + +PluckerTextStream::PluckerTextStream(const ZLFile &file) : PdbStream(file) { + myFullBuffer = 0; +} + +PluckerTextStream::~PluckerTextStream() { + close(); +} + +bool PluckerTextStream::open() { + if (!PdbStream::open()) { + return false; + } + + PdbUtil::readUnsignedShort(*myBase, myCompressionVersion); + + myBuffer = new char[65536]; + myFullBuffer = new char[65536]; + + myRecordIndex = 0; + + return true; +} + +bool PluckerTextStream::fillBuffer() { + while (myBufferOffset == myBufferLength) { + if (myRecordIndex + 1 > header().Offsets.size() - 1) { + return false; + } + ++myRecordIndex; + const std::size_t currentOffset = recordOffset(myRecordIndex); + if (currentOffset < myBase->offset()) { + return false; + } + myBase->seek(currentOffset, true); + const std::size_t nextOffset = recordOffset(myRecordIndex + 1); + if (nextOffset < currentOffset) { + return false; + } + processRecord(nextOffset - currentOffset); + } + return true; +} + +void PluckerTextStream::close() { + if (myFullBuffer != 0) { + delete[] myFullBuffer; + myFullBuffer = 0; + } + PdbStream::close(); +} + +void PluckerTextStream::processRecord(std::size_t recordSize) { + myBase->seek(2, false); + + unsigned short paragraphs; + PdbUtil::readUnsignedShort(*myBase, paragraphs); + + unsigned short size; + PdbUtil::readUnsignedShort(*myBase, size); + + unsigned char type; + myBase->read((char*)&type, 1); + if (type > 1) { // this record is not text record + return; + } + + myBase->seek(1, false); + + std::vector<int> pars; + for (int i = 0; i < paragraphs; ++i) { + unsigned short pSize; + PdbUtil::readUnsignedShort(*myBase, pSize); + pars.push_back(pSize); + myBase->seek(2, false); + } + + bool doProcess = false; + if (type == 0) { + doProcess = myBase->read(myFullBuffer, size) == size; + } else if (myCompressionVersion == 1) { + doProcess = + DocDecompressor().decompress(*myBase, myFullBuffer, recordSize - 8 - 4 * paragraphs, size) == size; + } else if (myCompressionVersion == 2) { + myBase->seek(2, false); + doProcess = + ZLZDecompressor(recordSize - 10 - 4 * paragraphs).decompress(*myBase, myFullBuffer, size) == size; + } + if (doProcess) { + myBufferLength = 0; + myBufferOffset = 0; + + char *start = myFullBuffer; + char *end = myFullBuffer; + + for (std::vector<int>::const_iterator it = pars.begin(); it != pars.end(); ++it) { + start = end; + end = start + *it; + if (end > myFullBuffer + size) { + break; + } + processTextParagraph(start, end); + } + } +} + +void PluckerTextStream::processTextParagraph(char *start, char *end) { + char *textStart = start; + bool functionFlag = false; + for (char *ptr = start; ptr < end; ++ptr) { + if (*ptr == 0) { + functionFlag = true; + if (ptr != textStart) { + std::memcpy(myBuffer + myBufferLength, textStart, ptr - textStart); + myBufferLength += ptr - textStart; + } + } else if (functionFlag) { + int paramCounter = ((unsigned char)*ptr) % 8; + if (end - ptr > paramCounter + 1) { + ptr += paramCounter; + } else { + ptr = end - 1; + } + functionFlag = false; + textStart = ptr + 1; + } + } + if (end != textStart) { + std::memcpy(myBuffer + myBufferLength, textStart, end - textStart); + myBufferLength += end - textStart; + } +} diff --git a/fbreader/src/formats/pdb/PluckerTextStream.h b/fbreader/src/formats/pdb/PluckerTextStream.h new file mode 100644 index 0000000..70c1182 --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerTextStream.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __PLUCKERTEXTSTREAM_H__ +#define __PLUCKERTEXTSTREAM_H__ + +#include "PdbStream.h" + +class ZLFile; + +class PluckerTextStream : public PdbStream { + +public: + PluckerTextStream(const ZLFile &file); + ~PluckerTextStream(); + bool open(); + void close(); + +private: + bool fillBuffer(); + +private: + void processRecord(std::size_t recordSize); + void processTextParagraph(char *start, char *end); + +private: + unsigned short myCompressionVersion; + char *myFullBuffer; + std::size_t myRecordIndex; +}; + +#endif /* __PLUCKERTEXTSTREAM_H__ */ diff --git a/fbreader/src/formats/pdb/PmlBookReader.cpp b/fbreader/src/formats/pdb/PmlBookReader.cpp new file mode 100644 index 0000000..e365983 --- /dev/null +++ b/fbreader/src/formats/pdb/PmlBookReader.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLTextParagraph.h> +#include <ZLUnicodeUtil.h> +#include <ZLStringUtil.h> +#include <ZLTextStyleEntry.h> + +#include "PmlBookReader.h" +#include "../../bookmodel/BookModel.h" + +PmlBookReader::PmlBookReader(BookReader &bookReader, const PlainTextFormat&, const std::string &encoding) : PmlReader(encoding), myBookReader(bookReader) { +} + +PmlBookReader::~PmlBookReader() { +} + +bool PmlBookReader::readDocument(ZLInputStream& stream) { + myBookReader.pushKind(REGULAR); + myBookReader.beginParagraph(); + myParagraphIsEmpty = true; + bool code = PmlReader::readDocument(stream); + myBookReader.endParagraph(); + return code; +} + +void PmlBookReader::addCharData(const char *data, std::size_t len, bool convert) { + if (!myBookReader.paragraphIsOpen()) { + myBookReader.beginParagraph(); + } + static std::string newString; + if (len != 0) { + if (!myConverter.isNull() && convert) { + myConverter->convert(newString, data, data + len); + } else { + newString.append(data, len); + } + if (myState.SmallCaps) { + myBookReader.addData(ZLUnicodeUtil::toUpper(newString)); + } else { + myBookReader.addData(newString); + } + newString.erase(); + if (myParagraphIsEmpty) { + myParagraphIsEmpty = false; + } + } +} + +void PmlBookReader::switchFontProperty(FontProperty property) { + if (!myBookReader.paragraphIsOpen()) { + myBookReader.beginParagraph(); + } + switch (property) { + case FONT_BOLD: + if (myState.Bold) { + myBookReader.pushKind(STRONG); + } else { + myBookReader.popKind(); + } + myBookReader.addControl(STRONG, myState.Bold); + break; + case FONT_ITALIC: + if (myState.Italic) { + if (!myState.Bold) { + myBookReader.pushKind(EMPHASIS); + myBookReader.addControl(EMPHASIS, true); + } else { + myBookReader.popKind(); + myBookReader.addControl(STRONG, false); + + myBookReader.pushKind(EMPHASIS); + myBookReader.addControl(EMPHASIS, true); + myBookReader.pushKind(STRONG); + myBookReader.addControl(STRONG, true); + } + } else { + if (!myState.Bold) { + myBookReader.addControl(EMPHASIS, false); + myBookReader.popKind(); + } else { + myBookReader.addControl(STRONG, false); + myBookReader.popKind(); + myBookReader.addControl(EMPHASIS, false); + myBookReader.popKind(); + + myBookReader.pushKind(STRONG); + myBookReader.addControl(STRONG, true); + } + } + break; + case FONT_UNDERLINED: + break; + case FONT_SUBSCRIPT: //don't have to be mixed with other style tags + if (myState.Subscript) { + myBookReader.pushKind(SUB); + } else { + myBookReader.popKind(); + } + myBookReader.addControl(SUB, myState.Subscript); + break; + case FONT_SUPERSCRIPT: //Should not be mixed with other style tags + if (myState.Superscript) { + myBookReader.pushKind(SUP); + } else { + myBookReader.popKind(); + } + myBookReader.addControl(SUP, myState.Superscript); + break; + } +} + +void PmlBookReader::newLine() { + if (myBookReader.paragraphIsOpen()) { + myBookReader.endParagraph(); + } + if (myParagraphIsEmpty) { + myBookReader.beginParagraph(ZLTextParagraph::EMPTY_LINE_PARAGRAPH); + myBookReader.endParagraph(); + } else { + myParagraphIsEmpty = true; + } + newParagraph(); +} + +void PmlBookReader::newPage() { + if (myBookReader.paragraphIsOpen()) { + myBookReader.endParagraph(); + } + //newLine(); + newParagraph(); +} + +void PmlBookReader::newParagraph() { + if (myBookReader.paragraphIsOpen()) { + myBookReader.endParagraph(); + } + myBookReader.beginParagraph(); + if (myState.Alignment != ALIGN_UNDEFINED) { + setAlignment(); + } + if (myState.FontSize != NORMAL) { + setFontSize(); + } + if (myState.IndentBlockOn && (myState.Indent != 0)) { + setIndent(); + } +} + +void PmlBookReader::setAlignment() { + ZLTextStyleEntry entry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + entry.setAlignmentType(myState.Alignment); + myBookReader.addStyleEntry(entry); +} + +void PmlBookReader::setIndent() { + ZLTextStyleEntry entry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + entry.setLength(ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, 0, ZLTextStyleEntry::SIZE_UNIT_PERCENT); + entry.setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, (short)myState.Indent, ZLTextStyleEntry::SIZE_UNIT_PERCENT); + myBookReader.addStyleEntry(entry); +} + +void PmlBookReader::setFontSize() { + if (!myBookReader.paragraphIsOpen()) { + myBookReader.beginParagraph(); + } + ZLTextStyleEntry entry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + switch(myState.FontSize) { + case SMALLER: + entry.setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_SMALLER, true); + break; + case LARGER: + entry.setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_LARGER, true); + break; + default: + break; + } + myBookReader.addStyleEntry(entry); +} + +void PmlBookReader::addLink(FBTextKind kind, const std::string &id, bool on) { + switch (kind) { + case INTERNAL_HYPERLINK: + case FOOTNOTE: + //case EXTERNAL_HYPERLINK: + //case BOOK_HYPERLINK: + if (on) { + myBookReader.addHyperlinkControl(kind, id); + } else { + myBookReader.addControl(kind, false); + } + break; + default: + break; + } +} + +void PmlBookReader::addLinkLabel(const std::string &label) { + myBookReader.addHyperlinkLabel(label); +} + +void PmlBookReader::addImageReference(const std::string &id) { + const bool stopParagraph = myBookReader.paragraphIsOpen(); + if (stopParagraph) { + myBookReader.endParagraph(); + } + myBookReader.addImageReference(id); + if (stopParagraph) { + myBookReader.beginParagraph(); + } +} diff --git a/fbreader/src/formats/pdb/PmlBookReader.h b/fbreader/src/formats/pdb/PmlBookReader.h new file mode 100644 index 0000000..22944b4 --- /dev/null +++ b/fbreader/src/formats/pdb/PmlBookReader.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __PMLBOOKREADER_H__ +#define __PMLBOOKREADER_H__ + +#include <string> + +#include "PmlReader.h" +#include "../../bookmodel/BookReader.h" +#include "../txt/PlainTextFormat.h" + +class PmlBookReader : public PmlReader { + +public: + PmlBookReader(BookReader &bookReader, const PlainTextFormat &format, const std::string &encoding); + ~PmlBookReader(); + + bool readDocument(ZLInputStream &stream); + +protected: + void addCharData(const char *data, std::size_t len, bool convert); + void addLink(FBTextKind kind, const std::string &id, bool on); + void addLinkLabel(const std::string &label); + void addImageReference(const std::string &id); + void switchFontProperty(FontProperty property); + void setFontSize(); + void newLine(); + void newPage(); + void newParagraph(); + +private: + void setAlignment(); + void setIndent(); + +private: + BookReader& myBookReader; + bool myParagraphIsEmpty; + + /*FontType myFont; + char *myCharBuffer; + std::string myConvertedTextBuffer; + bool myParagraphStarted; + bool myBufferIsEmpty; + ZLTextStyleEntry *myForcedEntry; + std::vector<std::pair<FBTextKind,bool> > myDelayedControls; + std::vector<std::string> myDelayedHyperlinks; + unsigned short myCompressionVersion; + unsigned char myBytesToSkip; + + std::set<std::pair<int, int> > myReferencedParagraphs; + std::map<int, std::vector<int> > myParagraphMap; + std::vector<int> *myParagraphVector; + bool myParagraphStored;*/ +}; + +#endif /* __PMLBOOKREADER_H__ */ diff --git a/fbreader/src/formats/pdb/PmlReader.cpp b/fbreader/src/formats/pdb/PmlReader.cpp new file mode 100644 index 0000000..712a6e0 --- /dev/null +++ b/fbreader/src/formats/pdb/PmlReader.cpp @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/* + * Information about Palm Markup Language was taken from: + * http://www.m.ereader.com/ereader/help/dropbook/pml.htm + * http://ccit205.wikispaces.com/Palm+Markup+Language+(PML) + */ + +#include <cstdlib> +#include <cctype> + +#include <ZLFile.h> +#include <ZLInputStream.h> + +#include "PmlReader.h" + +static const int pmlStreamBufferSize = 4096; + +const std::string PmlReader::ourDefaultParameter = ""; + +PmlReader::PmlReader(const std::string &encoding) : EncodedTextReader(encoding) { +} + +PmlReader::~PmlReader() { +} + +bool PmlReader::readDocument(ZLInputStream& stream) { + myStreamBuffer = new char[pmlStreamBufferSize]; + + myIsInterrupted = false; + + myState.Italic = false; + myState.Bold = false; + myState.Underlined = false; + myState.SmallCaps = false; + myState.Subscript = false; + myState.Superscript = false; + myState.Alignment = ALIGN_UNDEFINED; + myState.FontSize = NORMAL; + myState.Indent = 0; + myState.IndentBlockOn = false; + myState.BoldBlockOn = false; + myState.FootnoteLinkOn = false; + myState.InternalLinkOn = false; + myState.InvisibleText = false; + + bool code = parseDocument(stream); + + delete[] myStreamBuffer; + + return code; +} + +bool PmlReader::parseDocument(ZLInputStream &stream) { + enum { + READ_NORMAL_DATA, + READ_TAG, + READ_TAG_PARAMETER, + } parserState = READ_NORMAL_DATA; + + std::size_t tagNameLength = 0; + std::string tagName; + std::string parameterString; + + bool startParameterReading = false; + std::size_t tagCounter = 0; + static bool FLAG = true; + + while (!myIsInterrupted) { + const char *ptr = myStreamBuffer; + const char *end = myStreamBuffer + stream.read(myStreamBuffer, pmlStreamBufferSize); + if (ptr == end) { + break; + } + const char *dataStart = ptr; + bool readNextChar = true; + while (ptr != end) { + switch (parserState) { + case READ_NORMAL_DATA: + if (*ptr == '\n') { + if (ptr > dataStart) { + processCharData(dataStart, ptr - dataStart); + } + newLine(); + FLAG = true; + dataStart = ptr + 1; + } else if (FLAG && std::isspace(*ptr)) { + } else { + FLAG = false; + if (*ptr == '\\') { + if (ptr > dataStart) { + processCharData(dataStart, ptr - dataStart); + } + dataStart = ptr + 1; + tagName.erase(); + parserState = READ_TAG; + } + } + break; + case READ_TAG: + if ((ptr == dataStart) && (tagName.empty())) { + if (*ptr == '\\') { + processCharData(ptr, 1); + dataStart = ptr + 1; + parserState = READ_NORMAL_DATA; + } else { + tagNameLength = findTagLength(ptr); + if (tagNameLength == 0) { + dataStart = ptr + 1; + parserState = READ_NORMAL_DATA; + ++tagCounter; + } else { + --tagNameLength; + } + } + } else { + if (tagNameLength == 0) { + tagName.append(dataStart, ptr - dataStart); + if (*ptr == '=') { + dataStart = ptr + 1; + parameterString.erase(); + parserState = READ_TAG_PARAMETER; + ++tagCounter; + } else { + readNextChar = false; + processTag(tagName); + dataStart = ptr; + parserState = READ_NORMAL_DATA; + ++tagCounter; + } + } else { + --tagNameLength; + } + } + break; + case READ_TAG_PARAMETER: + if (*ptr == '"') { + if (!startParameterReading) { + startParameterReading = true; + dataStart = ptr + 1; + } else { + parameterString.append(dataStart, ptr - dataStart); + processTag(tagName, parameterString); + parserState = READ_NORMAL_DATA; + dataStart = ptr + 1; + startParameterReading = false; + } + } + break; + } + if (readNextChar) { + ++ptr; + } else { + readNextChar = true; + } + } + if (dataStart < end) { + switch (parserState) { + case READ_NORMAL_DATA: + processCharData(dataStart, end - dataStart); + case READ_TAG: + tagName.append(dataStart, end - dataStart); + break; + case READ_TAG_PARAMETER: + parameterString.append(dataStart, end - dataStart); + break; + default: + break; + } + } + } + return myIsInterrupted; +} + +std::size_t PmlReader::findTagLength(const char* ptr) { + switch(*ptr) { // tag action description | close | support | + case 'p': // new page | - | + | + case 'x': // new chapter and new page | + | + | + case 'c': // center alignment block | + | + | + case 'r': // right alignment block | + | + | + case 'i': // italize block | + | + | + case 'u': // underlined block | + | + | + case 'o': // overstrike block | + | - | + case 'v': // invisible text block | + | + | + case 't': // indent block | + | + | + case 'T': // indent with value | - | + | + case 'w': // embed text width rule | - | - | + case 'n': // switch to normal font | - | + | + case 's': // switch to std font |+ or \n| + | + case 'b': // switch to bold font (deprecated) |+ or \n| - | + case 'l': // switch to large font |+ or \n| + | + case 'B': // mark text as bold | + | + | + case 'k': // smaller font size and uppercase | + | + | + case 'm': // insert named image | - | + | + case 'q': // reference to another spot | + | + | + case 'Q': // link anchor for \q reference | - | + | + case '-': // soft hyphen | - | - | + case 'I': // reference index item | - | - | + return 1; + case 'X': // XN - new chapter, n indent level | + | - | + case 'S': // Sp - mark text as superscript | + | + | + // Sb - mark text as subscript | + | + | + // Sd - link to a sidebar | + | - | + case 'C': // CN - chapter title + indent level| - | - | + case 'F': // Fn - link to a footnote | + | + | + return 2; + default: + return 0; + } +} + + +void PmlReader::interrupt() { + myIsInterrupted = true; +} + + +void PmlReader::processTag(std::string &tagName, const std::string ¶meter) { + const char tagDeterminant = *tagName.data(); + switch (tagDeterminant) { + case 'p': + newPage(); + break; + case 'x': + //TODO add close tag processing + newPage(); + break; + case 'B': + if (!myState.BoldBlockOn) { + processFontProperty(FONT_BOLD); + } + break; + case 'i': + processFontProperty(FONT_ITALIC); + break; + case 'u': + processFontProperty(FONT_UNDERLINED); + break; + case 'v': + myState.InvisibleText = !myState.InvisibleText;; + break; + case 'c': + processAlignment(ALIGN_CENTER); + break; + case 'r': + processAlignment(ALIGN_RIGHT); + break; + case 'n': + processFontSize(NORMAL); + break; + case 'b': + myState.BoldBlockOn = !myState.BoldBlockOn; + processFontProperty(FONT_BOLD); + break; + case 's': + processFontSize(SMALLER); + break; + case 'l': + processFontSize(LARGER); + break; + case 'k': + myState.SmallCaps = !myState.SmallCaps; + processFontSize(SMALLER); + break; + case 'S': + if (tagName == "Sb") { + processFontProperty(FONT_SUBSCRIPT); + } else if (tagName == "Sp") { + processFontProperty(FONT_SUPERSCRIPT); + } else if (tagName == "Sd") { + //processSidebarLink(); + } + break; + case 't': + processIndent(); + break; + case 'T': + processIndent(parameter); + myState.IndentBlockOn = false; + break; + case 'w': + //addHorizontalRule(parameter); + break; + case 'F': + processLink(FOOTNOTE, parameter); + break; + case 'q': + processLink(INTERNAL_HYPERLINK, parameter); + break; + case 'Q': + addLinkLabel(parameter); + break; + case 'm': + addImageReference(parameter); + break; + default: + //std::cerr << "PmlReader: unsupported tag: name: " << tagName << " parameter: " << parameter << "\n"; + break; + } +} + +void PmlReader::processCharData(const char* data, std::size_t len, bool convert) { + if(!myState.InvisibleText) { + addCharData(data, len, convert); + } +} + +void PmlReader::processFontProperty(PmlReader::FontProperty property) { + switch (property) { + case FONT_BOLD: + myState.Bold = !myState.Bold; + switchFontProperty(FONT_BOLD); + break; + case FONT_ITALIC: + myState.Italic = !myState.Italic; + switchFontProperty(FONT_ITALIC); + break; + case FONT_UNDERLINED: + myState.Underlined = !myState.Underlined; + switchFontProperty(FONT_UNDERLINED); + break; + case FONT_SUBSCRIPT: + myState.Subscript = !myState.Subscript; + switchFontProperty(FONT_SUBSCRIPT); + break; + case FONT_SUPERSCRIPT: + myState.Superscript = !myState.Superscript; + switchFontProperty(FONT_SUPERSCRIPT); + break; + } +} + +void PmlReader::processAlignment(ZLTextAlignmentType alignment) { + if (myState.Alignment != alignment) { + myState.Alignment = alignment; + } else { + myState.Alignment = ALIGN_UNDEFINED; + } + newParagraph(); +} + +void PmlReader::processFontSize(FontSizeType sizeType) { + if (myState.FontSize != sizeType) { + myState.FontSize = sizeType; + } else { + myState.FontSize = NORMAL; + } + setFontSize(); +} + +void PmlReader::processIndent(const std::string& parameter) { + int indentPercentSize = 5; + if (!parameter.empty()) { + const int index = parameter.find('%'); + if (index != -1) { + const std::string indentValueStr = parameter.substr(0, index); + indentPercentSize = std::atoi(indentValueStr.data()); + } else { + indentPercentSize = 5; + } + } + if (!myState.IndentBlockOn) { + myState.Indent = indentPercentSize; + } else { + myState.Indent = 0; + } + myState.IndentBlockOn = !myState.IndentBlockOn; + newParagraph(); +} + +void PmlReader::processLink(FBTextKind kind, const std::string ¶meter) { + switch(kind) { + case FOOTNOTE: + myState.FootnoteLinkOn = !myState.FootnoteLinkOn; + addLink(FOOTNOTE, parameter, myState.FootnoteLinkOn); + break; + case INTERNAL_HYPERLINK: + myState.InternalLinkOn = !myState.InternalLinkOn; + if (parameter.size() > 1) { + // '#' character has to stand before link label , so we should omit '#' for getting label + addLink(INTERNAL_HYPERLINK, parameter.substr(1), myState.InternalLinkOn); + } else { + // In case trailing or corrupted tag we use parameter entirely + addLink(INTERNAL_HYPERLINK, parameter, myState.InternalLinkOn); + } + break; + default: + break; + } +} diff --git a/fbreader/src/formats/pdb/PmlReader.h b/fbreader/src/formats/pdb/PmlReader.h new file mode 100644 index 0000000..496c8d9 --- /dev/null +++ b/fbreader/src/formats/pdb/PmlReader.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/* + * Information about Palm Markup Language was taken from next sources: + * http://www.m.ereader.com/ereader/help/dropbook/pml.htm + * http://ccit205.wikispaces.com/Palm+Markup+Language+(PML) + */ + +#ifndef __PMLREADER_H__ +#define __PMLREADER_H__ + +#include <string> + +#include <ZLEncodingConverter.h> +#include <ZLTextAlignmentType.h> + +#include "../EncodedTextReader.h" +#include "../../bookmodel/FBTextKind.h" + +class ZLInputStream; + +class PmlReader : public EncodedTextReader { + +public: + virtual bool readDocument(ZLInputStream &stream); + +protected: + PmlReader(const std::string &encoding); + virtual ~PmlReader(); + +protected: + enum FontProperty { + FONT_BOLD, + FONT_ITALIC, + FONT_UNDERLINED, + FONT_SUBSCRIPT, + FONT_SUPERSCRIPT + }; + + enum FontSizeType { + NORMAL, + SMALLER, + LARGER + }; + + + virtual void addCharData(const char *data, std::size_t len, bool convert) = 0; + virtual void addLink(FBTextKind kind, const std::string &id, bool on) = 0; + virtual void addLinkLabel(const std::string &label) = 0; + virtual void addImageReference(const std::string &id) = 0; + virtual void setFontSize() = 0; + virtual void switchFontProperty(FontProperty property) = 0; + virtual void newLine() = 0; + virtual void newPage() = 0; + virtual void newParagraph() = 0; + + void interrupt(); + +private: + bool parseDocument(ZLInputStream &stream); + void processTag(std::string &tagName, const std::string ¶meter = ourDefaultParameter); + void processCharData(const char* data, std::size_t len, bool convert = true); + void processFontProperty(FontProperty property); + void processAlignment(ZLTextAlignmentType alignment); + void processFontSize(FontSizeType sizeType); + void processIndent(const std::string ¶meter =ourDefaultParameter); + void processLink(FBTextKind kind, const std::string ¶meter); + + static std::size_t findTagLength(const char* ptr); + +protected: + struct PmlReaderState { + bool Bold; + bool Italic; + bool Underlined; + bool SmallCaps; + bool Subscript; + bool Superscript; + + ZLTextAlignmentType Alignment; + FontSizeType FontSize; + + unsigned short Indent; + bool IndentBlockOn; + bool BoldBlockOn; + bool FootnoteLinkOn; + bool InternalLinkOn; + bool InvisibleText; + }; + + PmlReaderState myState; + +private: + char* myStreamBuffer; + + bool myIsInterrupted; + const static std::string ourDefaultParameter; +}; + +#endif /* __PMLREADER_H__ */ diff --git a/fbreader/src/formats/pdb/SimplePdbPlugin.cpp b/fbreader/src/formats/pdb/SimplePdbPlugin.cpp new file mode 100644 index 0000000..f4b5c30 --- /dev/null +++ b/fbreader/src/formats/pdb/SimplePdbPlugin.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> +#include <ZLInputStream.h> + +#include "PdbPlugin.h" +#include "../txt/TxtBookReader.h" +#include "../html/HtmlBookReader.h" +#include "HtmlMetainfoReader.h" +#include "../util/TextFormatDetector.h" + +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +bool SimplePdbPlugin::readMetaInfo(Book &book) const { + const ZLFile &file = book.file(); + shared_ptr<ZLInputStream> stream = createStream(file); + detectEncodingAndLanguage(book, *stream); + if (book.encoding().empty()) { + return false; + } + int readType = HtmlMetainfoReader::NONE; + if (book.title().empty()) { + readType |= HtmlMetainfoReader::TITLE; + } + if (book.authors().empty()) { + readType |= HtmlMetainfoReader::AUTHOR; + } + if ((readType != HtmlMetainfoReader::NONE) && TextFormatDetector().isHtml(*stream)) { + readType |= HtmlMetainfoReader::TAGS; + HtmlMetainfoReader metainfoReader(book, (HtmlMetainfoReader::ReadType)readType); + metainfoReader.readDocument(*stream); + } + + return true; +} + +bool SimplePdbPlugin::readModel(BookModel &model) const { + const Book &book = *model.book(); + const ZLFile &file = book.file(); + shared_ptr<ZLInputStream> stream = createStream(file); + + PlainTextFormat format(file); + if (!format.initialized()) { + PlainTextFormatDetector detector; + detector.detect(*stream, format); + } + readDocumentInternal(file, model, format, book.encoding(), *stream); + return true; +} + +void SimplePdbPlugin::readDocumentInternal(const ZLFile&, BookModel &model, const PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const { + if (TextFormatDetector().isHtml(stream)) { + HtmlBookReader("", model, format, encoding).readDocument(stream); + } else { + TxtBookReader(model, format, encoding).readDocument(stream); + } +} diff --git a/fbreader/src/formats/pdb/ZTXTPlugin.cpp b/fbreader/src/formats/pdb/ZTXTPlugin.cpp new file mode 100644 index 0000000..1465856 --- /dev/null +++ b/fbreader/src/formats/pdb/ZTXTPlugin.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> +#include <ZLInputStream.h> + +#include "PdbPlugin.h" +#include "ZTXTStream.h" +#include "../txt/PlainTextFormat.h" +#include "../util/TextFormatDetector.h" + +bool ZTXTPlugin::providesMetaInfo() const { + return false; +} + +bool ZTXTPlugin::acceptsFile(const ZLFile &file) const { + return PdbPlugin::fileType(file) == "zTXTGPlm"; +} + +shared_ptr<ZLInputStream> ZTXTPlugin::createStream(const ZLFile &file) const { + return new ZTXTStream(file); +} + +FormatInfoPage *ZTXTPlugin::createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file) { + shared_ptr<ZLInputStream> stream = createStream(file); + return new PlainTextInfoPage(dialog, file, ZLResourceKey("Text"), !TextFormatDetector().isHtml(*stream)); +} diff --git a/fbreader/src/formats/pdb/ZTXTStream.cpp b/fbreader/src/formats/pdb/ZTXTStream.cpp new file mode 100644 index 0000000..2dc549c --- /dev/null +++ b/fbreader/src/formats/pdb/ZTXTStream.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> +#include <ZLZDecompressor.h> + +#include "ZTXTStream.h" + +ZTXTStream::ZTXTStream(const ZLFile &file) : PdbStream(file) { +} + +ZTXTStream::~ZTXTStream() { + close(); +} + +bool ZTXTStream::open() { + if (!PdbStream::open()) { + return false; + } + + myBase->seek(2, false); + unsigned short recordNumber; + PdbUtil::readUnsignedShort(*myBase, recordNumber); + myMaxRecordIndex = std::min(recordNumber, (unsigned short)(header().Offsets.size() - 1)); + myBase->seek(4, false); + PdbUtil::readUnsignedShort(*myBase, myMaxRecordSize); + if (myMaxRecordSize == 0) { + return false; + } + myBuffer = new char[myMaxRecordSize]; + + myRecordIndex = 0; + + return true; +} + +bool ZTXTStream::fillBuffer() { + while (myBufferOffset == myBufferLength) { + if (myRecordIndex + 1 > myMaxRecordIndex) { + return false; + } + ++myRecordIndex; + std::size_t currentOffset = recordOffset(myRecordIndex); + // Hmm, this works on examples from manybooks.net, + // but I don't understand what this code means :(( + if (myRecordIndex == 1) { + currentOffset += 2; + } + if (currentOffset < myBase->offset()) { + return false; + } + myBase->seek(currentOffset, true); + const std::size_t nextOffset = recordOffset(myRecordIndex + 1); + if (nextOffset < currentOffset) { + return false; + } + myBufferLength = ZLZDecompressor(nextOffset - currentOffset).decompress(*myBase, myBuffer, myMaxRecordSize); + myBufferOffset = 0; + } + return true; +} diff --git a/fbreader/src/formats/pdb/ZTXTStream.h b/fbreader/src/formats/pdb/ZTXTStream.h new file mode 100644 index 0000000..f89d3a0 --- /dev/null +++ b/fbreader/src/formats/pdb/ZTXTStream.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2004-2012 Geometer Plus <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __ZTXTSTREAM_H__ +#define __ZTXTSTREAM_H__ + +#include <ZLInputStream.h> + +#include "PdbStream.h" + +class ZLFile; + +class ZTXTStream : public PdbStream { + +public: + ZTXTStream(const ZLFile &file); + ~ZTXTStream(); + bool open(); + +private: + bool fillBuffer(); + +private: + std::size_t myMaxRecordIndex; + unsigned short myMaxRecordSize; + std::size_t myRecordIndex; +}; + +#endif /* __ZTXTSTREAM_H__ */ |