epub 読み込み、目次等を読み込む

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);
   }
}