diff --git a/data/formats/fb2/FBReaderVersion.ent b/data/formats/fb2/FBReaderVersion.ent index e6d9705b9..2ca45276d 100644 --- a/data/formats/fb2/FBReaderVersion.ent +++ b/data/formats/fb2/FBReaderVersion.ent @@ -1 +1 @@ - + diff --git a/platform/android/AndroidManifest.xml b/platform/android/AndroidManifest.xml index 535643f84..c5309c20a 100644 --- a/platform/android/AndroidManifest.xml +++ b/platform/android/AndroidManifest.xml @@ -1,5 +1,5 @@ - + diff --git a/src/org/geometerplus/fbreader/bookmodel/BookReader.java b/src/org/geometerplus/fbreader/bookmodel/BookReader.java index 42c139214..f3b49aef3 100644 --- a/src/org/geometerplus/fbreader/bookmodel/BookReader.java +++ b/src/org/geometerplus/fbreader/bookmodel/BookReader.java @@ -32,7 +32,7 @@ import org.geometerplus.zlibrary.text.model.*; public class BookReader { public final BookModel Model; - private ZLTextModel myCurrentTextModel = null; + private ZLTextWritableModel myCurrentTextModel = null; private boolean myTextParagraphExists = false; @@ -111,7 +111,7 @@ public class BookReader { } public final void beginParagraph(byte kind) { - final ZLTextModel textModel = myCurrentTextModel; + final ZLTextWritableModel textModel = myCurrentTextModel; if (textModel != null) { textModel.createParagraph(kind); final byte[] stack = myKindStack; @@ -139,7 +139,7 @@ public class BookReader { } private final void insertEndParagraph(byte kind) { - final ZLTextModel textModel = myCurrentTextModel; + final ZLTextWritableModel textModel = myCurrentTextModel; if ((textModel != null) && mySectionContainsRegularContents) { int size = textModel.getParagraphsNumber(); if ((size > 0) && (textModel.getParagraph(size-1).getKind() != kind)) { @@ -170,11 +170,11 @@ public class BookReader { } public final void setMainTextModel() { - myCurrentTextModel = Model.BookTextModel; + myCurrentTextModel = (ZLTextWritableModel)Model.BookTextModel; } public final void setFootnoteTextModel(String id) { - myCurrentTextModel = Model.getFootnoteModel(id); + myCurrentTextModel = (ZLTextWritableModel)Model.getFootnoteModel(id); } public final void addData(char[] data) { @@ -268,7 +268,7 @@ public class BookReader { } public final void addHyperlinkLabel(String label) { - final ZLTextModel textModel = myCurrentTextModel; + final ZLTextWritableModel textModel = myCurrentTextModel; if (textModel != null) { int paragraphNumber = textModel.getParagraphsNumber(); if (myTextParagraphExists) { @@ -338,7 +338,7 @@ public class BookReader { setReference(contentsParagraphNumber, myCurrentTextModel, referenceNumber); } - public final void setReference(int contentsParagraphNumber, ZLTextModel textModel, int referenceNumber) { + public final void setReference(int contentsParagraphNumber, ZLTextWritableModel textModel, int referenceNumber) { final TOCTree contentsTree = Model.TOCTree; if (contentsParagraphNumber < contentsTree.getSize()) { contentsTree.getTree(contentsParagraphNumber).setReference( @@ -364,7 +364,7 @@ public class BookReader { } public final void addImageReference(String ref, short vOffset) { - final ZLTextModel textModel = myCurrentTextModel; + final ZLTextWritableModel textModel = myCurrentTextModel; if (textModel != null) { mySectionContainsRegularContents = true; if (myTextParagraphExists) { diff --git a/src/org/geometerplus/fbreader/fbreader/FBReader.java b/src/org/geometerplus/fbreader/fbreader/FBReader.java index 9de397c3d..164753f8a 100644 --- a/src/org/geometerplus/fbreader/fbreader/FBReader.java +++ b/src/org/geometerplus/fbreader/fbreader/FBReader.java @@ -136,7 +136,7 @@ public final class FBReader extends ZLApplication { if (book == null) { book = Library.Instance().getRecentBook(); } - if (book == null) { + if ((book == null) || !book.File.exists()) { book = Book.getByFile(Library.Instance().getHelpFile()); } openBookInternal(book, null); diff --git a/src/org/geometerplus/fbreader/fbreader/FBView.java b/src/org/geometerplus/fbreader/fbreader/FBView.java index 6751c0ed1..cd5fe512c 100644 --- a/src/org/geometerplus/fbreader/fbreader/FBView.java +++ b/src/org/geometerplus/fbreader/fbreader/FBView.java @@ -25,6 +25,7 @@ import org.geometerplus.zlibrary.core.application.ZLApplication; import org.geometerplus.zlibrary.core.util.ZLColor; import org.geometerplus.zlibrary.core.view.ZLPaintContext; import org.geometerplus.zlibrary.core.library.ZLibrary; +import org.geometerplus.zlibrary.text.model.ZLTextModel; import org.geometerplus.zlibrary.text.view.*; import org.geometerplus.fbreader.bookmodel.FBTextKind; @@ -38,6 +39,11 @@ public final class FBView extends ZLTextView { myReader = reader; } + public void setModel(ZLTextModel model) { + myIsManualScrollingActive = false; + super.setModel(model); + } + final void doShortScroll(boolean forward) { if (!moveHyperlinkPointer(forward)) { scrollPage(forward, ZLTextView.ScrollingMode.SCROLL_LINES, 1); diff --git a/src/org/geometerplus/fbreader/formats/pdb/HuffdicDecompressor.java b/src/org/geometerplus/fbreader/formats/pdb/HuffdicDecompressor.java new file mode 100644 index 000000000..e1601b487 --- /dev/null +++ b/src/org/geometerplus/fbreader/formats/pdb/HuffdicDecompressor.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2007-2009 Geometer Plus + * + * 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. + */ + +package org.geometerplus.fbreader.formats.pdb; + +import java.io.*; + +public abstract class HuffdicDecompressor { + public static int decompress(InputStream stream, byte[] targetBuffer, int compressedSize) throws IOException { + final byte[] sourceBuffer = new byte[compressedSize]; + + if (stream.read(sourceBuffer) != compressedSize) { + return 0; + } + + int sourceIndex = 0; + int targetIndex = 0; + + int count0 = 0; + int count1 = 0; + int count2 = 0; + int count3 = 0; + try { + while (true) { + final byte token = sourceBuffer[sourceIndex++]; + switch (token) { + default: + ++count0; + targetBuffer[targetIndex++] = token; + break; + case 1: case 2: case 3: case 4: + case 5: case 6: case 7: case 8: + ++count1; + System.arraycopy(sourceBuffer, sourceIndex, targetBuffer, targetIndex, token); + sourceIndex += token; + targetIndex += token; + break; + case -64: case -63: case -62: case -61: + case -60: case -59: case -58: case -57: + case -56: case -55: case -54: case -53: + case -52: case -51: case -50: case -49: + case -48: case -47: case -46: case -45: + case -44: case -43: case -42: case -41: + case -40: case -39: case -38: case -37: + case -36: case -35: case -34: case -33: + case -32: case -31: case -30: case -29: + case -28: case -27: case -26: case -25: + case -24: case -23: case -22: case -21: + case -20: case -19: case -18: case -17: + case -16: case -15: case -14: case -13: + case -12: case -11: case -10: case -9: + case -8: case -7: case -6: case -5: + case -4: case -3: case -2: case -1: + ++count2; + targetBuffer[targetIndex++] = ' '; + targetBuffer[targetIndex++] = (byte)(token ^ 0x80); + break; + case -128: case -127: case -126: case -125: + case -124: case -123: case -122: case -121: + case -120: case -119: case -118: case -117: + case -116: case -115: case -114: case -113: + case -112: case -111: case -110: case -109: + case -108: case -107: case -106: case -105: + case -104: case -103: case -102: case -101: + case -100: case -99: case -98: case -97: + case -96: case -95: case -94: case -93: + case -92: case -91: case -90: case -89: + case -88: case -87: case -86: case -85: + case -84: case -83: case -82: case -81: + case -80: case -79: case -78: case -77: + case -76: case -75: case -74: case -73: + case -72: case -71: case -70: case -69: + case -68: case -67: case -66: case -65: + ++count3; + final int N = ((token & 0x3F) << 8) + (sourceBuffer[sourceIndex++] & 0xFF); + int copyLength = (N & 7) + 3; + int srcIndex = targetIndex - (N >> 3); + if (targetIndex >= srcIndex + copyLength) { + System.arraycopy(targetBuffer, srcIndex, targetBuffer, targetIndex, copyLength); + targetIndex += copyLength; + } else { + while (copyLength-- > 0) { + targetBuffer[targetIndex++] = targetBuffer[srcIndex++]; + } + } + break; + } + } + } catch (Exception e) { + if (targetIndex > targetBuffer.length) { + targetIndex = targetBuffer.length; + } + } + + return targetIndex; + } +} diff --git a/src/org/geometerplus/fbreader/formats/pdb/MobipocketStream.java b/src/org/geometerplus/fbreader/formats/pdb/MobipocketStream.java index 9532e731e..0ab6b94a6 100644 --- a/src/org/geometerplus/fbreader/formats/pdb/MobipocketStream.java +++ b/src/org/geometerplus/fbreader/formats/pdb/MobipocketStream.java @@ -30,17 +30,7 @@ class MobipocketStream extends PalmDocLikeStream { super(file); myFileSize = (int)file.size(); - final int version = PdbUtil.readShort(myBase); - switch (version) { - case 1: - myIsCompressed = false; - break; - case 2: - myIsCompressed = true; - break; - default: - throw new IOException("Unsupported compression type: " + version); - } + myCompressionType = PdbUtil.readShort(myBase); PdbUtil.skip(myBase, 6); myMaxRecordIndex = Math.min(PdbUtil.readShort(myBase), myHeader.Offsets.length - 1); final int maxRecordSize = PdbUtil.readShort(myBase); diff --git a/src/org/geometerplus/fbreader/formats/pdb/PalmDocLikeStream.java b/src/org/geometerplus/fbreader/formats/pdb/PalmDocLikeStream.java index e58a0d0b9..2fbe7ccf5 100644 --- a/src/org/geometerplus/fbreader/formats/pdb/PalmDocLikeStream.java +++ b/src/org/geometerplus/fbreader/formats/pdb/PalmDocLikeStream.java @@ -26,7 +26,12 @@ import org.geometerplus.zlibrary.core.filesystem.ZLFile; abstract class PalmDocLikeStream extends PdbStream { protected int myMaxRecordIndex; protected int myRecordIndex; - protected boolean myIsCompressed; + protected interface CompressionType { + int NONE = 1; + int DOC = 2; + int HUFFDIC = 17480; + } + protected int myCompressionType; private final long myFileSize; @@ -54,11 +59,20 @@ abstract class PalmDocLikeStream extends PdbStream { } final short recordSize = (short)(nextOffset - currentOffset); - if (myIsCompressed) { - myBufferLength = (short)DocDecompressor.decompress(myBase, myBuffer, recordSize); - } else { - myBase.read(myBuffer, 0, recordSize); - myBufferLength = recordSize; + switch (myCompressionType) { + case CompressionType.NONE: + myBase.read(myBuffer, 0, recordSize); + myBufferLength = recordSize; + break; + case CompressionType.DOC: + myBufferLength = (short)DocDecompressor.decompress(myBase, myBuffer, recordSize); + break; + //case CompressionType.HUFFDIC: + // myBufferLength = (short)HuffdicDecompressor.decompress(myBase, myBuffer, recordSize); + // break; + default: + // Unsupported compression type + return false; } } catch (IOException e) { return false; diff --git a/src/org/geometerplus/zlibrary/core/view/ZLView.java b/src/org/geometerplus/zlibrary/core/view/ZLView.java index c45447fa9..f1f9dfa6e 100644 --- a/src/org/geometerplus/zlibrary/core/view/ZLView.java +++ b/src/org/geometerplus/zlibrary/core/view/ZLView.java @@ -61,7 +61,7 @@ abstract public class ZLView { public abstract int getScrollbarThumbPosition(int viewPage); public abstract int getScrollbarThumbLength(int viewPage); - final public void scrollTo(int viewPage, int shift) { + public final void scrollTo(int viewPage, int shift) { ZLViewWidget viewWidget = ZLApplication.Instance().getViewWidget(); if (viewWidget != null) { viewWidget.scrollTo(viewPage, shift); diff --git a/src/org/geometerplus/zlibrary/text/model/ZLTextModel.java b/src/org/geometerplus/zlibrary/text/model/ZLTextModel.java index b6655e404..acf3b7465 100644 --- a/src/org/geometerplus/zlibrary/text/model/ZLTextModel.java +++ b/src/org/geometerplus/zlibrary/text/model/ZLTextModel.java @@ -21,25 +21,12 @@ package org.geometerplus.zlibrary.text.model; import java.util.ArrayList; -import org.geometerplus.zlibrary.core.image.ZLImageMap; - public interface ZLTextModel { String getId(); int getParagraphsNumber(); ZLTextParagraph getParagraph(int index); - void createParagraph(byte kind); - - void addControl(byte textKind, boolean isStart); - void addText(char[] text); - void addText(char[] text, int offset, int length); - - void addControl(ZLTextForcedControlEntry entry); - void addHyperlinkControl(byte textKind, byte hyperlinkType, String id); - void addImage(String id, ZLImageMap imageMap, short vOffset); - void addFixedHSpace(short length); - void removeAllMarks(); ZLTextMark getFirstMark(); ZLTextMark getLastMark(); diff --git a/src/org/geometerplus/zlibrary/text/model/ZLTextPlainModel.java b/src/org/geometerplus/zlibrary/text/model/ZLTextPlainModel.java index 55c9c95a7..614edbc3e 100644 --- a/src/org/geometerplus/zlibrary/text/model/ZLTextPlainModel.java +++ b/src/org/geometerplus/zlibrary/text/model/ZLTextPlainModel.java @@ -24,7 +24,7 @@ import org.geometerplus.zlibrary.core.util.*; import org.geometerplus.zlibrary.core.image.ZLImageMap; -public final class ZLTextPlainModel implements ZLTextModel { +public final class ZLTextPlainModel implements ZLTextWritableModel { private final String myId; private int[] myStartEntryIndices; diff --git a/src/org/geometerplus/zlibrary/text/model/ZLTextWritableModel.java b/src/org/geometerplus/zlibrary/text/model/ZLTextWritableModel.java new file mode 100644 index 000000000..e2fd1dfd5 --- /dev/null +++ b/src/org/geometerplus/zlibrary/text/model/ZLTextWritableModel.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2007-2009 Geometer Plus + * + * 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. + */ + +package org.geometerplus.zlibrary.text.model; + +import org.geometerplus.zlibrary.core.image.ZLImageMap; + +public interface ZLTextWritableModel extends ZLTextModel { + void createParagraph(byte kind); + + void addControl(byte textKind, boolean isStart); + void addText(char[] text); + void addText(char[] text, int offset, int length); + + void addControl(ZLTextForcedControlEntry entry); + void addHyperlinkControl(byte textKind, byte hyperlinkType, String id); + void addImage(String id, ZLImageMap imageMap, short vOffset); + void addFixedHSpace(short length); +} diff --git a/src/org/geometerplus/zlibrary/text/view/ZLTextView.java b/src/org/geometerplus/zlibrary/text/view/ZLTextView.java index 0364a7264..ce45b0518 100644 --- a/src/org/geometerplus/zlibrary/text/view/ZLTextView.java +++ b/src/org/geometerplus/zlibrary/text/view/ZLTextView.java @@ -61,10 +61,14 @@ public abstract class ZLTextView extends ZLTextViewBase { mySelectionModel = new ZLTextSelectionModel(this); } - public final void setModel(ZLTextModel model) { + public void setModel(ZLTextModel model) { ZLTextParagraphCursorCache.clear(); myModel = model; + myCurrentPage.reset(); + myPreviousPage.reset(); + myNextPage.reset(); + setScrollingActive(false); if (myModel != null) { final int paragraphsNumber = myModel.getParagraphsNumber(); if (paragraphsNumber > 0) { @@ -74,8 +78,6 @@ public abstract class ZLTextView extends ZLTextViewBase { myTextSize[i + 1] = myTextSize[i] + myModel.getParagraphTextLength(i); } myCurrentPage.moveStartCursor(ZLTextParagraphCursor.cursor(myModel, 0)); - myPreviousPage.reset(); - myNextPage.reset(); } } } @@ -500,6 +502,9 @@ public abstract class ZLTextView extends ZLTextViewBase { do { resetTextStyle(); final ZLTextParagraphCursor paragraphCursor = result.getParagraphCursor(); + if (paragraphCursor == null) { + break; + } final int wordIndex = result.getElementIndex(); applyControls(paragraphCursor, 0, wordIndex); ZLTextLineInfo info = new ZLTextLineInfo(paragraphCursor, wordIndex, result.getCharIndex(), getTextStyle()); @@ -996,6 +1001,9 @@ public abstract class ZLTextView extends ZLTextViewBase { private int paragraphSize(ZLTextWordCursor cursor, boolean beforeCurrentPosition, int unit) { final ZLTextParagraphCursor paragraphCursor = cursor.getParagraphCursor(); + if (paragraphCursor == null) { + return 0; + } final int endElementIndex = beforeCurrentPosition ? cursor.getElementIndex() : paragraphCursor.getParagraphLength(); @@ -1017,6 +1025,9 @@ public abstract class ZLTextView extends ZLTextViewBase { private void skip(ZLTextWordCursor cursor, int unit, int size) { final ZLTextParagraphCursor paragraphCursor = cursor.getParagraphCursor(); + if (paragraphCursor == null) { + return; + } final int endElementIndex = paragraphCursor.getParagraphLength(); resetTextStyle();