Android で epub を読み込むアプリを開発しようとすると、結構めんどうなことが多い。
ZIP圧縮から META-INF を読んで、本の目次ではない内容の構成の参照を書いた content.opf を読み、それとは別に、本の目次にあたる top.ncx を読み、
top,ncx に書かれたID→ content.opf → コンテンツの読み込み
ということをしないと到達できない。
XML パーサー実行を最低2回は必要なのである。
content.opf を1通り読む込む。
(ここに書いた XmlHandler は、org.xml.sax.helpers.DefaultHandlerの継承で、
http://blog.zaq.ne.jp/oboe2uran/article/661/ に、onStartTag を追加したもの。)
ZipFile zipfile = new ZipFile(file);
ZipEntry zen = zipfile.getEntry("mimetype");
if (new String(readByte(zipfile, zen)).equals("application/epub+zip")){
SAXParserFactory saxparserFactory = SAXParserFactory.newInstance();
saxparserFactory.setNamespaceAware(true);
saxparserFactory.newSAXParser().parse(zipfile.getInputStream(zipfile.getEntry("META-INF/container.xml")), new XmlHandler(){
@Override
public void onStartTag(String xpath, Map<String,String> attributes){
}
@Override
public void onReadedTag(String qName, String xpath, Map<String,String> attributes, String body){
if (xpath.equals("/container/rootfiles/rootfile") && attributes.containsKey("full-path")){
rootfile = attributes.get("full-path");
contentPrefix = rootfile.replaceFirst("/.+$", "/");
}
}
});
saxparserFactory.newSAXParser().parse(zipfile.getInputStream(zipfile.getEntry(rootfile)), new XmlHandler(){
String imageContent;
Map<String,String> mmap;
@Override
public void onStartTag(String xpath, Map<String,String> attributes){
}
@Override
public void startDocument() throws SAXException{
super.startDocument();
creator = null;
subject = null;
rights = null;
idname = null;
idscheme = null;
identifier = null;
mmap = new HashMap<String,String>();
}
@Override
public void onReadedTag(String qName, String xpath, Map<String,String> attributes, String body){
if (xpath.equals("/package/metadata/dc:title")){
title = body.trim();
}
if (xpath.equals("/package/metadata/dc:creator")){
creator = body.trim();
}
if (xpath.equals("/package/metadata/dc:title")){
title = body.trim();
}
if (xpath.equals("/package/metadata/dc:subject")){
subject = body.trim();
}
if (xpath.equals("/package/metadata/dc:language")){
language = body.trim();
}
if (xpath.equals("/package/metadata/dc:identifier")){
identifier = body.trim();
if (attributes.containsKey("id")) idname = attributes.get("id");
if (attributes.containsKey("opf:scheme")) idscheme = attributes.get("opf:scheme");
}
if (xpath.equals("/package/metadata/dc:rights")){
rights = body.trim();
}
if (xpath.equals("/package/metadata/meta") && attributes.containsKey("name") && attributes.containsKey("content")){
if (attributes.get("name").equals("cover")){
imageContent = attributes.get("content");
}
}
if (xpath.equals("/package/manifest/item") && attributes.containsKey("id") && attributes.containsKey("href")){
if (attributes.get("id").equals(imageContent)){
coverimagefile = attributes.get("href");
}
if (attributes.containsKey("media-type")){
mmap.put(attributes.get("href"), attributes.get("media-type"));
}
}
}
@Override
public void endDocument() throws SAXException{
super.endDocument();
Book book = new Book(file.getAbsolutePath, file.length, rootfile, coverimagefile, title, subject, creator, rights, language, identifier, idname, idscheme, contentPrefix);
for(String key:mmap.keySet()){
book.mediaTypeMap.put(key, mmap.get(key));
}
blist.add(book);
}
});
}
次に、top.ncx から目次を読込む Task
public abstract class NavReadTask extends AsyncTask<String,Void,Boolean>{
protected abstract void showProgress();
protected abstract void dissmissProgress();
protected abstract Book shouldReadedBook();
protected abstract void onResult(Navmap navmap, List<Navpoint> list, boolean result);
List<Navpoint> mlist;
String title;
String label;
String content;
Book book;
@Override
protected void onPreExecute(){
showProgress();
mlist = new ArrayList<Navpoint>();
book = shouldReadedBook();
}
/* params[0] = epub-path [1]=toc.ncx entry
*/
@Override
protected Boolean doInBackground(String...params){
boolean rtn = false;
try{
ZipFile zipfile = new ZipFile(new File(params[0]));
SAXParserFactory saxparserFactory = SAXParserFactory.newInstance();
saxparserFactory.setNamespaceAware(true);
saxparserFactory.newSAXParser().parse(zipfile.getInputStream(zipfile.getEntry(params[1])), new XmlHandler(){
int order = 0;
@Override
public void onStartTag(String xpath, Map<String,String> attributes){
if (xpath.equals("/ncx/navMap/navPoint")){
order = Integer.parseInt(attributes.get("playOrder"));
}
}
@Override
public void onReadedTag(String qName, String xpath, Map<String,String> attributes, String body){
if (xpath.equals("/ncx/docTitle/text")){
title = body.trim();
}
if (xpath.equals("/ncx/navMap/navPoint/navLabel/text")){
label = body.trim();
}
if (xpath.equals("/ncx/navMap/navPoint/content")){
content = attributes.get("src");
}
if (xpath.equals("/ncx/navMap/navPoint")){
mlist.add(new Navpoint(order, label, content, book.mediaTypeMap.get(content)));
}
}
});
rtn = true;
}catch(Exception e){
Logger.e(e.getMessage(), e);
}
return rtn;
}
@Override
protected void onPostExecute(Boolean result){
dissmissProgress();
onResult(new Navmap(mlist.size(), title), mlist, result);
}
}