1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-05 10:49:24 +02:00
FBReaderJ/src/org/geometerplus/fbreader/formats/fb2/FB2Reader.java
Nikolay Pultsin bcc798d801 updated copyright notices
git-svn-id: https://only.mawhrin.net/repos/FBReaderJ/trunk@1055 6a642e6f-84f6-412e-ac94-c4a38d5a04b0
2010-02-15 12:27:53 +00:00

404 lines
10 KiB
Java

/*
* Copyright (C) 2007-2010 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.formats.fb2;
import java.util.*;
import org.geometerplus.zlibrary.core.library.ZLibrary;
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
import org.geometerplus.fbreader.bookmodel.*;
import org.geometerplus.zlibrary.core.xml.*;
import org.geometerplus.zlibrary.core.util.*;
import org.geometerplus.zlibrary.text.model.ZLTextParagraph;
public final class FB2Reader extends BookReader implements ZLXMLReader {
private boolean myInsidePoem = false;
private boolean myInsideTitle = false;
private int myBodyCounter = 0;
private boolean myReadMainText = false;
private int mySectionDepth = 0;
private boolean mySectionStarted = false;
private byte myHyperlinkType;
private Base64EncodedImage myCurrentImage;
private boolean myInsideCoverpage = false;
private String myCoverImageReference;
private int myParagraphsBeforeBodyNumber = Integer.MAX_VALUE;
private final char[] SPACE = { ' ' };
private String myHrefAttribute;
private byte[] myTagStack = new byte[10];
private int myTagStackSize = 0;
public FB2Reader(BookModel model) {
super(model);
}
boolean readBook() {
Base64EncodedImage.resetCounter();
return ZLXMLProcessor.read(this, Model.Book.File);
}
public void startDocumentHandler() {
}
public void endDocumentHandler() {
}
public boolean dontCacheAttributeValues() {
return true;
}
public void characterDataHandler(char[] ch, int start, int length) {
if (length == 0) {
return;
}
final Base64EncodedImage image = myCurrentImage;
if (image != null) {
image.addData(ch, start, length);
} else {
addData(ch, start, length, false);
}
}
public void characterDataHandlerFinal(char[] ch, int start, int length) {
if (length == 0) {
return;
}
final Base64EncodedImage image = myCurrentImage;
if (image != null) {
image.addData(ch, start, length);
} else {
addData(ch, start, length, true);
}
}
public boolean endElementHandler(String tagName) {
final byte tag = myTagStack[--myTagStackSize];
switch (tag) {
case FB2Tag.P:
endParagraph();
break;
case FB2Tag.SUB:
addControl(FBTextKind.SUB, false);
break;
case FB2Tag.SUP:
addControl(FBTextKind.SUP, false);
break;
case FB2Tag.CODE:
addControl(FBTextKind.CODE, false);
break;
case FB2Tag.EMPHASIS:
addControl(FBTextKind.EMPHASIS, false);
break;
case FB2Tag.STRONG:
addControl(FBTextKind.STRONG, false);
break;
case FB2Tag.STRIKETHROUGH:
addControl(FBTextKind.STRIKETHROUGH, false);
break;
case FB2Tag.V:
case FB2Tag.SUBTITLE:
case FB2Tag.TEXT_AUTHOR:
case FB2Tag.DATE:
popKind();
endParagraph();
break;
case FB2Tag.CITE:
case FB2Tag.EPIGRAPH:
popKind();
break;
case FB2Tag.POEM:
myInsidePoem = false;
break;
case FB2Tag.STANZA:
beginParagraph(ZLTextParagraph.Kind.AFTER_SKIP_PARAGRAPH);
endParagraph();
popKind();
break;
case FB2Tag.SECTION:
if (myReadMainText) {
endContentsParagraph();
--mySectionDepth;
mySectionStarted = false;
} else {
unsetCurrentTextModel();
}
break;
case FB2Tag.ANNOTATION:
popKind();
if (myBodyCounter == 0) {
insertEndOfSectionParagraph();
unsetCurrentTextModel();
}
break;
case FB2Tag.TITLE:
popKind();
exitTitle();
myInsideTitle = false;
break;
case FB2Tag.BODY:
popKind();
myReadMainText = false;
unsetCurrentTextModel();
break;
case FB2Tag.A:
addControl(myHyperlinkType, false);
break;
case FB2Tag.COVERPAGE:
if (myBodyCounter == 0) {
myInsideCoverpage = false;
insertEndOfSectionParagraph();
unsetCurrentTextModel();
}
break;
case FB2Tag.BINARY:
if (myCurrentImage != null) {
myCurrentImage.close();
myCurrentImage = null;
}
break;
default:
break;
}
return false;
}
public boolean startElementHandler(String tagName, ZLStringMap attributes) {
String id = attributes.getValue("id");
if (id != null) {
if (!myReadMainText) {
setFootnoteTextModel(id);
}
addHyperlinkLabel(id);
}
final byte tag = FB2Tag.getTagByName(tagName);
byte[] tagStack = myTagStack;
if (tagStack.length == myTagStackSize) {
tagStack = ZLArrayUtils.createCopy(tagStack, myTagStackSize, myTagStackSize * 2);
myTagStack = tagStack;
}
tagStack[myTagStackSize++] = tag;
switch (tag) {
case FB2Tag.P:
if (mySectionStarted) {
mySectionStarted = false;
} else if (myInsideTitle) {
addContentsData(SPACE);
}
beginParagraph(ZLTextParagraph.Kind.TEXT_PARAGRAPH);
break;
case FB2Tag.SUB:
addControl(FBTextKind.SUB, true);
break;
case FB2Tag.SUP:
addControl(FBTextKind.SUP, true);
break;
case FB2Tag.CODE:
addControl(FBTextKind.CODE, true);
break;
case FB2Tag.EMPHASIS:
addControl(FBTextKind.EMPHASIS, true);
break;
case FB2Tag.STRONG:
addControl(FBTextKind.STRONG, true);
break;
case FB2Tag.STRIKETHROUGH:
addControl(FBTextKind.STRIKETHROUGH, true);
break;
case FB2Tag.V:
pushKind(FBTextKind.VERSE);
beginParagraph(ZLTextParagraph.Kind.TEXT_PARAGRAPH);
break;
case FB2Tag.TEXT_AUTHOR:
pushKind(FBTextKind.AUTHOR);
beginParagraph(ZLTextParagraph.Kind.TEXT_PARAGRAPH);
break;
case FB2Tag.SUBTITLE:
pushKind(FBTextKind.SUBTITLE);
beginParagraph(ZLTextParagraph.Kind.TEXT_PARAGRAPH);
break;
case FB2Tag.DATE:
pushKind(FBTextKind.DATE);
beginParagraph(ZLTextParagraph.Kind.TEXT_PARAGRAPH);
break;
case FB2Tag.EMPTY_LINE:
beginParagraph(ZLTextParagraph.Kind.EMPTY_LINE_PARAGRAPH);
endParagraph();
break;
case FB2Tag.CITE:
pushKind(FBTextKind.CITE);
break;
case FB2Tag.EPIGRAPH:
pushKind(FBTextKind.EPIGRAPH);
break;
case FB2Tag.POEM:
myInsidePoem = true;
break;
case FB2Tag.STANZA:
pushKind(FBTextKind.STANZA);
beginParagraph(ZLTextParagraph.Kind.BEFORE_SKIP_PARAGRAPH);
endParagraph();
break;
case FB2Tag.SECTION:
if (myReadMainText) {
insertEndOfSectionParagraph();
++mySectionDepth;
beginContentsParagraph();
mySectionStarted = true;
}
break;
case FB2Tag.ANNOTATION:
if (myBodyCounter == 0) {
setMainTextModel();
}
pushKind(FBTextKind.ANNOTATION);
break;
case FB2Tag.TITLE:
if (myInsidePoem) {
pushKind(FBTextKind.POEM_TITLE);
} else if (mySectionDepth == 0) {
insertEndOfSectionParagraph();
pushKind(FBTextKind.TITLE);
} else {
pushKind(FBTextKind.SECTION_TITLE);
myInsideTitle = true;
enterTitle();
}
break;
case FB2Tag.BODY:
++myBodyCounter;
myParagraphsBeforeBodyNumber = Model.BookTextModel.getParagraphsNumber();
if ((myBodyCounter == 1) || (attributes.getValue("name") == null)) {
setMainTextModel();
myReadMainText = true;
}
pushKind(FBTextKind.REGULAR);
break;
case FB2Tag.A:
if (myHrefAttribute != null) {
String ref = attributes.getValue(myHrefAttribute);
String type = attributes.getValue("type");
if ((ref != null) && (ref.length() != 0)) {
if (ref.charAt(0) == '#') {
myHyperlinkType = "note".equals(type) ? FBTextKind.FOOTNOTE : FBTextKind.INTERNAL_HYPERLINK;
ref = ref.substring(1);
} else {
myHyperlinkType = FBTextKind.EXTERNAL_HYPERLINK;
}
addHyperlinkControl(myHyperlinkType, ref);
} else {
myHyperlinkType = FBTextKind.FOOTNOTE;
addControl(myHyperlinkType, true);
}
}
break;
case FB2Tag.COVERPAGE:
if (myBodyCounter == 0) {
myInsideCoverpage = true;
setMainTextModel();
}
break;
case FB2Tag.IMAGE:
if (myHrefAttribute != null) {
String imgRef = attributes.getValue(myHrefAttribute);
if ((imgRef != null) && (imgRef.length() != 0) && (imgRef.charAt(0) == '#')) {
String vOffset = attributes.getValue("voffset");
short offset = 0;
try {
offset = Short.parseShort(vOffset);
} catch (NumberFormatException e) {
}
imgRef = imgRef.substring(1);
if (!imgRef.equals(myCoverImageReference) ||
myParagraphsBeforeBodyNumber != Model.BookTextModel.getParagraphsNumber()) {
addImageReference(imgRef, offset);
}
if (myInsideCoverpage) {
myCoverImageReference = imgRef;
}
}
}
break;
case FB2Tag.BINARY:
String contentType = attributes.getValue("content-type");
String imgId = attributes.getValue("id");
if ((contentType != null) && (id != null)) {
myCurrentImage = new Base64EncodedImage(contentType);
addImage(imgId, myCurrentImage);
}
break;
default:
break;
}
return false;
}
public boolean processNamespaces() {
return true;
}
public void namespaceListChangedHandler(HashMap<String,String> namespaces) {
myHrefAttribute = namespaces.get("http://www.w3.org/1999/xlink");
if (myHrefAttribute != null) {
myHrefAttribute += ":href";
myHrefAttribute = myHrefAttribute.intern();
}
}
public void addExternalEntities(HashMap<String,char[]> entityMap) {
entityMap.put("FBReaderVersion", ZLibrary.Instance().getVersionName().toCharArray());
}
public List<String> externalDTDs() {
return Collections.emptyList();
}
}