使用epublib解析epub文件(章节内容、书籍菜单)

前阵子在android上解析epub格式的书籍。发现了这个开源的epub解析库。相关资料甚少!折腾了一阵子,发现其实光使用的话还是挺简单的。真是萌萌哒~下面简单介绍一下epublib。PS:第一次在CSDN发博客,排版略丑别嫌弃啊~

epublib不仅可以用来解析epub格式的书籍,同样也可以用来生成一本epub书籍。由于我只是用于阅读,所以这里只介绍解析的方法。当然,要想了解epub的解析,首先得对epub格式的规范有一定的认识。

话不多说,进入正题!

(一) 相关资料:

1. 项目地址:

https://github.com/psiegman/epublib

2. 官方api文档:

http://www.siegmann.nl/static/epublib/apidocs/

3. 官方网站:

http://www.siegmann.nl/epublib 

4. Android上使用需要用到的依赖 

Slf4j-android : http://www.slf4j.org/android/ 

epublib-core-latest.jar  :  https://github.com/downloads/psiegman/epublib/epublib-core-latest.jar

5. 使用epublib的开源android项目

PagerTurner (这个以后可能会开一个篇章简单介绍一下)

(二)重要的类:

1. Book 

—— 表示一本书。书籍的内容全在这里。通过book对象能过获得各类书籍内容,如resource,Metadata等;


2.Resource

—— 所有的resource对象构成一本书。所以,一个Resource就是书籍的一部分资源,这资源信息可以是html,css,js,图片等;

3.MetaData

—— 书籍的头信息。比如,作者,出版社,语言等;

4.Spine

—— 书籍的阅读顺序,是一个线性的顺序。通过Spine可以知道应该按照怎样的章节(注:这里所说的章节其实就是resource,不仅是书籍文本内容哦~下同)顺序去阅读,并且通过Spine可以找到对应章节的内容。

形式如:


5.TableOfContent

—— 这个与Spine有所区别。同样可以访问到各个章节的内容。但是他是树形结构。

形式如:




6.Resources

——获得全部的Resource对象。然后通过Resources对象能够轻易的取出你想要查找的Resource对象,可以用于查找Resource.

可以通过id或者href来定位resource,也可以通过MediaType类型来获得指定resource。部分重要方法如下图所示:

7.MediaType

—— Resource的类型描述。用于说明此Resource是何种类型(CSS/JS/图片/HTML/ VEDIO等)。

8.MediatypeService

—— 这个类中就包含了各种MediaType类型。如下图所示:


(三)基本用法

1.读取一本书

(1)最简单的方法就是直接从流中读取书籍

try {
    EpubReader reader = new EpubReader();
    InputStream in = aciticity.getAssets().open("1.epub");
    reader.readEpub(in);
} catch (Exception e) {
    e.printStackTrace();
}

(2)加载速度太慢?图片。视频之类的就先不读取了。

  try {
            EpubReader epubReader = new EpubReader();

        MediaType[] lazyTypes = {
                 MediatypeService.CSS,  
                 MediatypeService.GIF, MediatypeService.JPG,
                 MediatypeService.PNG,
                 MediatypeService.MP3,
                 MediatypeService.MP4};
        String fileName = "1.epub";
        Book book = epubReader.readEpubLazy(fileName,"UTF-8",Arrays.asList(lazyTypes));

    } catch (Exception e) {
        e.printStackTrace();
    }

2.获取章节内容

(1)获取所有章节内容。
 List<Resource> contents = book.getContents();

       (2)获取某一章的章节内容。

//通过index获取
int index = 0;
Resource byIndex = book.getSpine().getResource(index);

//通过href获取
String href = "/images/1.png";
Resource byHref = book.getResources().getByHref(href);

//通过id
String id = "chapter01"; 
Resource byId = book.getResources().getById(id);

//特殊的resource,可以直接获取
book.getCoverImage();
book.getCoverPage();
book.getNcxResource();
book.getOpfResource();

//其他
book.getSpine().getSpineReferences().get(0).getResource();
book.getGuide().getReferences().get(0).getResource();
                    

3.获取书籍菜单

(1)两种不同的菜单
//通过spine获取线性的阅读菜单,此菜单依照阅读顺序排序
List<SpineReference> spineReferences = book.getSpine().getSpineReferences();
//获取对应resource
pineReferences.get(0).getResource();

//通过TableOfContents获取树形菜单。此菜单按照章节之间的关系(树形)排序
TableOfContents tableOfContents = book.getTableOfContents();
//获得对应的reource
tableOfContents.getTocReferences().get(0).getResource();

其中,对于TableOfContent,接下来给出参考的遍历方式。

首先,是菜单的JavaBean:
/**
 * 菜单,包含了标题,href,儿子节点
 * @author Administrator
 *
 */
public class ContentItem{

    private String title;
    private String url;//href
    private int size;//resource的大小

    //孩子节点
    private List<ContentItem> children;

    public ContentItem() {
        super();
    }

    public ContentItem(String title, String url,int size) {
        super();
        this.title = title;
        this.url = url;
        this.size = size;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public List<ContentItem> getChildren() {
        if(this.children==null){
            children = new ArrayList<ContentItem>();
        }
        return children;
    }

    public void setChildren(List<ContentItem> children) {
        this.children = children;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

}

上面contentItem中的url就是href,到时候需要加载具体章节的内容的话,只需要根据href就可以拿到指定的resource了。

然后,通过TableOfContents,遍历出我们想要的树形菜单。

public class EpubMenuParser {

    private ContentItem menu = new ContentItem();

    public ContentItem startParse(Book book){
        //从深度为0开始遍历
        parseMenu(book.getTableOfContents().getTocReferences(), 0);
        return menu;

    }

    /**
     * 遍历epub书籍的目录
     * @param refs 
     * @param level 菜单深度
     */
    private void parseMenu(List<TOCReference> refs, int level) {

        if (refs == null || refs.isEmpty()) {
            return;
        }

        for (TOCReference ref : refs) {

            if (ref.getResource() != null) {
                if (level == 0) {
                    // 第一层,一级节点,父节点是root
                    ContentItem item = new ContentItem(ref.getTitle(),
                             ref.getCompleteHref(),(int)ref.getResource().getSize());
                    menu.getChildren().add(item);

                } else if (level == 1) {

                    int lastIndexOf_depth1 = menu.getChildren().size() - 1;// 当前最后一个一级节点的位置

                    // 存入root的孩子节点中的最后一个一级节点中
                    ContentItem item2 = new ContentItem(ref.getTitle(),
                            ref.getCompleteHref(),(int)ref.getResource().getSize());

                    menu.getChildren().get(lastIndexOf_depth1).getChildren()
                            .add(item2);

                } else if (level == 2) {

                    int lastIndexOf_depth1 = menu.getChildren().size() - 1;// 当前最后一个一级节点的位置
                    int lastIndexOf_depth2 = menu.getChildren()
                            .get(lastIndexOf_depth1).getChildren().size() - 1;// 当前最后一个二级节点的位置

                    // 父节点为二级节点中的最后一个节点
                    ContentItem item = new ContentItem(ref.getTitle(),
                        ref.getCompleteHref(),(int)ref.getResource().getSize());

                    menu.getChildren().get(lastIndexOf_depth1).getChildren()
                            .get(lastIndexOf_depth2).getChildren().add(item);
                }
            }
            //继续遍历它的儿子
            parseMenu(ref.getChildren(), level + 1);
        }
    }

}

当然,这个遍历方法还是有点问题的。比如,目前的写法只支持3级菜单。不过一般的都够用了。当然,写死了并不好,根据需求而定哦~

还有很多其他的用法,之后慢慢研究。




  • 12
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值