/* * 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.formats.oeb; import java.util.*; import org.geometerplus.zlibrary.core.constants.XMLNamespaces; import org.geometerplus.zlibrary.core.constants.MimeTypes; import org.geometerplus.zlibrary.core.xml.*; import org.geometerplus.zlibrary.core.filesystem.ZLFile; import org.geometerplus.zlibrary.core.image.ZLFileImage; import org.geometerplus.fbreader.bookmodel.*; import org.geometerplus.fbreader.formats.xhtml.XHTMLReader; import org.geometerplus.fbreader.formats.util.MiscUtil; class Reference { public final String Title; public final String HRef; public Reference(String title, String href) { Title = title; HRef = href; } } class OEBBookReader extends ZLXMLReaderAdapter implements XMLNamespaces { private static final char[] Dots = new char[] {'.', '.', '.'}; private final BookReader myModelReader; private final HashMap<String,String> myIdToHref = new HashMap<String,String>(); private final ArrayList<String> myHtmlFileNames = new ArrayList<String>(); private final ArrayList<Reference> myTourTOC = new ArrayList<Reference>(); private final ArrayList<Reference> myGuideTOC = new ArrayList<Reference>(); private String myOPFSchemePrefix; private String myFilePrefix; private String myNCXTOCFileName; OEBBookReader(BookModel model) { myModelReader = new BookReader(model); } private TreeMap<String,Integer> myFileNumbers = new TreeMap<String,Integer>(); private TreeMap<String,Integer> myTOCLabels = new TreeMap<String,Integer>(); //hym 获取 epub 内容 boolean readBook(ZLFile file) { myFilePrefix = MiscUtil.htmlDirectoryPrefix(file); myIdToHref.clear(); myHtmlFileNames.clear(); myNCXTOCFileName = null; myTourTOC.clear(); myGuideTOC.clear(); myState = READ_NONE; if (!read(file)) { return false; } myModelReader.setMainTextModel(); myModelReader.pushKind(FBTextKind.REGULAR); for (String name : myHtmlFileNames) { final ZLFile xhtmlFile = ZLFile.createFileByPath(myFilePrefix + name); final XHTMLReader reader = new XHTMLReader(myModelReader, myFileNumbers); final String referenceName = reader.getFileAlias(MiscUtil.archiveEntryName(xhtmlFile.getPath())); myModelReader.addHyperlinkLabel(referenceName); myTOCLabels.put(referenceName, myModelReader.Model.BookTextModel.getParagraphsNumber()); reader.readFile(xhtmlFile, referenceName + '#'); myModelReader.insertEndOfSectionParagraph(); } generateTOC(); return true; } private BookModel.Label getTOCLabel(String id) { final int index = id.indexOf('#'); final String path = (index >= 0) ? id.substring(0, index) : id; Integer num = myFileNumbers.get(path); if (num == null) { return null; } if (index == -1) { final Integer para = myTOCLabels.get(num.toString()); if (para == null) { return null; } return new BookModel.Label(null, para); } return myModelReader.Model.getLabel(num + id.substring(index)); } //hym 生成目录 private void generateTOC() { if (myNCXTOCFileName != null) { final NCXReader ncxReader = new NCXReader(myModelReader); if (ncxReader.readFile(myFilePrefix + myNCXTOCFileName)) { final Map<Integer,NCXReader.NavPoint> navigationMap = ncxReader.navigationMap(); if (!navigationMap.isEmpty()) { int level = 0; for (NCXReader.NavPoint point : navigationMap.values()) { final BookModel.Label label = getTOCLabel(point.ContentHRef); int index = (label != null) ? label.ParagraphIndex : -1; while (level > point.Level) { myModelReader.endContentsParagraph(); --level; } while (++level <= point.Level) { myModelReader.beginContentsParagraph(-2); myModelReader.addContentsData(Dots); } myModelReader.beginContentsParagraph(index); myModelReader.addContentsData(point.Text.toCharArray()); } while (level > 0) { myModelReader.endContentsParagraph(); --level; } return; } } } for (Reference ref : myTourTOC.isEmpty() ? myGuideTOC : myTourTOC) { final BookModel.Label label = getTOCLabel(ref.HRef); if (label != null) { final int index = label.ParagraphIndex; if (index != -1) { myModelReader.beginContentsParagraph(index); myModelReader.addContentsData(ref.Title.toCharArray()); myModelReader.endContentsParagraph(); } } } } private static final String MANIFEST = "manifest"; private static final String SPINE = "spine"; private static final String GUIDE = "guide"; private static final String TOUR = "tour"; private static final String SITE = "site"; private static final String REFERENCE = "reference"; private static final String ITEMREF = "itemref"; private static final String ITEM = "item"; private static final String COVER_IMAGE = "other.ms-coverimage-standard"; private static final int READ_NONE = 0; private static final int READ_MANIFEST = 1; private static final int READ_SPINE = 2; private static final int READ_GUIDE = 3; private static final int READ_TOUR = 4; private int myState; @Override public boolean startElementHandler(String tag, ZLStringMap xmlattributes) { tag = tag.toLowerCase(); if ((myOPFSchemePrefix != null) && tag.startsWith(myOPFSchemePrefix)) { tag = tag.substring(myOPFSchemePrefix.length()); } tag = tag.intern(); if (MANIFEST == tag) { myState = READ_MANIFEST; } else if (SPINE == tag) { myNCXTOCFileName = myIdToHref.get(xmlattributes.getValue("toc")); myState = READ_SPINE; } else if (GUIDE == tag) { myState = READ_GUIDE; } else if (TOUR == tag) { myState = READ_TOUR; } else if ((myState == READ_MANIFEST) && (ITEM == tag)) { final String id = xmlattributes.getValue("id"); String href = xmlattributes.getValue("href"); if ((id != null) && (href != null)) { href = MiscUtil.decodeHtmlReference(href); myIdToHref.put(id, href); } } else if ((myState == READ_SPINE) && (ITEMREF == tag)) { final String id = xmlattributes.getValue("idref"); if (id != null) { final String fileName = myIdToHref.get(id); if (fileName != null) { myHtmlFileNames.add(fileName); } } } else if ((myState == READ_GUIDE) && (REFERENCE == tag)) { final String type = xmlattributes.getValue("type"); final String title = xmlattributes.getValue("title"); String href = xmlattributes.getValue("href"); if (href != null) { href = MiscUtil.decodeHtmlReference(href); if (title != null) { myGuideTOC.add(new Reference(title, href)); } if ((type != null) && (COVER_IMAGE.equals(type))) { myModelReader.setMainTextModel(); final ZLFile imageFile = ZLFile.createFileByPath(myFilePrefix + href); final String imageName = imageFile.getLongName(); myModelReader.addImageReference(imageName, (short)0); myModelReader.addImage(imageName, new ZLFileImage(MimeTypes.MIME_IMAGE_AUTO, imageFile)); } } } else if ((myState == READ_TOUR) && (SITE == tag)) { final String title = xmlattributes.getValue("title"); String href = xmlattributes.getValue("href"); if ((title != null) && (href != null)) { href = MiscUtil.decodeHtmlReference(href); myTourTOC.add(new Reference(title, href)); } } return false; } @Override public boolean endElementHandler(String tag) { tag = tag.toLowerCase(); if ((myOPFSchemePrefix != null) && tag.startsWith(myOPFSchemePrefix)) { tag = tag.substring(myOPFSchemePrefix.length()); } tag = tag.intern(); if ((MANIFEST == tag) || (SPINE == tag) || (GUIDE == tag) || (TOUR == tag)) { myState = READ_NONE; } return false; } @Override public boolean processNamespaces() { return true; } @Override public void namespaceMapChangedHandler(Map<String,String> namespaceMap) { myOPFSchemePrefix = null; for (Map.Entry<String,String> entry : namespaceMap.entrySet()) { if (OpenPackagingFormat.equals(entry.getValue())) { myOPFSchemePrefix = entry.getKey() + ":"; break; } } } @Override public boolean dontCacheAttributeValues() { return true; } }