Difference between revisions of "Epub Sample"
m (fix markup) |
m (mark XML error as fixed) |
||
Line 51: | Line 51: | ||
== Fix export.xml == | == Fix export.xml == | ||
+ | |||
+ | '''This error is fixed in ConTeXt of 27 August 2014.''' | ||
If you run the epub script on a single file, you’ll get a well-formed and usable export.xml. If you use a project structure, the root node {{code|<document>}} is missing. Just put it in manually (after the comment lines). You can also move the {{code|<metadata>}} block out of the first structure, but that’s merely a cosmetical error. | If you run the epub script on a single file, you’ll get a well-formed and usable export.xml. If you use a project structure, the root node {{code|<document>}} is missing. Just put it in manually (after the comment lines). You can also move the {{code|<metadata>}} block out of the first structure, but that’s merely a cosmetical error. |
Revision as of 10:17, 27 August 2014
< Epub
Creating an ebook with ConTeXt is still tedious and needs a lot of manual work - that will not change, since everyone has other needs, uses different structures etc. Here I’ll show you my workflow for creating ebooks of my songbooklets (that use LilyPond via filter module for the notes).
I’m using ConTeXt’s Project structure, separating content in products (for me: single booklets) and components (for me: single songs) with a common stylesheet (environment).
--Hraban 27 August 2014
Contents
ConTeXt setup
In your environment or product, you need these settings (perhaps not all of them):
\setupexport[hyphen=yes, %firstpage={cover.jpg}, % is ignored title={Songbook}, subtitle={}, author={Hraban} ] \setupbackend[export=export.xml] \settaggedmetadata[ % here you can set as many metadata entries as you like %firstpage={cover.jpg}, % is ignored title={Songbook}, name=ebook, % this becomes the name of the output directory author={Hraban}, subtitle={} version={\expand\currentdate}] % doesn’t work \setupinteraction[state=start, color=,contrastcolor=, % these settings are for PDF metadata title={Songbook}, subtitle={}, keywords={}, author={Hraban}]
Make sure to tag all your structural elements with \start...-\stop..., e.g. \startchapter, but even \startparagraph!
Then you can call ConTeXt and its ePub script:
context mysongbook mtxrun --script epub --make mysongbook
The first creates export.xml
and a bunch of other files.
The second creates a directory ebook.tree
with the proper structure for ePub. The ePub file in the tree directory is unusable.
We’ll mostly work with "export.xml" that contains all your content (check that, you’ll miss everything that was not properly tagged).
Fix export.xml
This error is fixed in ConTeXt of 27 August 2014.
If you run the epub script on a single file, you’ll get a well-formed and usable export.xml. If you use a project structure, the root node <document>
is missing. Just put it in manually (after the comment lines). You can also move the <metadata>
block out of the first structure, but that’s merely a cosmetical error.
A proper export.xml
starts like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <!-- input filename : solo --> <!-- processing date : Wed Aug 27 13:47:46 2014 --> <!-- context version : 2014.08.21 09:56 --> <!-- exporter version : 0.31 --> <document language="en" file="solo" date="Wed Aug 27 13:47:46 2014" context="2014.08.21 09:56" version="0.31" xmlns:m="http://www.w3.org/1998/Math/MathML"> <metadata> <metavariable name="author">Hraban</metavariable> </metadata> <section detail="chapter" location="aut:1"> ...
You don’t need the attributes of the document node, even if we could use the language setting.
ePub structure
This is the directory structure that we need for our ePub:
/songbook.tree/ ├── META-INF │ └── container.xml ├── OEBPS │ ├── Fonts │ │ └── somefont.otf │ ├── Images │ │ ├── ... │ │ ├── c_wjnter-1.png │ │ ├── c_wjnter-2.png │ │ ├── c_wjnter-3.png │ │ ├── c_wjnter-4.png │ │ ├── c_wjnter.png │ │ └── cover.jpg │ ├── Styles │ │ └── style.css │ ├── content.xhtml │ ├── cover.xhtml │ ├── songbook.opf │ └── toc.ncx └── mimetype
We use mimetype and container.xml unchanged from ConTeXt’s epub script and (re)create everything else. At the end this structure is just zipped with an "epub" file ending.
Unchanged files
For the records:
mimetype
application/epub+zip
container.xml
<?xml version="1.0" encoding="UTF-8"?> <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container"> <rootfiles> <rootfile full-path="OEBPS/songbook.opf" media-type="application/oebps-package+xml"/> </rootfiles> </container>
Transform XML to HTML
Even if the ePub format is supposed to work with any XML, most readers only accept HTML. I use the free version of Saxon and some XSL transformations for the conversion.
The incantation goes like saxon -o:content.xhtml -s:export.xml -xsl:export2html.xsl
.
I installed Saxon on my Mac with MacPorts, then instead of just "saxon" you must call java -jar /opt/local/share/java/saxon9he.jar
.
export2html.xsl
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version= "2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" /> <xsl:variable name="within-paragraph">0</xsl:variable ><!-- status: are we within a paragraph? --> <xsl:template match="/"> <html> <head> <meta charset="utf-8" /> <title><xsl:value-of select='//metavariable[@name="title"]'/> </title> <xsl:for-each select="//metavariable"> <meta> <xsl:attribute name="name"> <xsl:value-of select="@name"/> </xsl:attribute> <xsl:attribute name="content"> <xsl:apply-templates/> </xsl:attribute> </meta> </xsl:for-each> <link rel="stylesheet" href="style.css" type="text/css" ></link><!-- for testing the html outside the ePub tree --> <link rel="stylesheet" href="Styles/style.css" type="text/css" ></link> </head> <body lang="de"> <h1 class="booktitle"><xsl:value-of select='//metavariable[@name="title"]'/></h1> <h2 class="subtitle"><xsl:value-of select='//metavariable[@name="subtitle"]'/></h2> <p class="author"><xsl:value-of select='//metavariable[@name="author"]'/></p> <xsl:apply-templates/> </body> </html> </xsl:template> <xsl:template match="metadata"> </xsl:template> <xsl:template match="section"> <a name="{translate(@location,':','_')}"/> <div class="{@detail}"> <xsl:apply-templates/> </div> </xsl:template> <xsl:template match="sectiontitle"> <xsl:choose> <xsl:when test="../@detail='part'"> <h2><xsl:apply-templates/> </h2> </xsl:when> <xsl:when test="../@detail='chapter'"> <h3><xsl:apply-templates/> </h3> </xsl:when> <xsl:when test="../@detail='section'"> <h4><xsl:apply-templates/> </h4> </xsl:when> <xsl:when test="../@detail='subsection'"> <h5><xsl:apply-templates/> </h5> </xsl:when> <xsl:otherwise> <h6 class="../@detail"><xsl:apply-templates/> </h6> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="sectioncontent"> <div class="{../@detail}-content"> <xsl:apply-templates/> </div> </xsl:template> <xsl:template match="externalfilter"> <div class="{@detail}"> <xsl:apply-templates/> </div> </xsl:template> <xsl:template match="lines"> <div class="lines"> <xsl:apply-templates/> </div> </xsl:template> <xsl:template match="line"> <xsl:apply-templates/> <br /> </xsl:template> <xsl:template match="list"> <ul> <xsl:apply-templates/> </ul> </xsl:template> <xsl:template match="listitem"> <li><xsl:apply-templates/></li> </xsl:template> <xsl:template match="listcontent"> <span class="listcontent"><xsl:apply-templates/></span> </xsl:template> <xsl:template match="listpage"> <span class="listpage"><xsl:apply-templates/></span> </xsl:template> <xsl:template match="break"> <xsl:if test="within-paragraph = 0"> <xsl:text disable-output-escaping="yes"><![CDATA[</p><p>]]></xsl:text> </xsl:if> <xsl:if test="within-paragraph > 0"> <br/> </xsl:if> </xsl:template> <xsl:template match="paragraph"> <xsl:variable name="within-paragraph">1</xsl:variable > <p><xsl:apply-templates/></p> <xsl:variable name="within-paragraph">0</xsl:variable > </xsl:template> <xsl:template match="delimited"> <span class="delim-{@detail}"><xsl:apply-templates/></span> </xsl:template> <xsl:template match="highlight"> <xsl:if test="@detail = 'emph'"> <em><xsl:apply-templates/></em> </xsl:if> </xsl:template> <!-- all the images in my songbook are notes generated by LilyPond via t-filter; in ePub I shorten the file names --> <xsl:template match="image"> <img src="Images/{substring-after(@name,'prd_songbook-temp-lilypond-')}.png" id="{@id}" alt="{@name}" /> </xsl:template> <xsl:template match="link"> <a href="#{@location}" title="{@destination}"><xsl:apply-templates/></a> </xsl:template> </xsl:stylesheet> <!-- h1 = book title h2 = part title h3 = chapter h4 = section h5 = subsection -->
style.css
Now you have a (hopefully usable) (X)HTML file, you need a CSS for the styling.
A simple example:
body { font-family: TeX Gyre Schola, Century Schoolbook, serif; font-size: 12pt; margin: 0 auto; max-width: 40em; line-height: 1.44; -webkit-hyphens: auto; -moz-hyphens: auto; -ms-hyphens: auto; hyphens: auto; } h1, h2, h3, h4, h4, h6 { font-family: TeX Gyre Heros, Helvetica, Arial, sans-serif; color: #286000; -webkit-hyphens: manual; -moz-hyphens: manual; -ms-hyphens: manual; hyphens: manual; } .part { page-break-before: always; } .chapter { margin-top: 1em; border-top: 1px solid #ccc; } img { max-width: 100%; max-height: 100%; } .lilypond { margin: 1em 0; } .lilypond img { width: 100%; }
Create a Cover
Create a cover.xhtml
:
export2cover.xsl
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version= "2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" /> <xsl:template match="/"> <html> <head> <title><xsl:value-of select='//metavariable[@name="title"]'/></title> <link rel="stylesheet" href="style.css" type="text/css" ></link> <link rel="stylesheet" href="Styles/style.css" type="text/css" ></link> </head> <body> <div> <img src="Images/cover.jpg"> <xsl:attribute name="alt"> <xsl:value-of select='//metavariable[@name="title"]'/> </xsl:attribute> </img> </div> </body> </html> </xsl:template> </xsl:stylesheet>
If you don’t have a different cover picture, create one from the first page of your PDF (using ImageMagick’s convert
):
convert -density 196 songbook.pdf'[0]' +repage cover.jpg
Create OPF
This file keeps the others together.
export2opf.xsl
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version= "2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" encoding="utf-8" indent="yes" /> <xsl:template match="/"> <package version="2.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="BookId"> <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf"> <dc:title><xsl:value-of select='//metavariable[@name="title"]'/></dc:title> <dc:language><xsl:value-of select='//document/@language'/></dc:language> <dc:identifier id="BookId" opf:scheme="UUID">urn:uuid:xxxx</dc:identifier> <dc:creator><xsl:value-of select='//metavariable[@name="author"]'/></dc:creator> <dc:date><xsl:value-of select='//document/@date'/></dc:date> <meta name="cover" content="-" /> </metadata> <manifest> <item id="cover-xhtml" href="cover.xhtml" media-type="application/xhtml+xml"/> <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/> <item id="content-xhtml" href="content.xhtml" media-type="application/xhtml+xml"/> </manifest> <spine toc="ncx"> <itemref idref="cover-xhtml" /> <itemref idref="content-xhtml" /> </spine> </package> </xsl:template> </xsl:stylesheet>
Create NCX (table of contents)
This one will probably differ from your setup – I (ab)use an index for an alphabetically ordered table of contents. The structure that ConTeXt outputs for index entries is somewhat uncomfortable...
export2ncx.xsl
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version= "2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" encoding="utf-8" indent="yes" /> <xsl:template match="/"> <!-- <!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd"> --> <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1"> <head> <meta name="dtb:uid" content="BookId" /> <meta name="dtb:depth" content="1" /> <meta name="dtb:totalPgeCount" content="0" /> <meta name="dtb:maxPageNumber" content="0" /> </head> <docTitle> <text><xsl:value-of select='//metavariable[@name="title"]'/></text> </docTitle> <docAuthor> <text><xsl:value-of select='//metavariable[@name="author"]'/></text> </docAuthor> <navMap> <navPoint id="aut_1" playOrder="1"> <navLabel> <text>Start</text> </navLabel> <content src="content.xhtml"/> </navPoint> <xsl:for-each select="//registerentry"> <navPoint> <xsl:attribute name="id"> <xsl:value-of select='translate((following-sibling::registerpages/registerpage/link/@location)[1], ":", "_")'/> </xsl:attribute> <xsl:attribute name="playOrder"> <xsl:value-of select="2 + count(preceding-sibling::registerentry)" /> </xsl:attribute> <navLabel> <text><xsl:apply-templates/></text> </navLabel> <content> <xsl:attribute name="src">content.xhtml#<xsl:value-of select='translate((following-sibling::registerpages/registerpage/link/@location)[1], ":", "_")'/></xsl:attribute> </content> </navPoint> </xsl:for-each> </navMap> </ncx> </xsl:template> </xsl:stylesheet>
The links to a named anchor should work, but don’t in my reader. I must investigate further...
Converting images
In my case, all images are note lines, generated by LilyPond in one temporary directory. Your mileage will vary.
ConTeXt’s epub script creates a list of images in export-images.css
, you might want to parse that to find all images.
cd $LILYPONDTEMPDIR for IMG in prd_songbook-temp-lilypond-*.pdf; do NEWIMG=${IMG#prd_songbook-temp-lilypond-} convert -density 196 $IMG'[0]' -trim +repage ../songbook.tree/OEBPS/Images/${NEWIMG%.pdf}.png done
Create ePub
Look at the tree above - are all your files in the right location? Then zip your tree!
cd songbook.tree zip -ur ../songbook.epub *
Now test it with your reader or editor (e.g. Calibre). (Apple iBooks or Adobe Digital Editions don’t work, don’t know why.)