if not modules then modules = { } end modules ['mtx-epub'] = {
version = 1.001,
comment = "companion to mtxrun.lua",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-- The epub specification is far from beautiful. Especially the id related
-- part is messy and devices/programs react differently on them (so an id is not
-- really an id but has some special property). Then there is this ncx suffix
-- thing. Somehow it give the impression of a reversed engineered application
-- format so it will probably take a few cycles to let it become a real
-- clean standard. Thanks to Adam Reviczky, Luigi Scarso and Andy Thomas for
-- helping to figure out all the puzzling details.
-- This is preliminary code. At some point we will deal with images as well but
-- first we need a decent strategy to export them. More information will be
-- available on the wiki.
local format, gsub = string.format, string.gsub
local concat = table.concat
local helpinfo = [[
mtx-epub
ConTeXt EPUB Helpers
0.12
create epub zip file
Example
mtxrun --script epub --make mydocument
]]
local application = logs.application {
name = "mtx-epub",
banner = "ConTeXt EPUB Helpers 0.12",
helpinfo = helpinfo,
}
-- script code
scripts = scripts or { }
scripts.epub = scripts.epub or { }
local mimetype = "application/epub+zip"
local container = [[
]]
local package = [[
%s
%s
urn:uuid:%s
%s
%s
%s
]]
local item = [[ ]]
local toc = [[
%s
start
]]
local coverxhtml = [[
cover.xhtml
]]
-- We need to figure out what is permitted. Numbers only seem to give
-- problems is some applications as do names with dashes. Also the
-- optional toc is supposed to be there and although id's are by
-- concept neutral, there are sometimes hard requirements with respect
-- to their name like ncx and toc.ncx). Maybe we should stick to 3.0
-- only.
local function dumbid(filename)
-- return (string.gsub(os.uuid(),"%-%","")) -- to be tested
return file.nameonly(filename) .. "-" .. file.suffix(filename)
end
local mimetypes = {
xhtml = "application/xhtml+xml",
xml = "application/xhtml+xml",
css = "text/css",
svg = "image/svg+xml",
png = "image/png",
jpg = "image/jpeg",
ncx = "application/x-dtbncx+xml",
gif = "image/gif",
-- default = "text/plain",
}
local idmakers = {
ncx = function(filename) return "ncx" end,
-- css = function(filename) return "stylesheet" end,
default = function(filename) return dumbid(filename) end,
}
-- specification = {
-- name = "document",
-- identifier = "123",
-- root = "a.xhtml",
-- files = {
-- "a.xhtml",
-- "b.css",
-- "c.png",
-- }
-- }
local function locateimages(oldname,newname,subpath)
local data = io.loaddata(oldname)
local images = { }
local done = gsub(data,"(background%-image *: * url%()(.-)(%))", function(before,name,after)
if subpath then
name = file.join(subpath,name)
end
images[#images+1] = name
return before .. name .. after
end)
if newname then
io.savedata(done,newname)
end
return images
end
local zippers = {
{
name = "zip",
uncompressed = "zip %s -X -0 %s",
compressed = "zip %s -X -9 -r %s",
},
{
name = "7zip (7z)",
uncompressed = "7z a -tzip -mx0 %s %s",
compressed = "7z a -tzip %s %s",
},
}
function scripts.epub.make()
local filename = environment.files[1]
if filename and filename ~= "" and type(filename) == "string" then
filename = file.basename(filename)
local specfile = file.replacesuffix(filename,"specification")
local specification = lfs.isfile(specfile) and dofile(specfile) or { }
-- inspect(specification)
local name = specification.name or file.removesuffix(filename)
local identifier = specification.identifier or os.uuid(true)
local files = specification.files or { file.addsuffix(filename,"xhtml") }
local images = specification.images or { }
local root = specification.root or files[1]
local language = specification.language or "en"
local creator = specification.author or "My Self"
local title = specification.title or "My Title"
local firstpage = specification.firstpage or ""
local lastpage = specification.lastpage or ""
-- identifier = gsub(identifier,"[^a-zA-z0-9]","")
if firstpage ~= "" then
images[firstpage] = firstpage
end
if lastpage ~= "" then
images[lastpage] = lastpage
end
identifier = "BookId" -- weird requirement
local epubname = name
local epubpath = file.replacesuffix(name,"tree")
local epubfile = file.replacesuffix(name,"epub")
local epubroot = file.replacesuffix(name,"opf")
local epubtoc = "toc.ncx"
local epubcover = "cover.xhtml"
application.report("creating paths in tree %s",epubpath)
lfs.mkdir(epubpath)
lfs.mkdir(file.join(epubpath,"META-INF"))
lfs.mkdir(file.join(epubpath,"OEBPS"))
local used = { }
local function copyone(filename)
local suffix = file.suffix(filename)
local mime = mimetypes[suffix]
if mime then
local idmaker = idmakers[suffix] or idmakers.default
local target = file.join(epubpath,"OEBPS",filename)
file.copy(filename,target)
application.report("copying %s to %s",filename,target)
used[#used+1] = format(item,idmaker(filename),filename,mime)
end
end
copyone("cover.xhtml")
copyone("toc.ncx")
local function copythem(files)
for i=1,#files do
local filename = files[i]
if type(filename) == "string" then
copyone(filename)
end
end
end
copythem(files)
local theimages = { }
for k, v in table.sortedpairs(images) do
theimages[#theimages+1] = k
if not lfs.isfile(k) and file.suffix(k) == "svg" and file.suffix(v) == "pdf" then
local command = format("inkscape --export-plain-svg=%s %s",k,v)
application.report("running command '%s'\n\n",command)
os.execute(command)
end
end
copythem(theimages)
local idmaker = idmakers[file.suffix(root)] or idmakers.default
container = format(container,
epubroot
)
package = format(package,
identifier,
title,
language,
identifier,
os.uuid(),
creator,
os.date("!%Y-%m-%dT%H:%M:%SZ"),
idmaker(firstpage),
concat(used,"\n"),
idmaker(root)
)
toc = format(toc,
identifier,
title,
root
)
coverxhtml = format(coverxhtml,
firstpage
)
io.savedata(file.join(epubpath,"mimetype"),mimetype)
io.savedata(file.join(epubpath,"META-INF","container.xml"),container)
io.savedata(file.join(epubpath,"OEBPS",epubroot),package)
io.savedata(file.join(epubpath,"OEBPS",epubtoc),toc)
io.savedata(file.join(epubpath,"OEBPS",epubcover),coverxhtml)
application.report("creating archive\n\n")
lfs.chdir(epubpath)
os.remove(epubfile)
local done = false
for i=1,#zippers do
local zipper = zippers[i]
if os.execute(format(zipper.uncompressed,epubfile,"mimetype")) then
os.execute(format(zipper.compressed,epubfile,"META-INF"))
os.execute(format(zipper.compressed,epubfile,"OEBPS"))
done = zipper.name
break
end
end
lfs.chdir("..")
if done then
application.report("epub archive made using %s: %s",done,file.join(epubpath,epubfile))
else
local list = { }
for i=1,#zippers do
list[#list+1] = zipper.name
end
application.report("no epub archive made, install one of: %s",concat(list," "))
end
end
end
--
if environment.argument("make") then
scripts.epub.make()
elseif environment.argument("exporthelp") then
application.export(environment.argument("exporthelp"),environment.files[1])
else
application.help()
end