/* * Copyright (C) 2007-2011 Geometer Plus <contact@geometerplus.com> * * 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.bookmodel; import org.geometerplus.zlibrary.core.util.*; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharsetDecoder; import org.geometerplus.zlibrary.core.image.ZLImage; import org.geometerplus.zlibrary.text.model.*; public class BookReader { public final BookModel Model; private ZLTextWritableModel myCurrentTextModel = null; private boolean myTextParagraphExists = false; private boolean myTextParagraphIsNonEmpty = false; private char[] myTextBuffer = new char[4096]; private int myTextBufferLength; private StringBuilder myContentsBuffer = new StringBuilder(); private byte[] myKindStack = new byte[20]; private int myKindStackSize; private byte myHyperlinkKind; private String myHyperlinkReference = ""; private boolean myInsideTitle = false; private boolean mySectionContainsRegularContents = false; private TOCTree myCurrentContentsTree; private CharsetDecoder myByteDecoder; public BookReader(BookModel model) { Model = model; myCurrentContentsTree = model.TOCTree; } public final void setByteDecoder(CharsetDecoder decoder) { myByteDecoder = decoder; } private final void flushTextBufferToParagraph() { if (myTextBufferLength > 0) { myCurrentTextModel.addText(myTextBuffer, 0, myTextBufferLength); myTextBufferLength = 0; if (myByteDecoder != null) { myByteDecoder.reset(); } } } public final void addControl(byte kind, boolean start) { if (myTextParagraphExists) { flushTextBufferToParagraph(); myCurrentTextModel.addControl(kind, start); } if (!start && myHyperlinkReference.length() != 0 && kind == myHyperlinkKind) { myHyperlinkReference = ""; } } /* public final void addControl(ZLTextForcedControlEntry entry) { if (myTextParagraphExists) { flushTextBufferToParagraph(); myCurrentTextModel.addControl(entry); } } */ public final void pushKind(byte kind) { byte[] stack = myKindStack; if (stack.length == myKindStackSize) { stack = ZLArrayUtils.createCopy(stack, myKindStackSize, myKindStackSize << 1); myKindStack = stack; } stack[myKindStackSize++] = kind; } public final void pushOneKind(byte kind) {//hym 只加一个 html 不规范的时候 if(myKindStackSize==0){ byte[] stack = myKindStack; if (stack.length == myKindStackSize) { stack = ZLArrayUtils.createCopy(stack, myKindStackSize, myKindStackSize << 1); myKindStack = stack; } stack[myKindStackSize++] = kind; } } public final boolean popKind() { if (myKindStackSize != 0) { --myKindStackSize; return true; } return false; } public final boolean popAllKind() {//hym html 不规范的时候 if (myKindStackSize != 0) { myKindStackSize=0; return true; } return false; } public final void beginParagraph() { beginParagraph(ZLTextParagraph.Kind.TEXT_PARAGRAPH); } public final void beginParagraph(byte kind) { endParagraph(); final ZLTextWritableModel textModel = myCurrentTextModel; if (textModel != null) { textModel.createParagraph(kind); final byte[] stack = myKindStack; final int size = myKindStackSize; for (int i = 0; i < size; ++i) { textModel.addControl(stack[i], true); } if (myHyperlinkReference.length() != 0) { textModel.addHyperlinkControl(myHyperlinkKind, hyperlinkType(myHyperlinkKind), myHyperlinkReference); } myTextParagraphExists = true; } } public final void endParagraph() { if (myTextParagraphExists) { flushTextBufferToParagraph(); myTextParagraphExists = false; myTextParagraphIsNonEmpty = false; } } private final void insertEndParagraph(byte kind) { final ZLTextWritableModel textModel = myCurrentTextModel; if (textModel != null && mySectionContainsRegularContents) { int size = textModel.getParagraphsNumber(); if (size > 0 && textModel.getParagraph(size - 1).getKind() != kind) { textModel.createParagraph(kind); mySectionContainsRegularContents = false; } } } public final void insertEndOfSectionParagraph() { insertEndParagraph(ZLTextParagraph.Kind.END_OF_SECTION_PARAGRAPH); } /* public final void insertEndOfTextParagraph() { insertEndParagraph(ZLTextParagraph.Kind.END_OF_TEXT_PARAGRAPH); } */ public final void unsetCurrentTextModel() { if (myCurrentTextModel != null) { myCurrentTextModel.stopReading(); } myCurrentTextModel = null; } public final void enterTitle() { myInsideTitle = true; } public final void exitTitle() { myInsideTitle = false; } public final void setMainTextModel() { if ((myCurrentTextModel != null) && (myCurrentTextModel != Model.BookTextModel)) { myCurrentTextModel.stopReading(); } myCurrentTextModel = (ZLTextWritableModel)Model.BookTextModel; } public final void setFootnoteTextModel(String id) { if ((myCurrentTextModel != null) && (myCurrentTextModel != Model.BookTextModel)) { myCurrentTextModel.stopReading(); } myCurrentTextModel = (ZLTextWritableModel)Model.getFootnoteModel(id); } public final void addData(char[] data) { addData(data, 0, data.length, false); } public final void addData(char[] data, int offset, int length, boolean direct) { if (!myTextParagraphExists || length == 0) { return; } if (!myInsideTitle && !mySectionContainsRegularContents) { while (length > 0 && Character.isWhitespace(data[offset])) { --length; ++offset; } if (length == 0) { return; } } myTextParagraphIsNonEmpty = true; if (direct && (myTextBufferLength == 0) && !myInsideTitle) { myCurrentTextModel.addText(data, offset, length); } else { final int oldLength = myTextBufferLength; final int newLength = oldLength + length; if (myTextBuffer.length < newLength) { myTextBuffer = ZLArrayUtils.createCopy(myTextBuffer, oldLength, newLength); } System.arraycopy(data, offset, myTextBuffer, oldLength, length); myTextBufferLength = newLength; if (myInsideTitle) { addContentsData(myTextBuffer, oldLength, length); } } if (!myInsideTitle) { mySectionContainsRegularContents = true; } } private byte[] myUnderflowByteBuffer = new byte[4]; private int myUnderflowLength; public final void addByteData(byte[] data, int start, int length) { if (!myTextParagraphExists || (length == 0)) { return; } myTextParagraphIsNonEmpty = true; final int oldLength = myTextBufferLength; if (myTextBuffer.length < oldLength + length) { myTextBuffer = ZLArrayUtils.createCopy(myTextBuffer, oldLength, oldLength + length); } final CharBuffer cb = CharBuffer.wrap(myTextBuffer, myTextBufferLength, length); if (myUnderflowLength > 0) { int l = myUnderflowLength; while (length-- > 0) { myUnderflowByteBuffer[l++] = data[start++]; final ByteBuffer ubb = ByteBuffer.wrap(myUnderflowByteBuffer); myByteDecoder.decode(ubb, cb, false); if (cb.position() != oldLength) { myUnderflowLength = 0; break; } } if (length == 0) { myUnderflowLength = l; return; } } ByteBuffer bb = ByteBuffer.wrap(data, start, length); myByteDecoder.decode(bb, cb, false); myTextBufferLength = cb.position(); int rem = bb.remaining(); if (rem > 0) { for (int i = 0, j = start + length - rem; i < rem;) { myUnderflowByteBuffer[i++] = data[j++]; } myUnderflowLength = rem; } if (myInsideTitle) { addContentsData(myTextBuffer, oldLength, myTextBufferLength - oldLength); } else { mySectionContainsRegularContents = true; } } private static byte hyperlinkType(byte kind) { return (kind == FBTextKind.EXTERNAL_HYPERLINK) ? FBHyperlinkType.EXTERNAL : FBHyperlinkType.INTERNAL; } public final void addHyperlinkControl(byte kind, String label) { if (myTextParagraphExists) { flushTextBufferToParagraph(); myCurrentTextModel.addHyperlinkControl(kind, hyperlinkType(kind), label); } myHyperlinkKind = kind; myHyperlinkReference = label; } public final void addHyperlinkLabel(String label) { final ZLTextWritableModel textModel = myCurrentTextModel; if (textModel != null) { int paragraphNumber = textModel.getParagraphsNumber(); if (myTextParagraphExists) { --paragraphNumber; } Model.addHyperlinkLabel(label, textModel, paragraphNumber); } } public final void addHyperlinkLabel(String label, int paragraphIndex) { Model.addHyperlinkLabel(label, myCurrentTextModel, paragraphIndex); } public final void addContentsData(char[] data) { addContentsData(data, 0, data.length); } public final void addContentsData(char[] data, int offset, int length) { if ((length != 0) && (myCurrentContentsTree != null)) { myContentsBuffer.append(data, offset, length); } } public final boolean hasContentsData() { return myContentsBuffer.length() > 0; } //hym 生成目录?? 开始生成目录 如下 形成树结构 /**hym * beginContentsParagraph() * addContentsData * beginContentsParagraph() * addContentsData * endContentsParagraph * * beginContentsParagraph() * addContentsData * endContentsParagraph * endContentsParagraph */ public final void beginContentsParagraph(int referenceNumber) { beginContentsParagraph(Model.BookTextModel, referenceNumber); } public final void beginContentsParagraph(ZLTextModel bookTextModel, int referenceNumber) { final ZLTextModel textModel = myCurrentTextModel; // System.out.println("-add-TOCTree-|"); // if (textModel == bookTextModel) {//XXX hym? 为什么要做这个判断呢? if (true) {//hym 改 if (referenceNumber == -1) { referenceNumber = textModel.getParagraphsNumber(); } TOCTree parentTree = myCurrentContentsTree; // System.out.println("--TOCTree-|"+parentTree.Level+"|"+parentTree.hasChildren()); if (parentTree.Level > 0) { if (myContentsBuffer.length() > 0) { parentTree.setText(myContentsBuffer.toString()); myContentsBuffer.delete(0, myContentsBuffer.length()); } else if (parentTree.getText() == null) { parentTree.setText("..."); } } else { myContentsBuffer.delete(0, myContentsBuffer.length()); } TOCTree tree = new TOCTree(parentTree); tree.setReference(myCurrentTextModel, referenceNumber); myCurrentContentsTree = tree; } } public final void endContentsParagraph() { final TOCTree tree = myCurrentContentsTree; if (tree.Level == 0) { myContentsBuffer.delete(0, myContentsBuffer.length()); return; } if (myContentsBuffer.length() > 0) { tree.setText(myContentsBuffer.toString()); myContentsBuffer.delete(0, myContentsBuffer.length()); } else if (tree.getText() == null) { tree.setText("..."); } myCurrentContentsTree = tree.Parent; } //hym 生成目录?? public final void setReference(int contentsParagraphNumber, int referenceNumber) { setReference(contentsParagraphNumber, myCurrentTextModel, referenceNumber); } public final void setReference(int contentsParagraphNumber, ZLTextWritableModel textModel, int referenceNumber) { final TOCTree contentsTree = Model.TOCTree; if (contentsParagraphNumber < contentsTree.getSize()) { contentsTree.getTreeByParagraphNumber(contentsParagraphNumber).setReference( textModel, referenceNumber ); } } public final boolean paragraphIsOpen() { return myTextParagraphExists; } public boolean paragraphIsNonEmpty() { return myTextParagraphIsNonEmpty; } public final boolean contentsParagraphIsOpen() { return myCurrentContentsTree.Level > 0; } public final void beginContentsParagraph() { beginContentsParagraph(-1); } public final void addImageReference(String ref) { addImageReference(ref, (short)0); } public final void addImageReference(String ref, short vOffset) { final ZLTextWritableModel textModel = myCurrentTextModel; if (textModel != null) { mySectionContainsRegularContents = true; if (myTextParagraphExists) { flushTextBufferToParagraph(); textModel.addImage(ref, vOffset); } else { beginParagraph(ZLTextParagraph.Kind.TEXT_PARAGRAPH); textModel.addControl(FBTextKind.IMAGE, true); textModel.addImage(ref, vOffset); textModel.addControl(FBTextKind.IMAGE, false); endParagraph(); } } } public final void addImage(String id, ZLImage image) { Model.addImage(id, image); // Model. // image. //保存 当前图片的 段落号码 hym ZLTextWritableModel textModel = myCurrentTextModel; // System.out.println("----hym--textModel"+textModel); textModel.addImgList(textModel.getParagraphsNumber()+"");//addImage(id, image); } public final void addFixedHSpace(short length) { if (myTextParagraphExists) { myCurrentTextModel.addFixedHSpace(length); } } }