summaryrefslogtreecommitdiffstats
path: root/fbreader/src/formats/pdb
diff options
context:
space:
mode:
Diffstat (limited to 'fbreader/src/formats/pdb')
-rw-r--r--fbreader/src/formats/pdb/BitReader.cpp57
-rw-r--r--fbreader/src/formats/pdb/BitReader.h39
-rw-r--r--fbreader/src/formats/pdb/DocDecompressor.cpp103
-rw-r--r--fbreader/src/formats/pdb/DocDecompressor.h36
-rw-r--r--fbreader/src/formats/pdb/EReaderPlugin.cpp125
-rw-r--r--fbreader/src/formats/pdb/EReaderStream.cpp289
-rw-r--r--fbreader/src/formats/pdb/EReaderStream.h88
-rw-r--r--fbreader/src/formats/pdb/HtmlMetainfoReader.cpp89
-rw-r--r--fbreader/src/formats/pdb/HtmlMetainfoReader.h60
-rw-r--r--fbreader/src/formats/pdb/HuffDecompressor.cpp192
-rw-r--r--fbreader/src/formats/pdb/HuffDecompressor.h63
-rw-r--r--fbreader/src/formats/pdb/MobipocketHtmlBookReader.cpp356
-rw-r--r--fbreader/src/formats/pdb/MobipocketHtmlBookReader.h89
-rw-r--r--fbreader/src/formats/pdb/MobipocketPlugin.cpp229
-rw-r--r--fbreader/src/formats/pdb/PalmDocLikePlugin.cpp40
-rw-r--r--fbreader/src/formats/pdb/PalmDocLikeStream.cpp78
-rw-r--r--fbreader/src/formats/pdb/PalmDocLikeStream.h58
-rw-r--r--fbreader/src/formats/pdb/PalmDocPlugin.cpp54
-rw-r--r--fbreader/src/formats/pdb/PalmDocStream.cpp209
-rw-r--r--fbreader/src/formats/pdb/PalmDocStream.h50
-rw-r--r--fbreader/src/formats/pdb/PdbPlugin.cpp69
-rw-r--r--fbreader/src/formats/pdb/PdbPlugin.h119
-rw-r--r--fbreader/src/formats/pdb/PdbReader.cpp108
-rw-r--r--fbreader/src/formats/pdb/PdbReader.h82
-rw-r--r--fbreader/src/formats/pdb/PdbStream.cpp109
-rw-r--r--fbreader/src/formats/pdb/PdbStream.h72
-rw-r--r--fbreader/src/formats/pdb/PluckerBookReader.cpp528
-rw-r--r--fbreader/src/formats/pdb/PluckerBookReader.h89
-rw-r--r--fbreader/src/formats/pdb/PluckerImages.cpp80
-rw-r--r--fbreader/src/formats/pdb/PluckerImages.h79
-rw-r--r--fbreader/src/formats/pdb/PluckerPlugin.cpp48
-rw-r--r--fbreader/src/formats/pdb/PluckerTextStream.cpp159
-rw-r--r--fbreader/src/formats/pdb/PluckerTextStream.h48
-rw-r--r--fbreader/src/formats/pdb/PmlBookReader.cpp227
-rw-r--r--fbreader/src/formats/pdb/PmlBookReader.h73
-rw-r--r--fbreader/src/formats/pdb/PmlReader.cpp407
-rw-r--r--fbreader/src/formats/pdb/PmlReader.h117
-rw-r--r--fbreader/src/formats/pdb/SimplePdbPlugin.cpp75
-rw-r--r--fbreader/src/formats/pdb/ZTXTPlugin.cpp43
-rw-r--r--fbreader/src/formats/pdb/ZTXTStream.cpp77
-rw-r--r--fbreader/src/formats/pdb/ZTXTStream.h45
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 &parameter) {
+ 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 &parameter) {
+ 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 &parameter = 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 &parameter =ourDefaultParameter);
+ void processLink(FBTextKind kind, const std::string &parameter);
+
+ 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__ */