#!/usr/bin/env ruby
+# This program converts DocBook documents into .epub files.
+#
+# Usage: dbtoepub [OPTIONS] [DocBook Files]
+#
+# .epub is defined by the IDPF at www.idpf.org and is made up of 3 standards:
+# - Open Publication Structure (OPS)
+# - Open Packaging Format (OPF)
+# - Open Container Format (OCF)
+#
+# Specific options:
+# -d, --debug Show debugging output.
+# -h, --help Display usage info
+# -v, --verbose Make output verbose
+
lib = File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
$LOAD_PATH.unshift(lib) if File.exist?(lib)
-# Keith Fahlgren
-# Sat Feb 23 17:22:35 PST 2008
-
require 'optparse'
require 'docbook'
end
def self.invalid?(file)
- # obnoxiously, we can't just check for a non-zero output...
+ # Obnoxiously, we can't just check for a non-zero output...
cmd = "#{CHECKER} #{file}"
output = `#{cmd} 2>&1`
def bundle_epub(output_file, verbose)
quiet = verbose ? "" : "-q"
- # zip -X -r ../book.epub mimetype META-INF OEBPS
mimetype_filename = write_mimetype()
meta = File.basename(@meta_dir)
oebps = File.basename(@oebps_dir)
images = copy_images()
callouts = copy_callouts()
+ # zip -X -r ../book.epub mimetype META-INF OEBPS
zip_cmd = "cd #{@output_dir} && #{ZIPPER} #{quiet} -X -r #{File.expand_path(output_file)} #{mimetype_filename} #{meta} #{oebps}"
puts zip_cmd if $DEBUG
success = system(zip_cmd)
lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(lib) if File.exist?(lib)
-require 'tmpdir'
require 'fileutils'
+require 'tmpdir'
require 'rubygems'
require 'spec'
--- /dev/null
+#!/usr/bin/env ruby
+spec = File.expand_path(File.dirname(__FILE__))
+$LOAD_PATH.unshift(spec) if File.exist?(spec)
+require 'spec/spec_helper'
+
+lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
+$LOAD_PATH.unshift(lib) if File.exist?(lib)
+
+require 'fileutils'
+require 'rexml/document'
+require 'tmpdir'
+
+require 'rubygems'
+require 'spec'
+
+require 'docbook'
+
+$DEBUG = false
+
+describe DocBook::Epub do
+ before(:all) do
+ @filedir = File.expand_path(File.join(File.dirname(__FILE__), 'files'))
+ @testdocsdir = File.expand_path(File.join(File.dirname(__FILE__), 'testdocs'))
+ @tmpdir = File.join(Dir::tmpdir(), "epubregressions"); Dir.mkdir(@tmpdir) rescue Errno::EEXIST
+ end
+
+ it "should not include two <itemref>s to the contents of <part>s in the OPF file" do
+ part_file = File.join(@testdocsdir, "subtitle.001.xml")
+ epub_file = File.join(@tmpdir, File.basename(part_file, ".xml") + ".epub")
+ part_epub = DocBook::Epub.new(part_file, @tmpdir)
+ part_epub.render_to_file(epub_file, $DEBUG)
+
+ FileUtils.copy(epub_file, "./.t.epub") if $DEBUG
+
+ itemref_tmpdir = File.join(Dir::tmpdir(), "epubitemref"); Dir.mkdir(itemref_tmpdir) rescue Errno::EEXIST
+ system("unzip -q -o -d #{itemref_tmpdir} #{epub_file}")
+ opf_file = File.join(itemref_tmpdir, "OEBPS", "content.opf")
+ opf = REXML::Document.new(File.new(opf_file))
+
+ itemrefs = REXML::XPath.match(opf, "//itemref").map {|e| e.attributes['idref']}
+ itemrefs.should == itemrefs.uniq
+ end
+
+ after(:all) do
+ FileUtils.rm_r(@tmpdir, :force => true)
+ end
+end
require 'docbook'
-$DEBUG = true
+$DEBUG = false
TESTDOCSDIR = File.expand_path(File.join(File.dirname(__FILE__), 'testdocs'))
NUMBER_TO_TEST = 15
@tmpdir = File.join(Dir::tmpdir(), "epubspecsmoke"); Dir.mkdir(@tmpdir) rescue Errno::EEXIST
end
- # TODO olink, index, cmdsynopsis, refentry, table
-
- Dir["#{TESTDOCSDIR}/programlisting*.006.xml"].sort_by { rand }[0..(NUMBER_TO_TEST-1)].each do |xml_file|
- #Dir["#{TESTDOCSDIR}/*.[0-9][0-9][0-9].xml"].sort_by { rand }[0..(NUMBER_TO_TEST-1)].each do |xml_file|
+ Dir["#{TESTDOCSDIR}/*.[0-9][0-9][0-9].xml"].sort_by { rand }[0..(NUMBER_TO_TEST-1)].each do |xml_file|
it "should be able to render a valid .epub for the test document #{xml_file}" do
epub = DocBook::Epub.new(xml_file, @tmpdir)
epub_file = File.join(@tmpdir, File.basename(xml_file, ".xml") + ".epub")
@tmpdir = File.join(Dir::tmpdir(), "epubspecsmoke"); Dir.mkdir(@tmpdir) rescue Errno::EEXIST
end
+ # TODO
+ # Known failures on all of:
+ # calloutlist.003.xml
+ # extensions.00[24].xml
+ # programlisting.00[26].xml
+ # olink.*.xml
+ # cmdsynopsis.002.xml
+ # refentry.007.xml
+ # programlistingco.002.xml
+ # textobject.*.xml
+ #
+ # The causes of the failures are typically missing extensions in xsltproc
+ # (specifically insertfile, for textdata, imagedata, graphic, or inlinegraphic
+ # text/XML @filerefs, invalid XHTML 1.1 (block elements inside inlines that
+ # I don't feel like # fixing because I think they're edge cases), callouts
+ # (which are hard in .epub), or test docs I really don't think are cromulent.
+
+ # Current passage rate:
+ # 224 examples, 12 failures (94.6%)
+
Dir["#{TESTDOCSDIR}/[a-z]*.[0-9][0-9][0-9].xml"].each_with_index do |xml_file, ix|
it "should be able to render a valid .epub for the test document #{xml_file} [#{ix}]" do
epub = DocBook::Epub.new(xml_file, @tmpdir)
</xsl:param>
<xsl:param name="ade.extensions" select="0"/>
- <xsl:param name="epub.autolabel" select="'1'"/> <!-- TODO: Document this in params -->
- <xsl:param name="manifest.in.base.dir" select="'1'"/> <!-- TODO: Document this in params; is '1' correct? -->
+ <xsl:param name="epub.autolabel" select="'1'"/>
- <xsl:param name="epub.oebps.dir" select="'OEBPS/'"/>
+
+ <xsl:param name="manifest.in.base.dir" select="'1'"/>
<xsl:param name="base.dir" select="$epub.oebps.dir"/>
+ <xsl:param name="epub.oebps.dir" select="'OEBPS/'"/>
<xsl:param name="epub.ncx.filename" select="'toc.ncx'"/>
<xsl:param name="epub.container.filename" select="'container.xml'"/>
<xsl:param name="epub.opf.filename" select="concat($epub.oebps.dir, 'content.opf')"/>
<xsl:param name="epub.html.toc.id">htmltoc</xsl:param>
<xsl:param name="epub.metainf.dir" select="'META-INF/'"/>
-
<!-- Per Bob Stayton:
"""Process your documents with the css.decoration parameter set to zero.
That will avoid the use of style attributes in XHTML elements where they are not permitted."""
</xsl:when>
<xsl:otherwise>
<xsl:variable name="title">
- <xsl:if test="$epub.autolabel=1">
+ <xsl:if test="$epub.autolabel != 0">
<xsl:variable name="label.markup">
<xsl:apply-templates select="/*" mode="label.markup" />
</xsl:variable>
mode="ncx">
<xsl:variable name="depth" select="count(ancestor::*)"/>
<xsl:variable name="title">
- <xsl:if test="$epub.autolabel=1">
+ <xsl:if test="$epub.autolabel != 0">
<xsl:variable name="label.markup">
<xsl:apply-templates select="." mode="label.markup" />
</xsl:variable>
</xsl:if>
<!-- TODO: be nice to have a idref="titlepage" here -->
- <xsl:if test="$root.is.a.chunk != '0'">
-
- <xsl:apply-templates select="/*" mode="opf.spine"/>
- </xsl:if>
- <xsl:apply-templates select="/*/*|
- /*/part/*" mode="opf.spine"/>
+ <xsl:choose>
+ <xsl:when test="$root.is.a.chunk != '0'">
+ <xsl:apply-templates select="/*" mode="opf.spine"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates select="/*/*" mode="opf.spine"/>
+ </xsl:otherwise>
+ </xsl:choose>
</xsl:element>
</xsl:template>
<xsl:template match="mediaobjectco"
mode="opf.manifest">
- <xsl:message>Warning: mediaobjectco almost certain will not render as expected in .epub </xsl:message>
+ <xsl:message>Warning: mediaobjectco almost certainly will not render as expected in .epub!</xsl:message>
<xsl:apply-templates select="imageobjectco/imageobject/imagedata"
mode="opf.manifest"/>
</xsl:template>
<xsl:value-of select="$html.ext"/>
</xsl:template>
+ <xsl:template match="bibliodiv" mode="label.markup">
+ </xsl:template>
+
+
</xsl:stylesheet>