程序员制作epub电子书
来自:http://www.cnblogs.com/linlf03/archive/2011/12/15/2285953.html
/////////////////////////////////////////////////////////////////////////////////////////////Page1
简介:是否需要分发文档、创建电子图书或者把喜欢的博客文章存档?EPUB是一种开放式的数字图书规范,以常用的技术如XML、CSS和XHTML为基础,EPUB文件可在便携式的e-ink设备、移动电话和桌面计算机上阅读。本教程详细阐述了EPUB格式,首先用Java?技术示范了EPUB验证,然后详细说明如何使用DocBook和Python自动创建EPUB。
开始之前
本教程讲述如何创建EPUB格式的电子图书。EPUB是一种基于XML的、对开发者友好的格式,正逐渐成为数字图书的事实标准。但EPUB不仅可用于图书,还包括:
•对文档打包以便离线阅读或者分发
•打包博客文章或者其他Web内容
•使用常见的开放源代码工具创建、搜索和整理
关于本教程
常用的缩写词
•API:应用程序编程接口(applicationprogramminginterface)
•CSS:级联样式表(Cascadingstylesheet)
•DOM:文档对象模型(DocumentObjectModel)
•DTD:文档类型定义(Documenttypedefinition)
•GUI:图形用户界面(Graphicaluserinterface)
•HTML:超文本标记语言(HypertextMarkupLanguage)
•SAX:XML简易API(SimpleAPIForXML)
•W3C:万维网联盟(WorldWideWebConsortium)
•XHTML:可扩展的HTML(ExtensibleHTML)
•XML:可扩展标记语言
本教程首先手工创建一个EPUB图书,帮助您了解其构成和需要的文件。然后说明如何捆绑完成的数字图书,按照规范进行验证以及在不同的阅读系统上测试。
然后讨论如何从DocBookXML生成EPUB—最常用的技术文档标准之一—以及如何使用Python实现从DocBook到EPUB的自动创建。
目标
通过本教程可以学习如下内容:
•了解EPUB是什么,谁支持它,谁采用它
•了解EPUB包的结构,包括需要的文件及其模式
•如何从头创建一个内容简单而有效的EPUB文件
•使用开放源代码工具从DocBook生成EPUB文件,DocBook是一种常见的技术文档和图书模式
•使用Python和DocBook自动转换成EPUB
先决条件
本教程对操作系统没有特殊要求,但是应该熟悉创建文件和目录的机制。建议使用XML编辑器或者集成开发环境(IDE)。
对于本教程后半部分的EPUB创建自动化内容,需要读者了解基本的XML处理技巧—XSLT、DOM或者基于SAX的解析—并熟悉使用XML原生API构造XML文档。
阅读本教程不需要熟悉EPUB文件格式。
系统需求
尝试本教程中的例子,需要一个Java解释器(1.5或更高版本)和Python解释器(2.4或更高版本)以及相应的XML库。不过,有经验的XML开发人员很容易将这些例子修改为适合任何编程语言和XML库。
/////////////////////////////////////////////////////////////////////////////////////////////Page2
关于EPUB格式
了解EPUB的背景,EPUB最适合做什么,以及EPUB和便携式文档格式(PDF)的区别。
什么是EPUB?
EPUB是可逆的数字图书和出版物XML格式,数字出版业商业和标准协会InternationalDigitalPublishingForum(IDPF)制定的标准。IDPF于2007年10月正式采用EPUB,随后被主流出版商迅速采用。可以使用各种开放源代码或者商业软件在所有主流操作系统、SonyPRS之类的e-ink设备或者AppleiPhone之类的小型设备上阅读EPUB格式。
谁在使用EPUB?只能用于图书吗?
虽然最早采用EPUB的是传统的印刷品出版商,但是这并不妨碍它在电子图书中的应用。利用免费的软件工具,可以将网页捆绑成EPUB,转化成文本文件或者将原有的DocBookXML文档转化成结构良好的、有效的EPUB(后一点将在从DocBook到EPUB一节讨论)。
EPUB与PDF有什么不同?
PDF仍然是世界上应用最广泛的电子文档格式。从图书出版商的角度来看,PDF的优点包括:
•PDF文件允许对页面布局进行像素级的控制,包括复杂的打印格式,如多栏格式和奇偶页相间的格式。
•有多种不同的GUI文档工具可生成PDF,如Microsoft?OfficeWord和Adobe?InDesign?。
•PDF阅读器非常普及,现在大多数计算机上都有安装。
•PDF可以嵌入特殊的字体,精确控制最终的输出结果。
三合一的标准
EPUB包括三个单独的IDPF规范,虽然实际上将其统称为EPUB更保险:
•OpeneBookPublicationStructureContainerFormat(OCF):定义了EPUB档案的目录树结构和文件结构(ZIP)。
•OpenPublicationStructure(OPS):定义了电子图书的公共词汇表,特别是可作为图书内容的格式(比如XHTML和CSS)。
•OpenPackagingFormat(OPF):描述了EPUB必须的和可选的元数据、阅读顺序和目录。
此外,对于档案中的特定类型的内容,EPUB还重用了其他一些标准,如XHTML1.0和DigitalAccessibleInformationSYstem(DAISY)。
从软件开发人员的角度来看,PDF还远远不够理想:
•这不是一种简单易学的标准,因此编写自己的PDF生成代码非常困难。
•虽然PDF现在是一种InternationalOrganizationforStandardization(ISO)标准(ISO32000-1:2008),但过去一直受一家公司的控制:AdobeSystems。
•尽管多数编程语言都提供了PDF库,但很多是商业产品或者嵌入到GUI应用程序中,外部进程不容易控制。并非所有的免费库都得到积极的维护。
•PDF原生文本可以通过程序提取出来并进行搜索,但很少可以对PDF进行标记以便简单可靠地转化成Web友好的格式。
•PDF文档不容易流动,就是说很难适应小屏幕或者对布局进行明显的改变。
为何说EPUB对开发人员是友好的
EPUB解决了PDF和开发人员友好性有关的所有瑕疵。一个EPUB就是一个简单ZIP格式文件(使用.epub扩展名),其中包括按照预先定义的方式排列的文件。如何制作ZIP文档有一些技巧,稍后将在将EPUB文件捆绑为ZIP文档一节介绍。除此以外,EPUB非常简单:
•EPUB中的所有内容基本上都是XML。EPUB文件可使用标准XML工具创建,不需要任何专门或者私有的软件。
•EPUB内容(eBook的具体内容)基本上都是XHTML1.1(另一种格式是DTBook,为视力受限者编码书籍的一种标准。关于DTBook的更多信息请参阅参考资料,本教程中不涉及这部分)。
•大多数EPUBXML模式都来自现成的、可免费获得的、已发布的规范。
最关键的在于EPUB元数据是XML,EPUB内容是XHTML。如果您的文档构建系统产生的结果用于Web和/或基于XML,那么也可用于生成EPUB。
/////////////////////////////////////////////////////////////////////////////////////////////Page3
创建第一个EPUB
最小的EPUB包至少要包含几个文件。规范对于EPUB包中这些文件的格式、内容和位置要求可能很严格。这一节讨论使用EPUB标准必须了解的基础知识。
解剖EPUB包
小型EPUB文件的基本结构遵循清单1所示的样式。准备好分发之前,整个目录结构被压缩到一个ZIP格式文件中,几点特殊要求将在用ZIP打包EPUB文件一节讨论。
清单1.简单EPUB档案的目录和文件结构
mimetype
META-INF/
container.xml
OEBPS/
content.opf
title.html
content.html
stylesheet.css
toc.ncx
images/
cover.png
提示:可下载符合该结构的一个电子图书,但建议按照本教程的说明自己创建一个。
编写EPUB图书之前首先创建EPUB项目的目录。打开文本编辑器或者Eclipse之类的IDE。建议采用支持XML的编辑器—具体而言就是能够根据参考资料给出的RelaxNG模式进行验证。
--------------------------------------------------------------------------------
mimetype文件
这个文件非常简单,必须命名为mimetype,文件内容如下:
application/epub+zip
要注意,mimetype文件不能包含新行或者回车。
此外,mimetype文件必须作为ZIP档案中的第一个文件,而且自身不能压缩。用ZIP打包EPUB文件一节将介绍如何使用一般的ZIP参数将其包含进来。现在创建该文件并保存,并确保它在EPUB项目的根目录中。
META-INF/container.xml
EPUB根目录下必须包含META-INF目录,而且其中要有一个文件container.xml。EPUB阅读系统首先查看该文件,它指向数字图书元数据的位置。
创建目录META-INF。在其中创建一个新文件container.xml。container文件非常小,但是对结构要求很严格。将清单2中的代码粘贴到META-INF/container.xml中。
清单2.container.xml文件
<?xmlversion="1.0"?>
<containerversion="1.0"xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<rootfiles>
<rootfilefull-path="OEBPS/content.opf"
media-type="application/oebps-package+xml"/>
</rootfiles>
</container>
full-path(粗体)的值仅仅是该文件的一部分,不同的文件可能相差甚大。目录路径必须相对于EPUB文件根目录本身,而不是META-INF目录。
关于META-INF
META-INF目录还可以包含其他几个文件。这些文件使EPUB支持数字签名、加密和数字版权管理(DRM)。本教程不讨论这些主题。更多信息请参阅OCF规范。
mimetype和container是EPUB档案中仅有的两个需要严格限制位置的文件。建议(尽管不是必须的)将其他文件保存到EPUB的子目录下(按照惯例,通常被称为OEBPS,即OpeneBookPublicationStructure,但不是必须的)。
接下来在EPUB项目中创建目录OEBPS。本教程下一节将介绍OEBPS中的文件—数字图书的核心:元数据和页面。
--------------------------------------------------------------------------------
打开PackagingFormat元数据文件
尽管该文件名没有特殊要求,但通常被称为content.opf。它指定了图书中所有内容的位置,如文本和图像等其他媒体。它还给出了另一个元数据文件,内容的NavigationCentereXtended(NCX)表。
该OPF文件是EPUB规范中最复杂的元数据。创建OEBPS/content.opf并粘贴清单3所示的内容。
清单3.包含示例元数据的OPFcontent文件
<?xmlversion='1.0'encoding='utf-8'?>
<packagexmlns="http://www.idpf.org/2007/opf"
xmlns:dc="http://purl.org/dc/elements/1.1/"
unique-identifier="bookid"version="2.0">
<metadata>
<dc:title>HelloWorld:MyFirstEPUB</dc:title>
<dc:creator>MyName</dc:creator>
<dc:identifierid="bookid">urn:uuid:12345</dc:identifier>
<metaname="cover"content="cover-image"/>
</metadata>
<manifest>
<itemid="ncx"href="toc.ncx"media-type="text/xml"/>
<itemid="cover"href="title.html"media-type="application/xhtml+xml"/>
<itemid="content"href="content.html"media-type="application/xhtml+xml"/>
<itemid="cover-image"href="images/cover.png"media-type="image/png"/>
<itemid="css"href="stylesheet.css"media-type="text/css"/>
</manifest>
<spinetoc="ncx">
<itemrefidref="cover"linear="no"/>
<itemrefidref="content"/>
</spine>
<guide>
<referencehref="cover.html"type="cover"title="Cover"/>
</guide>
</package>
OPF模式与名称空间
OPF文档本身必须使用名称空间http://www.idpf.org/2007/opf,元数据则使用DublinCoreMetadataInitiative(DCMI)名称空间http://purl.org/dc/elements/1.1/。
最好现在将OPF和DCMI模式添加到XML编辑器中。EPUB用到的所有模式都可以下载。
元数据
DublinCore定义了一组常用的元数据,可用于描述各种不同的数字资料,它不是EPUB规范的一部分。所有这些术语都可以出现在OPF元数据部分。编写要分发的EPUB时,这里可以放很多内容,目前来说清单4的内容就足够了。
清单4.OPF元数据摘要
...
<metadata>
<dc:title>HelloWorld:MyFirstEPUB</dc:title>
<dc:creator>MyName</dc:creator>
<dc:identifierid="bookid">urn:uuid:12345</dc:identifier>
<metaname="cover"content="cover-image"/>
</metadata>
...
有两个术语是必须的,即title和identifier。按照EPUB规范,标识符必须是惟一的,但是这个惟一的值要靠数字图书的创建者来定义。对于图书出版商来说,这个字段一般包含ISBN或者LibraryofCongress编号。对于其他EPUB创建者,可以考虑使用URL或者很大的随机生成的惟一用户ID(UUID)。要注意,属性unique-identifier的值必须和dc:identifier元素的ID属性匹配。
其他和内容相关的可以考虑添加的元数据包括:
•语言(如dc:language)。
•出版日期(如dc:date)。
•出版商(如dc:publisher)。(可以是公司或个人的名称)。
•版权信息(如dc:rights)。(如果采用CreativeCommons许可证,可以将许可证的URL放在这里)。
关于DCMI的更多信息请参阅参考资料。
EPUB规范没有要求包含name属性值为cover的meta元素,但为了增加封面和图像的可移植性,建议这样做。一些EPUB呈现程序喜欢使用图像文件作为封面,另一些则愿意使用包含内联封面图像的XHTML文件。该例子显示了这两种情况。meta元素的content属性的值应该是图书封面图像在manifest中的ID号,manifest是OPF文件的一部分。
Manifest
OPFmanifest列出了EPUB内容(不包括元数据)中的所有资源。就是说,通常是组成电子图书文本的一组XHTML文件再加上一些相关的媒体如图像。EPUB鼓励使用CSS设定图书内容的样式,因此manifest中也包含CSS。进入数字图书的所有文件都必须在manifest中列出。
清单5显示了manifest的一部分。
清单5.OPFmanifest的一部分
...
<manifest>
<itemid="ncx"href="toc.ncx"media-type="text/xml"/>
<itemid="cover"href="title.html"media-type="application/xhtml+xml"/>
<itemid="content"href="content.html"media-type="application/xhtml+xml"/>
<itemid="cover-image"href="images/cover.png"media-type="image/png"/>
<itemid="css"href="stylesheet.css"media-type="text/css"/>
</manifest>
...
高级OPFmanifest
更高级的manifest文件可能包含多个XHTML文件以及图像和CSS。可下载一个完整的包含各种常见类型的EPUB例子。
第一项toc.ncx(参见下一节)是必须的。所有的项都有相应的media-type值,XHTML内容的媒体类型为application/xhtml+xml。媒体类型必须正确,不能是text/html或者其他类型。
EPUB支持四种核心图像文件类型:JointPhotographicExpertsGroup(JPEG)、PortableNetworkGraphics(PNG)、GraphicsInterchangeFormat(GIF)和ScalableVectorGraphics(SVG)。如果能够提供对核心类型的后退转换(fall-back),也可包含不支持的文件类型。关于后退转换内容的更多信息请参阅OPF规范。
href属性的值应该是一个相对于该OPF文件的统一资源标识符(URI)。(很容易和container.xml中对OPF文件的引用混淆,其中的引用是相对于EPUB的整体引用)。这里的OPF文件位于和内容相同的OEBPS目录中,因此不需要路径信息。
Spine
manifest告诉EPUB阅读器哪些文件属于档案,spine则指定这些文件出现的顺序或—按照EPUB的说法—数字图书的线性阅读顺序。可以将OPFspine看作是书中“页面”的顺序。按照文档顺序从上到下依次读取spine。清单6显示了OPF文件的一个片段。
清单6.OPFspine的一部分
...
<spinetoc="ncx">
<itemrefidref="cover"linear="no"/>
<itemrefidref="content"/>
</spine>
...
每个itemref元素都需要有一个idref属性,并且和manifest中的某个ID匹配。toc属性也是必需的。它引用manifest中表示内容NCX表文件名的ID。
spine中的linear属性表明该项是作为线性阅读顺序中的一项,还是和先后次序无关。建议将封面定义为linear=no。符合EPUB规范的阅读系统将首先打开spine中没有设置为linear=no中的第一项。
Guide
OPF内容文件的最后一部分是guide。这一节是可选的,但最好保留。清单7显示了guide文件的部分内容。
清单7.OPFguide的一部分
...
<guide>
<referencehref="cover.html"type="cover"title="Cover"/>
</guide>
...
guide可以为EPUB阅读系统提供语义信息。manifest定义了EPUB中的物理资源,spine提供了这些资源的顺序信息,guide负责解释这些部分的含义。下面是可以出现在OPFguide中的部分值:
•cover:图书封面
•title-page:包含作者和出版商信息的页面
•toc:目录
完整的列表请参阅OPF2.0规范(参见参考资料)。
--------------------------------------------------------------------------------
内容的NCX表
NCX和OPF元数据的交叉
由于NCX源自其他标准,使用NCX编码的信息和OPF内容之间存在重复。如果通过程序生成EPUB,这算不上什么问题,因为同样的代码可输出到两个文件中。两个位置的信息要一致,不同的EPUB读者可能使用不同位置的值。
尽管OCF文件是作为EPUB本身的一部分定义的,但最后一个主要的元数据文件参照了不同的数字图书标准。DAISY是一个专门为不能使用传统书籍的读者设计数据格式的组织,通常是因为视力受损或者不便于使用印刷的书籍。EPUB借用了DAISY的NCXDTD。NCX定义了数字图书的目录表。复杂的图书中,目录表通常采用层次结构,包括嵌套的内容、章和节。
使用XML编辑器创建OEBPS/toc.ncx并粘贴清单8所示的代码。
清单8.简单的NCX文件
<?xmlversion='1.0'encoding='utf-8'?>
<!DOCTYPEncxPUBLIC"-//NISO//DTDncx2005-1//EN"
"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
<ncxxmlns="http://www.daisy.org/z3986/2005/ncx/"version="2005-1">
<head>
<metaname="dtb:uid"content="urn:uuid:12345"/>
<metaname="dtb:depth"content="1"/>
<metaname="dtb:totalPageCount"content="0"/>
<metaname="dtb:maxPageNumber"content="0"/>
</head>
<docTitle>
<text>HelloWorld:MyFirstEPUB</text>
</docTitle>
<navMap>
<navPointid="navpoint-1"playOrder="1">
<navLabel>
<text>Bookcover</text>
</navLabel>
<contentsrc="title.html"/>
</navPoint>
<navPointid="navpoint-2"playOrder="2">
<navLabel>
<text>Contents</text>
</navLabel>
<contentsrc="content.html"/>
</navPoint>
</navMap>
</ncx>
NCX元数据
DTD要求NCX<head>标记中包含四个meta元素:
•uid:数字图书的惟一ID。该元素应该和OPF文件中的dc:identifier对应。
•depth:反映目录表中层次的深度。该例只有一层,因此是1。
•totalPageCount和maxPageNumber:仅用于纸质图书,保留0即可。
docTitle/text的内容是图书的标题,和OPF中的dc:title匹配。
NCXnavMap
NCX和OPFspine有什么不同?
两者很容易混淆,因为两个文件都描述了文档的顺序和内容。要说明两者的区别,最简单的办法就是拿印刷书来打比方:OPFspine描述了书中的各个章节是如何实际连接起来的,比方说翻过第一章的最后一页就看到第二章的第一页。NCX在图书的一开始描述了目录。目录肯定会包含书中主要的章节,但是还可能包含没有单独分页的小节。
一条法则是NCX包含的navPoint元素通常比OPFspine中的itemref元素多。实际上,spine中的所有项都会出现在NCX中,但NCX可能更详细。
navMap是NCX文件中最重要的部分,定义了图书的目录。navMap包含一个或多个navPoint元素。每个navPoint都要包含下列元素:
•playOrder属性,说明文档的阅读顺序。和OPFspine中itemref元素的顺序相同。
•navLabel/text元素,给出该章节的标题。通常是章的标题或者数字,如“第一章”,或者—像这个例子一样—“封面”。
•content元素,它的src属性指向包含这些内容的物理资源。就是OPFmanifest中声明的文件(也可使用片段标识符引用XHTML内容中的锚元素—比如content.html#footnote1)。
•还可以有一个或多个navPoint元素。NCX使用嵌套的导航点表示层次结构的文档。
该文档的结构非常简单:只有两页,不存在嵌套关系。就是说有两个navPoint元素,它们的playOrder值按升序排列,从1开始。在NCX中可以命名这些章节,以便读者跳到电子图书不同的部分。
--------------------------------------------------------------------------------
添加最后的内容
现在知道了EPUB需要的所有元数据,可以加入真正的图书内容了。可以使用下载的内容,也可以自己写,只要文件名和元数据匹配即可。
然后创建下列文件和文件夹:
•title.html:图书的标题页。创建该文件并在其中包含引用封面图片的img元素,src的属性值为images/cover.png。
•images:在OEBPS下创建该文件夹,然后复制给定的示例图片(或者创建自己的图片)并命名为cover.png。
•content.html:图书的实际文字内容。
•stylesheet.css:将该文件放在和XHTML文件相同的OEBPS目录中。该文件可以包含任意CSS声明,比如设置字体或者文字颜色。清单10给出了一个CSS文件的例子。
EPUB图书中的XHTML和CSS
清单9包含了一个有效的EPUB内容页。将其作为标题页(title.html),用一个类似的页面作为主要内容页(content.html)。
清单9.示例title页面(title.html)
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<title>HelloWorld:MyFirstEPUB</title>
<linktype="text/css"rel="stylesheet"href="stylesheet.css"/>
</head>
<body>
<h1>HelloWorld:MyFirstEPUB</h1>
<div><imgsrc="images/cover.png"alt="Titlepage"/></div>
</body>
</html>
EPUB的XHTML需要符合几条要求,和一般的Web开发不同:
•内容必须是有效的XHTML1.1:XHTML1.0Strict和XHTML1.1的主要区别是去掉了name属性(使用ID引用锚元素)。
•img元素只能引用电子图书的本地图片:该元素不能引用Web上的图片。
•避免使用script:EPUB阅读器不一定支持JavaScript代码。
EPUB支持CSS的方式有一些细微的差别,但是不会影响样式表的一般用法(详情参阅OPS规范)。清单10中的简单CSS文件可以设置基本的字体,并把标题设为红色。
清单10.电子图书的示例样式表(stylesheet.css)
body{
font-family:sans-serif;
}
h1,h2,h3,h4{
font-family:serif;
color:red;
}
有趣的是,EPUB非常支持CSS2@font-face规则,允许内嵌字体。如果创建技术文档,这点可能无关紧要,但是如果用多种语言或针对特定领域编写EPUB,能够指定具体的字体数据就很有必要了。
现在已经准备好了创建EPUB图书所需的所有内容。下一节将按照OCF规范将图书装订起来,并看看如何进行验证。
/////////////////////////////////////////////////////////////////////////////////////////////Page4
打包和检查EPUB
现在,应当可以对EPUB包进行打包。这个包可以是您自己创建的一本新书,也可使用从本文下载部分获得的原始文件。
用ZIP打包EPUB文件
EPUB规范的OEBPSContainerFormat讨论了EPUB和ZIP,最重要的几点是:
•档案中的第一个文件必须是mimetype文件(参见本教程Mimetype一节)。mimetype文件不能被压缩。这样非ZIP工具就能从EPUB包的第30个字节开始读取原始字节,从而发现mimetype。
•ZIP档案不能加密。EPUB支持加密,但不是在ZIP文件这一层上。
在类UNIX?操作系统上,使用ZIP2.3可通过两个命令来创建EPUBZIP文件,如清单11所示(这些命令假设当前工作目录为EPUB项目。)
清单11.将EPUB打包成有效的epub+zip文件
$zip-0Xqmy-book.epubmimetype
$zip-Xr9Dqmy-book.epub*
第一个命令创建了一个新的ZIP档案,并添加了没有进行压缩的mimetype文件。第二个命令添加其他内容。选项-X和-D最大限度地减少.zip文件中无关紧要的信息;-r递归地包含META-INF和OEBPS目录的内容。
--------------------------------------------------------------------------------
EPUB验证
虽然EPUB标准并不很难,但其XML文件必须符合特定的模式。如果使用模式感知的XML编辑器生成元数据和XHTML,就能事半功倍。对EpubCheck包进行最后检查(参见参考资料)。
Adobe负责维护EpubCheck包,它是采用BerkeleySoftwareDistribution(BSD)许可证的开源项目。它是一个可以作为独立工具、Web应用程序运行的Java程序,或者可以将它集成到在JavaRuntimeEnvironment(JRE)1.5或更高版本下运行的应用程序中。
在命令行中运行非常简单。清单12给出了一个例子。
清单12.运行EpubCheck工具程序
$java-jar/path/to/epubcheck.jarmy-book.epub
如果没有创建辅助文件或者元数据文件出错,可能会看到清单13所示的错误消息。
清单13.EpubCheck错误
my-book.epub:imagefileOEBPS/images/cover.pngismissing
my-book.epub:resourceOEBPS/stylesheet.cssismissing
my-book.epub/OEBPS/title.html(7):'OEBPS/images/cover.png':
referencedresourcemissinginthepackage
Checkfinishedwithwarningsorerrors!
这时候可能需要设置CLASSPATH使它指向EpubCheck的安装位置,因为确实需要导入几个外部库。如果得到这样的消息则需要设置CLASSPATH:
org.xml.sax.SAXParseException:noimplementationavailableforschemalanguage
withnamespaceURI"http://www.ascc.net/xml/schematron"
如果验证成功,就会看到“Noerrorsorwarningsdetected(没有检测到错误或警告)”。祝贺您完成了第一个EPUB!
--------------------------------------------------------------------------------
查看EPUB
测试不仅仅是验证,还要保证书的外观看起来不错。样式表能正确工作吗?章节的逻辑顺序是否正确?书中是否包含了所有需要的内容?
有多重EPUB阅读器可供选择。图1显示了AdobeDigitalEditions(ADE)的屏幕截图,这是最常用的EPUB阅读器。
图1.ADE中显示的EPUB
字体颜色和图像都显示出来了,不错。ADE未能用sans-serif字体正确地显示标题,不过这可能是CSS的问题。这时候最好换一个阅读器试试。图2是用我自己编写的、开放源代码的、基于Web的EPUB阅读器Bookworm显示的同一本书。
图2.在Bookworm中显示EPUB
这里的问题在于ADE不支持这种特殊声明。如果数字图书的格式非常重要,那么就必须了解不同阅读软件的特点。
前面我们费了很大力气手工创建了一个简单的EPUB,现在看看如何将一种常见的XML文档DocBook转换成EPUB。
/////////////////////////////////////////////////////////////////////////////////////////////Page5
从DocBook到EPUB
DocBook是需要维护大型技术文档的开发人员常用的选择。与传统字处理程序生成的文件不同,可以使用基于文本的版本控制系统管理DocBook输出。由于DocBook是XML,很容易将其转换成不同输出格式。2008年夏天出现了正式的DocBookXSL项目,将EPUB作为一种输出格式。
使用XSLT运行DocBook-to-EPUB管道
从一个简单DocBook文档开始,如清单14所示。该文档的类型为book,包括前言、两个章节以及标题页面中内联显示的图像。图像和DocBook源文件的目录相同。可以自己创建该文件和标题页,也可下载本文提供的例子。
清单14.简单的DocBook图书
<?xmlversion="1.0"encoding="utf-8"?`>
<book>
<bookinfo>
<title>MyEPUBbook</title>
<author><firstname>Liza</firstname>
<surname>Daly</surname></author>
<volumenum>1234</volumenum>
</bookinfo>
<prefaceid="preface">
<title>Titlepage</title>
<figureid="cover-image">
<title>OurEPUBcoverimageicon</title>
<graphicfileref="cover.png"/>
</figure>
</preface>
<chapterid="chapter1">
<title>ThisisaprettysimpleDocBookexample</title>
<para>
Notmuchtoseehere.
</para>
</chapter>
<chapterid="end-notes">
<title>Endnotes</title>
<para>
Thisspaceintentionallyleftblank.
</para>
</chapter>
</book>
然后从参考资料下载最新版本的DocBookXSL样式表,并安装xsltproc或Saxon之类的XSLT处理程序。本文使用xsltproc,大多数类UNIX系统上都能找到。转换DocBook文件,只需要用DocBookXSL中包含的EPUB模块运行该文件即可,如清单15所示。
清单15.将DocBook转化成EPUB
$xsltproc/path/to/docbook-xsl-1.74.0/epub/docbook.xsldocbook.xml
WritingOEBPS/bk01-toc.htmlforbook
WritingOEBPS/pr01.htmlforpreface(preface)
WritingOEBPS/ch01.htmlforchapter(chapter1)
WritingOEBPS/ch02.htmlforchapter(end-notes)
WritingOEBPS/index.htmlforbook
WritingOEBPS/toc.ncx
WritingOEBPS/content.opf
WritingMETA-INF/container.xml
定制DocBookXSL
DocBook-to-EPUB转换管道仍然比较新,可能需要定制XSLT以得到需要的结果。
然后添加mimetype文件并建立epub+zip档案。清单16显示了这三个命令和通过EpubCheck验证程序的结果。
清单16.从DocBook创建EPUB档案
$echo"application/epub+zip">mimetype
$zip-0Xqmy-book.epubmimetype
$zip-Xr9Dmy-book.epub*
$java-jarepubcheck.jarmy-book.epub
Noerrorsorwarningsdetected
太简单了!图3显示了ADE中的结果。
图3.ADE显示了从DocBook转化得到的EPUB
--------------------------------------------------------------------------------
利用Python和lxml实现DocBook-to-EPUB转换自动化
DocBookXSL大大降低了生成EPUB的复杂性,但是在XSLT之外还有几个步骤要执行。最后一节给出的Python示例程序能够生成有效的EPUB包。本教程显示了不同的方法,可下载完整的docbook2epub.py程序。
可使用不同的PythonXSLT库,我喜欢lxml。它不但提供了XSLT1.0必要的功能,而且解析效率高,完全支持XPath1.0,提供了专门处理HTML的扩展。如果喜欢不同的库或者使用Python以外的编程语言,修改这些例子也很简单。
用lxml调用DocBookXSL
使用lxml调用XSLT的最有效办法是事先解析XSLT,然后创建反复使用的转换器。这样很方便,因为我的DocBook-to-EPUB需要转换多个DocBook文件。如清单17所示。
清单17.使用lxml运行DocBookXSL
importos.path
fromlxmlimportetree
defconvert_docbook(docbook_file):
docbook_xsl=os.path.abspath('docbook-xsl/epub/docbook.xsl')
#GivetheXSLTprocessortheabilitytocreatenewdirectories
xslt_ac=etree.XSLTAccessControl(read_file=True,
write_file=True,
create_dir=True,
read_network=True,
write_network=False)
transform=etree.XSLT(etree.parse(docbook_xsl),access_control=xslt_ac)
transform(etree.parse(docbook_file))
DocBookXSL中的EPUB模块创建输出文件本身,因此转换过程什么也不返回。相反,DocBook在当前工作目录中创建了两个文件夹(META-INF和OEBPS),包含转换的结果。
将图片和其他资源复制到档案中
DocBookXSL不会对文档中使用的任何图片执行操作,仅仅创建元数据文件和要呈现的XHTML。由于EPUB规范要求content.opfmanifest列出所有资源,可以预料到manifest将寻找原始DocBook文件引用的任何图片。清单18显示了这种技术,其中假定path变量包含DocBookXSLT生成的、当前所处理的EPUB的路径。
清单18.解析OPF内容文件以寻找缺失的资源
importos.path,shutil
fromlxmlimportetree
deffind_resources(path='/path/to/our/epub/directory'):
opf=etree.parse(os.path.join(path,'OEBPS','content.opf'))
#Alltheopf:itemelementsareresources
foriteminopf.xpath('//opf:item',
namespaces={'opf':'http://www.idpf.org/2007/opf'}):
#IftheresourcewasnotalreadycreatedbyDocBookXSLitself,
#copyitintotheOEBPSfolder
href=item.attrib['href']
referenced_file=os.path.join(path,'OEBPS',href):
ifnotos.path.exists(referenced_file):
shutil.copy(href,os.path.join(path,'OEBPS'))
自动创建mimetype
DocBookXSL不会创建mimetype文件,不过清单19中所示的代码可以完成这项任务。
清单19.创建mimetype文件
defcreate_mimetype(path='/path/to/our/epub/directory'):
f='%s/%s'%(path,'mimetype')
f=open(f,'w')
#Becarefulnottoaddanewlinehere
f.write('application/epub+zip')
f.close()
用Python创建EPUB包
现在只需要将文件打包成有效的EPUBZIP包。需要分两步:将未经压缩的mimetype文件作为第一个文件加进去,然后添加其他目录。如清单20所示。
清单20.使用Pythonzipfile模块创建EPUB包
importzipfile,os
defcreate_archive(path='/path/to/our/epub/directory'):
'''CreatetheZIParchive.Themimetypemustbethefirstfileinthearchive
anditmustnotbecompressed.'''
epub_name='%s.epub'%os.path.basename(path)
#TheEPUBmustcontaintheMETA-INFandmimetypefilesattheroot,so
#we'llcreatethearchiveintheworkingdirectoryfirstandmoveitlater
os.chdir(path)
#Openanewzipfileforwriting
epub=zipfile.ZipFile(epub_name,'w')
#Addthemimetypefilefirstandsetittobeuncompressed
epub.write(MIMETYPE,compress_type=zipfile.ZIP_STORED)
#FortheremainingpathsintheEPUB,addalloftheirfiles
#usingnormalZIPcompression
forpinos.listdir('.'):
forfinos.listdir(p):
epub.write(os.path.join(p,f)),compress_type=zipfile.ZIP_DEFLATED)
epub.close()
好了!切记要进行验证。
/////////////////////////////////////////////////////////////////////////////////////////////Page6
结束语
上一节中的Python脚本仅仅是充分实现EPUB转换自动化的第一步。为了简化起见,没有涉及一些常见的情况,比如任意嵌套的路径、样式表或者内嵌字体。Ruby爱好者可以看看DocBookXSL分发包中所含的dbtoepub,方法与此类似。
因为EPUB还是一种比较年轻的格式,很多有效的转换方法还等待人们去创造。所幸的是,多数结构化标记,如reStructuredText或Markdown都已经存在生成HTML或者XHTML的渠道了;稍加修改来生成EPUB应该非常容易,尤其是有了本文所示的DocBook-to-EPUBPython或Ruby脚本这些例子以后。
因为EPUB基本上就是ZIP和XHTML,与其使用.zip文件,没有理由不使用EPUB来分发文档。拥有EPUB阅读器的读者可从传统元数据和自动目录表收益,没有阅读器的读者也可将其作为一般ZIP文件并在浏览器中查看XHTML内容。考虑将EPUB生成的代码添加到各类文档系统中,如Javadoc或Perldoc。EPUB是为具有图书长度的文档设计的,因此非常适合越来越多的在线或者渐进式编程图书。