diff --git a/ChangeLog b/ChangeLog index 8bd8d7015..f1475fb08 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,11 +6,13 @@ * (planned) Fixed authors list/tags list editing * (planned) CSS selectors priority -===== 2.3 (Feb ??, 2015) ===== +===== 2.3 (Feb 14, 2015) ===== * File format filter option (can be changed from the bookshelf plugin) * Samsung multi-window support * Bookshelf view item in the library * Updated Persian localisation by biomanw (https://github.com/biomanw) +* Better RTF encoding detection +* Don't fail on RTF files with .doc extension ===== 2.2.7 (Jan 30, 2015) ===== * Fixed behaviour for pages with multiple images (fb2.zip only) diff --git a/assets/resources/application/neutral.xml b/assets/resources/application/neutral.xml index 8b0712bd0..1f0f5c5f4 100644 --- a/assets/resources/application/neutral.xml +++ b/assets/resources/application/neutral.xml @@ -7,7 +7,7 @@ - + diff --git a/jni/NativeFormats/fbreader/src/formats/doc/DocPlugin.cpp b/jni/NativeFormats/fbreader/src/formats/doc/DocPlugin.cpp index e00b5816e..056da2eb3 100644 --- a/jni/NativeFormats/fbreader/src/formats/doc/DocPlugin.cpp +++ b/jni/NativeFormats/fbreader/src/formats/doc/DocPlugin.cpp @@ -27,6 +27,7 @@ #include "DocMetaInfoReader.h" #include "DocBookReader.h" #include "DocStreams.h" +#include "../rtf/RtfPlugin.h" #include "../../bookmodel/BookModel.h" #include "../../library/Book.h" @@ -36,6 +37,17 @@ DocPlugin::DocPlugin() { DocPlugin::~DocPlugin() { } +static bool isRtf(const Book &book) { + shared_ptr stream = book.file().inputStream(); + if (stream.isNull() || !stream->open()) { + return false; + } + char buffer[6] = { 0 }; + stream->read(buffer, 5); + static const std::string RTF_BYTES = "{\\rtf"; + return RTF_BYTES == buffer; +} + bool DocPlugin::providesMetainfo() const { return true; } @@ -49,6 +61,10 @@ bool DocPlugin::acceptsFile(const ZLFile &file) const { } bool DocPlugin::readMetainfo(Book &book) const { + if (::isRtf(book)) { + return RtfPlugin().readMetainfo(book); + } + if (!DocMetaInfoReader(book).readMetainfo()) { return false; } @@ -71,5 +87,9 @@ bool DocPlugin::readLanguageAndEncoding(Book &/*book*/) const { } bool DocPlugin::readModel(BookModel &model) const { + if (::isRtf(*model.book())) { + return RtfPlugin().readModel(model); + } + return DocBookReader(model, model.book()->encoding()).readBook(); } diff --git a/jni/NativeFormats/fbreader/src/formats/rtf/RtfPlugin.cpp b/jni/NativeFormats/fbreader/src/formats/rtf/RtfPlugin.cpp index 6b65b44c9..9b3a633e4 100644 --- a/jni/NativeFormats/fbreader/src/formats/rtf/RtfPlugin.cpp +++ b/jni/NativeFormats/fbreader/src/formats/rtf/RtfPlugin.cpp @@ -34,23 +34,16 @@ bool RtfPlugin::providesMetainfo() const { } const std::string RtfPlugin::supportedFileType() const { - return "rtf"; + return "RTF"; } bool RtfPlugin::readMetainfo(Book &book) const { + readLanguageAndEncoding(book); + if (!RtfDescriptionReader(book).readDocument(book.file())) { return false; } - if (book.encoding().empty()) { - book.setEncoding(ZLEncodingConverter::UTF8); - } else if (book.language().empty()) { - shared_ptr stream = new RtfReaderStream(book.file(), 50000); - if (!stream.isNull()) { - detectLanguage(book, *stream, book.encoding()); - } - } - return true; } @@ -64,5 +57,19 @@ bool RtfPlugin::readModel(BookModel &model) const { } bool RtfPlugin::readLanguageAndEncoding(Book &book) const { + if (book.encoding().empty()) { + shared_ptr stream = new RtfReaderStream(book.file(), 50000); + if (!stream.isNull()) { + detectEncodingAndLanguage(book, *stream); + } + if (book.encoding().empty()) { + book.setEncoding(ZLEncodingConverter::UTF8); + } + } else if (book.language().empty()) { + shared_ptr stream = new RtfReaderStream(book.file(), 50000); + if (!stream.isNull()) { + detectLanguage(book, *stream, book.encoding()); + } + } return true; } diff --git a/src/org/geometerplus/fbreader/book/XMLSerializer.java b/src/org/geometerplus/fbreader/book/XMLSerializer.java index 241878cf3..d2b93c032 100644 --- a/src/org/geometerplus/fbreader/book/XMLSerializer.java +++ b/src/org/geometerplus/fbreader/book/XMLSerializer.java @@ -35,9 +35,13 @@ import org.geometerplus.zlibrary.core.util.ZLColor; import org.geometerplus.zlibrary.text.view.ZLTextPosition; class XMLSerializer extends AbstractSerializer { + private StringBuilder builder() { + return new StringBuilder(""); + } + @Override public String serialize(BookQuery query) { - final StringBuilder buffer = new StringBuilder(); + final StringBuilder buffer = builder(); appendTag(buffer, "query", false, "limit", String.valueOf(query.Limit), "page", String.valueOf(query.Page) @@ -136,7 +140,7 @@ class XMLSerializer extends AbstractSerializer { @Override public String serialize(BookmarkQuery query) { - final StringBuilder buffer = new StringBuilder(); + final StringBuilder buffer = builder(); appendTag(buffer, "query", false, "visible", String.valueOf(query.Visible), "limit", String.valueOf(query.Limit), @@ -164,7 +168,7 @@ class XMLSerializer extends AbstractSerializer { @Override public String serialize(Book book) { - final StringBuilder buffer = new StringBuilder(); + final StringBuilder buffer = builder(); serialize(buffer, book); return buffer.toString(); } @@ -262,7 +266,7 @@ class XMLSerializer extends AbstractSerializer { @Override public String serialize(Bookmark bookmark) { - final StringBuilder buffer = new StringBuilder(); + final StringBuilder buffer = builder(); appendTag( buffer, "bookmark", false, "id", String.valueOf(bookmark.getId()), @@ -325,7 +329,7 @@ class XMLSerializer extends AbstractSerializer { @Override public String serialize(HighlightingStyle style) { - final StringBuilder buffer = new StringBuilder(); + final StringBuilder buffer = builder(); final ZLColor bgColor = style.getBackgroundColor(); final ZLColor fgColor = style.getForegroundColor(); appendTag(buffer, "style", true, @@ -441,23 +445,41 @@ class XMLSerializer extends AbstractSerializer { } } - private static String escapeForXml(String data) { - if (data.indexOf('&') != -1) { - data = data.replaceAll("&", "&"); + private static CharSequence escapeForXml(String data) { + final StringBuilder buffer = new StringBuilder(); + + final int len = data.length(); + for (int i = 0; i < len; ++i) { + final char ch = data.charAt(i); + switch (ch) { + case '\u0009': + buffer.append(ch); + break; + default: + if ((ch >= '\u0020' && ch <= '\uD7FF') || + (ch >= '\u0E00' && ch <= '\uFFFD')) { + buffer.append(ch); + } + break; + case '&': + buffer.append("&"); + break; + case '<': + buffer.append("<"); + break; + case '>': + buffer.append(">"); + break; + case '"': + buffer.append("""); + break; + case '\'': + buffer.append("'"); + break; + } } - if (data.indexOf('<') != -1) { - data = data.replaceAll("<", "<"); - } - if (data.indexOf('>') != -1) { - data = data.replaceAll(">", ">"); - } - if (data.indexOf('\'') != -1) { - data = data.replaceAll("'", "'"); - } - if (data.indexOf('"') != -1) { - data = data.replaceAll("\"", """); - } - return data; + + return buffer; } private static void clear(StringBuilder buffer) {