mtx-epub.lua /size: 27 Kb    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['mtx-epub'] = {
2    version   = 1.001,
3    comment   = "companion to mtxrun.lua",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9-- The epub specification is far from beautiful. Especially the id related
10-- part is messy and devices/programs react differently on them (so an id is not
11-- really an id but has some special property). Then there is this ncx suffix
12-- thing. Somehow it give the impression of a reversed engineered application
13-- format so it will probably take a few cycles to let it become a real
14-- clean standard. Thanks to Adam Reviczky, Luigi Scarso and Andy Thomas for
15-- helping to figure out all the puzzling details.
16
17-- This is preliminary code. At some point we will deal with images as well but
18-- first we need a decent strategy to export them. More information will be
19-- available on the wiki.
20
21-- META-INF
22--     container.xml
23-- OEBPS
24--     content.opf
25--     toc.ncx
26--     images
27--     styles
28-- mimetype
29
30-- todo:
31--
32-- remove m_k_v_i prefixes
33-- remap fonts %mono% in css so that we can replace
34-- coverpage tests
35-- split up
36
37-- todo: automated cover page:
38--
39-- \startMPpage
40--     StartPage ;
41--         fill Page withcolor .5red ;
42--         numeric n ;
43--         for i=10 downto 1 :
44--             n := i * PaperWidth/40  ;
45--             draw
46--                 lrcorner Page shifted (0,n)
47--               % -- lrcorner Page
48--                 -- lrcorner Page shifted (-n,0)
49--               % -- cycle
50--                 withpen pencircle scaled 1mm
51--                 withcolor white ;
52--         endfor ;
53--         picture p ; p := image (
54--             draw
55--                 anchored.top(
56--                     textext.bot("\tttf Some Title")
57--                         xsized .8PaperWidth
58--                    ,center topboundary Page
59--                 )
60--                     withcolor white ;
61--         ) ;
62--         picture q ; q := image (
63--             draw
64--                 anchored.top(
65--                     textext.bot("\tttf An Author")
66--                         xsized .4PaperWidth
67--                         shifted (0,-PaperHeight/40)
68--                    ,center bottomboundary p
69--                 )
70--                     withcolor white ;
71--         ) ;
72--         draw p ;
73--         draw q ;
74--     StopPage ;
75-- \stopMPpage
76
77local format, gsub, find = string.format, string.gsub, string.find
78local concat, sortedhash = table.concat, table.sortedhash
79
80local formatters      = string.formatters
81local replacetemplate = utilities.templates.replace
82
83local addsuffix       = file.addsuffix
84local nameonly        = file.nameonly
85local basename        = file.basename
86local pathpart        = file.pathpart
87local joinfile        = file.join
88local suffix          = file.suffix
89local addsuffix       = file.addsuffix
90local removesuffix    = file.removesuffix
91local replacesuffix   = file.replacesuffix
92
93local copyfile        = file.copy
94local removefile      = os.remove
95
96local needsupdating   = file.needsupdating
97
98local isdir           = lfs.isdir
99local isfile          = lfs.isfile
100local mkdir           = lfs.mkdir
101
102local pushdir         = dir.push
103local popdir          = dir.pop
104
105local helpinfo = [[
106<?xml version="1.0"?>
107<application>
108 <metadata>
109  <entry name="name">mtx-epub</entry>
110  <entry name="detail">ConTeXt EPUB Helpers</entry>
111  <entry name="version">1.10</entry>
112 </metadata>
113 <flags>
114  <category name="basic">
115   <subcategory>
116    <flag name="make"><short>create epub zip file</short></flag>
117    <flag name="purge"><short>remove obsolete files</short></flag>
118    <flag name="rename"><short>rename images to sane names</short></flag>
119    <flag name="svgmath"><short>convert mathml to svg</short></flag>
120    <flag name="svgstyle"><short>use given tex style for svg generation (overloads style in specification)</short></flag>
121    <flag name="all"><short>assume: --purge --rename --svgmath (for fast testing)</short></flag>
122   </subcategory>
123  </category>
124 </flags>
125 <examples>
126  <category>
127   <title>Example</title>
128   <subcategory>
129    <example><command>mtxrun --script epub --make mydocument</command></example>
130   </subcategory>
131  </category>
132 </examples>
133</application>
134]]
135
136local application = logs.application {
137    name     = "mtx-epub",
138    banner   = "ConTeXt EPUB Helpers 1.10",
139    helpinfo = helpinfo,
140}
141
142local report = application.report
143
144-- script code
145
146scripts      = scripts      or { }
147scripts.epub = scripts.epub or { }
148
149local mimetype = "application/epub+zip"
150
151local t_container = [[
152<?xml version="1.0" encoding="UTF-8"?>
153
154<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
155    <rootfiles>
156        <rootfile full-path="OEBPS/%rootfile%" media-type="application/oebps-package+xml"/>
157    </rootfiles>
158</container>
159]]
160
161-- urn:uuid:
162
163-- <dc:identifier id="%identifier%" opf:scheme="UUID">%uuid%</dc:identifier>
164
165local t_package = [[
166<?xml version="1.0" encoding="UTF-8"?>
167
168<package xmlns="http://www.idpf.org/2007/opf" unique-identifier="%identifier%" version="3.0">
169
170    <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
171        <dc:title>%title%</dc:title>
172        <dc:language>%language%</dc:language>
173        <dc:identifier id="%identifier%">%uuid%</dc:identifier>
174        <dc:creator>%creator%</dc:creator>
175        <dc:date>%date%</dc:date>
176        <!--
177            <dc:subject>%subject%</dc:subject>
178            <dc:description>%description%</dc:description>
179            <dc:publisher>%publisher%</dc:publisher>
180            <dc:source>%source%</dc:source>
181            <dc:relation>%relation%</dc:relation>
182            <dc:coverage>%coverage%</dc:coverage>
183            <dc:rights>%rights%</dc:rights>
184        -->
185        <meta name="cover" content="%coverpage%" />
186        <meta name="generator" content="ConTeXt MkIV" />
187        <meta property="dcterms:modified">%date%</meta>
188    </metadata>
189
190    <manifest>
191%manifest%
192    </manifest>
193
194    <spine toc="ncx">
195        <itemref idref="cover-xhtml" />
196        <itemref idref="%rootfile%" />
197    </spine>
198
199</package>
200]]
201
202
203local t_item = [[        <item id="%id%" href="%filename%" media-type="%mime%" />]]
204local t_prop = [[        <item id="%id%" href="%filename%" media-type="%mime%" properties="%properties%" />]]
205
206-- <!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
207
208local t_toc = [[
209<?xml version="1.0" encoding="UTF-8"?>
210
211<!-- this is no longer needed in epub 3.0+ -->
212
213<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
214
215    <head>
216        <meta name="generator"         content="ConTeXt MkIV" />
217        <meta name="dtb:uid"           content="%identifier%" />
218        <meta name="dtb:depth"         content="2" />
219        <meta name="dtb:totalPgeCount" content="0" />
220        <meta name="dtb:maxPageNumber" content="0" />
221    </head>
222
223    <docTitle>
224        <text>%title%</text>
225    </docTitle>
226
227    <docAuthor>
228        <text>%author%</text>
229    </docAuthor>
230
231    <navMap>
232        <navPoint id="np-1" playOrder="1">
233            <navLabel>
234                <text>start</text>
235            </navLabel>
236            <content src="%root%"/>
237        </navPoint>
238    </navMap>
239
240</ncx>
241]]
242
243local t_navtoc = [[
244<?xml version="1.0" encoding="UTF-8"?>
245
246<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
247    <head>
248        <meta charset="utf-8" />
249        <title>navtoc</title>
250    </head>
251    <body>
252        <div class="navtoc">
253            <!-- <nav epub:type="lot"> -->
254            <nav epub:type="toc" id="navtoc">
255                <ol>
256                    <li><a href="%root%">document</a></li>
257                </ol>
258            </nav>
259        </div>
260    </body>
261</html>
262]]
263
264-- <html xmlns="http://www.w3.org/1999/xhtml">
265-- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
266
267local t_coverxhtml = [[
268<?xml version="1.0" encoding="UTF-8"?>
269
270<html xmlns="http://www.w3.org/1999/xhtml">
271    <head>
272        <meta charset="utf-8" />
273        <title>cover page</title>
274    </head>
275    <body>
276        <div class="coverpage">
277            %content%
278        </div>
279    </body>
280</html>
281]]
282
283local t_coverimg = [[
284    <img src="%image%" alt="The cover image" style="max-width: 100%%;" />
285]]
286
287-- We need to figure out what is permitted. Numbers only seem to give
288-- problems is some applications as do names with dashes. Also the
289-- optional toc is supposed to be there and although id's are by
290-- concept neutral, there are sometimes hard requirements with respect
291-- to their name like ncx and toc.ncx). Maybe we should stick to 3.0
292-- only.
293
294local function dumbid(filename)
295 -- return (string.gsub(os.uuid(),"%-%","")) -- to be tested
296    return nameonly(filename) .. "-" .. suffix(filename)
297end
298
299local mimetypes = {
300    xhtml   = "application/xhtml+xml",
301    xml     = "application/xhtml+xml",
302    html    = "application/html",
303    css     = "text/css",
304    svg     = "image/svg+xml",
305    png     = "image/png",
306    jpg     = "image/jpeg",
307    ncx     = "application/x-dtbncx+xml",
308    gif     = "image/gif",
309 -- default = "text/plain",
310}
311
312local idmakers = {
313    ncx     = function(filename) return "ncx"            end,
314 -- css     = function(filename) return "stylesheet"     end,
315    default = function(filename) return dumbid(filename) end,
316}
317
318local function relocateimages(imagedata,oldname,newname,subpath,rename)
319    local data = io.loaddata(oldname)
320    if data then
321        subpath = joinfile("..",subpath)
322        report("relocating images")
323        local n = 0
324        local done = gsub(data,[[(id=")(.-)(".-background%-image *: *url%()(.-)(%))]], function(s1,id,s2,name,s3)
325            local data = imagedata[id]
326            if data then
327                local newname = data[id].newname
328                if newname then
329                    if subpath then
330                        name = joinfile(subpath,basename(newname))
331                    else
332                        name = basename(newname)
333                    end
334                 -- name = url.addscheme(name)
335                end
336                if newname then
337                    n = n + 1
338                    if rename then
339                        name = joinfile(subpath,addsuffix(id,suffix(name)))
340                    end
341                    return s1 .. id .. s2 .. name .. s3
342                end
343            end
344        end)
345        report("%s images relocated in %a",n,newname)
346        if newname then
347            io.savedata(newname,done)
348        end
349    end
350    return images
351end
352
353function reportobsolete(oldfiles,newfiles,purge)
354
355    for i=1,#oldfiles do oldfiles[i] = gsub(oldfiles[i],"^[%./]+","") end
356    for i=1,#newfiles do newfiles[i] = gsub(newfiles[i],"^[%./]+","") end
357
358    local old  = table.tohash(oldfiles)
359    local new  = table.tohash(newfiles)
360    local done = false
361
362    for name in sortedhash(old) do
363        if not new[name] then
364            if not done then
365                report()
366                if purge then
367                    report("removing obsolete files:")
368                else
369                    report("obsolete files:")
370                end
371                report()
372                done = true
373            end
374            report("    %s",name)
375            if purge then
376                removefile(name)
377            end
378        end
379    end
380
381    if done then
382        report()
383    end
384
385    return done
386
387end
388
389
390local zippers = {
391    {
392        name         = "zip",
393        binary       = "zip",
394        uncompressed = "zip %s -X -0 %s",
395        compressed   = "zip %s -X -9 -r %s",
396    },
397    {
398        name         = "7z (7zip)",
399        binary       = "7z",
400        uncompressed = "7z a -tzip -mx0 %s %s",
401        compressed   = "7z a -tzip %s %s",
402    },
403}
404
405function scripts.epub.make(purge,rename,svgmath,svgstyle)
406
407    -- one can enter a jobname or jobname-export but the simple jobname is
408    -- preferred
409
410    local filename = environment.files[1]
411
412    if not filename or filename == "" or type(filename) ~= "string" then
413        report("provide filename")
414        return
415    end
416
417    local specpath, specname, specfull
418
419    if isdir(filename) then
420        specpath = filename
421        specname = addsuffix(specpath,"lua")
422        specfull = joinfile(specpath,specname)
423    end
424
425    if not specfull or not isfile(specfull) then
426        specpath = filename .. "-export"
427        specname = addsuffix(filename .. "-pub","lua")
428        specfull = joinfile(specpath,specname)
429    end
430
431    if not specfull or not isfile(specfull) then
432        report("unknown specificaton file %a for %a",specfull or "?",filename)
433        return
434    end
435
436    local specification = dofile(specfull)
437
438    if not specification or not next(specification) then
439        report("invalid specificaton file %a",specfile)
440        return
441    end
442
443    report("using specification file %a",specfull)
444
445    -- images: { ... url = location ... }
446
447    local defaultcoverpage = "cover.xhtml"
448
449    local name       = specification.name       or nameonly(filename)
450    local identifier = specification.identifier or ""
451    local htmlfiles  = specification.htmlfiles  or { }
452    local styles     = specification.styles     or { }
453    local images     = specification.images     or { }
454    local htmlroot   = specification.htmlroot   or htmlfiles[1] or ""
455    local language   = specification.language   or "en"
456    local creator    = specification.creator    or "context mkiv"
457    local author     = specification.author     or "anonymous"
458    local title      = specification.title      or name
459    local subtitle   = specification.subtitle   or ""
460    local imagefile  = specification.imagefile  or ""
461    local imagepath  = specification.imagepath  or "images"
462    local stylepath  = specification.stylepath  or "styles"
463    local coverpage  = specification.firstpage  or defaultcoverpage
464
465    if type(svgstyle) == "string" and not svgstyle then
466        svgstyle = specification.svgstyle or ""
467    end
468
469    local obsolete   = false
470
471    if #htmlfiles == 0 then
472        report("no html files specified")
473        return
474    end
475    if htmlroot == "" then
476        report("no html root file specified")
477        return
478    end
479
480    if subtitle ~= "" then
481        title = format("%s, %s",title,subtitle)
482    end
483
484    local htmlsource  = specpath
485    local imagesource = joinfile(specpath,imagepath)
486    local stylesource = joinfile(specpath,stylepath)
487
488    -- once we're here we can start moving files to the right spot; first we deal
489    -- with images
490
491    -- ["image-1"]={
492    --     height = "7.056cm",
493    --     name   = "file:///t:/sources/cow.svg",
494    --     page   = "1",
495    --     width  = "9.701cm",
496    -- }
497
498    -- end of todo
499
500    local pdftosvg   = os.which("mutool") and formatters[ [[mutool draw -o "%s" "%s" %s]] ]
501
502    local f_svgpage  = formatters["%s-page-%s.svg"]
503    local f_svgname  = formatters["%s.svg"]
504
505    local notupdated = 0
506    local updated    = 0
507    local skipped    = 0
508    local oldfiles   = dir.glob(file.join(imagesource,"*"))
509    local newfiles   = { }
510
511    if not pdftosvg then
512        report("the %a binary is not present","mutool")
513    end
514
515    -- a coverpage file has to be in the root of the export tree
516
517    if not coverpage then
518        report("no cover page (image) defined")
519    elseif suffix(coverpage) ~= "xhtml" then
520        report("using cover page %a",coverpage)
521        local source = coverpage
522        local target = joinfile(htmlsource,coverpage)
523        htmlfiles[#htmlfiles+1 ] = coverpage
524        report("copying coverpage %a to %a",source,target)
525        copyfile(source,target)
526    elseif isfile(coverpage) then
527        report("using cover page image %a",coverpage)
528        images.cover = {
529            height = "100%",
530            width  = "100%",
531            page   = "1",
532            name   = url.filename(coverpage),
533            used   = coverpage,
534        }
535        local data = replacetemplate(t_coverxhtml, {
536            content = replacetemplate(t_coverimg, {
537                image = coverpage,
538            })
539        })
540        coverpage = defaultcoverpage
541        local target = joinfile(htmlsource,coverpage)
542        report("saving coverpage to %a",target)
543        io.savedata(target,data)
544        htmlfiles[#htmlfiles+1 ] = coverpage
545    else
546        report("cover page image %a is not present",coverpage)
547        coverpage = false
548    end
549
550    if not coverpage then
551        local data = replacetemplate(t_coverxhtml, {
552            content = "no cover page"
553        })
554        coverpage = defaultcoverpage
555        local target = joinfile(htmlsource,coverpage)
556        report("saving dummy coverpage to %a",target)
557        io.savedata(target,data)
558        htmlfiles[#htmlfiles+1 ] = coverpage
559    end
560
561    for id, data in sortedhash(images) do
562        local name = url.filename(data.name)
563        local used = url.filename(data.used)
564        local base = basename(used)
565        local page = tonumber(data.page) or 1
566        -- todo : check timestamp and prefix, rename to image-*
567        if suffix(used) == "pdf" then
568            -- todo: pass svg name
569            if page > 1 then
570                name = f_svgpage(nameonly(name),page)
571            else
572                name = f_svgname(nameonly(name))
573            end
574            local source  = used
575            local target  = joinfile(imagesource,name)
576            if needsupdating(source,target) then
577                if pdftosvg then
578                    local command = pdftosvg(target,source,page)
579                    report("running command %a",command)
580                    os.execute(command)
581                    updated = updated + 1
582                else
583                    skipped = skipped + 1
584                end
585            else
586                notupdated = notupdated + 1
587            end
588            newfiles[#newfiles+1] = target
589        else
590            name = basename(used)
591            local source = used
592            local target = joinfile(imagesource,name)
593            if needsupdating(source,target) then
594                report("copying %a to %a",source,target)
595                copyfile(source,target)
596                updated = updated + 1
597            else
598                notupdated = notupdated + 1
599                -- no message
600            end
601            newfiles[#newfiles+1] = target
602        end
603        local target = newfiles[#newfiles]
604        if suffix(target) == "svg" and isfile(target) then
605            local data = io.loaddata(target)
606            if data then
607                local done = gsub(data,"<!(DOCTYPE.-)>","<!-- %1 -->",1)
608                if data ~= done then
609                    report("doctype fixed in %a",target)
610                    io.savedata(target,data)
611                end
612            end
613        end
614        data.newname = name -- without path
615    end
616
617    report("%s images checked, %s updated, %s kept, %s skipped",updated + notupdated + skipped,updated,notupdated,skipped)
618
619    if reportobsolete(oldfiles,newfiles,purge) then
620        obsolete = true
621    end
622
623    -- here we can decide not to make an epub
624
625    local uuid          = format("urn:uuid:%s",os.uuid(true)) -- os.uuid()
626    local identifier    = "bookid" -- for now
627
628    local epubname      = removesuffix(name)
629    local epubpath      = name .. "-epub"
630    local epubfile      = replacesuffix(name,"epub")
631    local epubroot      = replacesuffix(name,"opf")
632    local epubtoc       = "toc.ncx"
633    local epubmimetypes = "mimetype"
634    local epubcontainer = "container.xml"
635    local epubnavigator = "nav.xhtml"
636
637    local metapath      = "META-INF"
638    local datapath      = "OEBPS"
639
640    local oldfiles      = dir.glob(file.join(epubpath,"**/*"))
641    local newfiles      = { }
642
643    report("creating paths in tree %a",epubpath)
644
645    if not isdir(epubpath) then
646        mkdir(epubpath)
647    end
648    if not isdir(epubpath) then
649        report("unable to create path %a",epubpath)
650        return
651    end
652
653    local metatarget  = joinfile(epubpath,metapath)
654    local htmltarget  = joinfile(epubpath,datapath)
655    local styletarget = joinfile(epubpath,datapath,stylepath)
656    local imagetarget = joinfile(epubpath,datapath,imagepath)
657
658    mkdir(metatarget)
659    mkdir(htmltarget)
660    mkdir(styletarget)
661    mkdir(imagetarget)
662
663    local used       = { }
664    local notupdated = 0
665    local updated    = 0
666
667    local oldimagespecification = joinfile(htmlsource,imagefile)
668    local newimagespecification = joinfile(htmltarget,imagefile)
669
670    report("removing %a",newimagespecification)
671 -- removefile(newimagespecification) -- because we update that one
672
673    local function registerone(path,filename,mathml)
674        local suffix = suffix(filename)
675        local mime = mimetypes[suffix]
676        if mime then
677            local idmaker  = idmakers[suffix] or idmakers.default
678            local fullname = path and joinfile(path,filename) or filename
679            if mathml then
680                used[#used+1] = replacetemplate(t_prop, {
681                    id         = idmaker(filename),
682                    filename   = fullname,
683                    mime       = mime,
684                    properties = "mathml",
685                } )
686            else
687                used[#used+1] = replacetemplate(t_item, {
688                    id       = idmaker(filename),
689                    filename = fullname,
690                    mime     = mime,
691                } )
692            end
693            return true
694        end
695    end
696
697    local function registerandcopyfile(check,path,name,sourcepath,targetpath,newname,image)
698
699        if name == "" then
700            report("ignoring unknown image")
701            return
702        end
703
704        if newname then
705            newname = replacesuffix(newname,suffix(name))
706        else
707            newname = name
708        end
709
710        local source = joinfile(sourcepath,name)
711        local target = joinfile(targetpath,newname)
712        local mathml = false
713
714        if suffix(source) == "xhtml" then
715            if find(io.loaddata(source),"MathML") then
716                mathml = true -- inbelievable: the property is only valid when there is mathml
717            end
718        else
719            report("checking image %a -> %a",source,target)
720        end
721        if registerone(path,newname,mathml) then
722            if not check or needsupdating(source,target) or mathml and svgmath then
723                report("copying %a to %a",source,target)
724                copyfile(source,target)
725                updated = updated + 1
726            else
727                notupdated = notupdated + 1
728            end
729            newfiles[#newfiles+1] = target
730            if mathml and svgmath then
731                report()
732                report("converting mathml into svg in %a",target)
733                report()
734                local status, total, unique = moduledata.svgmath.convert(target,svgstyle)
735                report()
736                if status then
737                    report("%s formulas converted, %s are unique",total,unique)
738                else
739                    report("warning: %a in %a",total,target)
740                end
741                report()
742            end
743        end
744    end
745
746 -- local nofdummies = 0
747 -- local dummyname  = formatters["dummy-figure-%03i"]
748 -- local makedummy  = formatters["context --extra=dummies --noconsole --once --result=%s"]
749 --
750 -- local function registerandcopydummy(targetpath,name)
751 --     nofdummies = nofdummies + 1
752 --     local newname = dummyname(nofdummies)
753 --     local target  = joinfile(targetpath,newname)
754 --     if not isfile(target) then
755 --         pushdir(targetpath)
756 --         report("generating dummy %a for %a",newname,name or "unknown")
757 --         os.execute(makedummy(newname))
758 --         popdir()
759 --     end
760 --     return newname
761 -- end
762
763    for image, data in sortedhash(images) do
764     -- if data.used == "" then
765     --     data.newname = registerandcopydummy(imagetarget,data.name)
766     -- end
767        registerandcopyfile(true,imagepath,data.newname,imagesource,imagetarget,rename and image,true)
768    end
769    for i=1,#styles do
770        registerandcopyfile(false,stylepath,styles[i],stylesource,styletarget)
771    end
772    for i=1,#htmlfiles do
773        registerandcopyfile(false,false,htmlfiles[i],htmlsource,htmltarget)
774    end
775
776    relocateimages(images,oldimagespecification,oldimagespecification,imagepath,rename)
777    relocateimages(images,oldimagespecification,newimagespecification,imagepath,rename)
778
779    report("%s files registered, %s updated, %s kept",updated + notupdated,updated,notupdated)
780
781    local function saveinfile(what,name,data)
782        report("saving %s in %a",what,name)
783        io.savedata(name,data)
784        newfiles[#newfiles+1] = name
785    end
786
787    used[#used+1] = replacetemplate(t_prop, {
788        id         = "nav",
789        filename   = epubnavigator,
790        properties = "nav",
791        mime       = "application/xhtml+xml",
792    })
793
794    registerone(false,epubtoc)
795
796    saveinfile("navigation data",joinfile(htmltarget,epubnavigator),replacetemplate(t_navtoc, { -- version 3.0
797        root = htmlroot,
798    } ) )
799
800    saveinfile("used mimetypes",joinfile(epubpath,epubmimetypes),mimetype)
801
802    saveinfile("version 2.0 container",joinfile(metatarget,epubcontainer),replacetemplate(t_container, {
803        rootfile = epubroot
804    } ) )
805
806    local idmaker = idmakers[suffix(htmlroot)] or idmakers.default
807
808    saveinfile("package specification",joinfile(htmltarget,epubroot),replacetemplate(t_package, {
809        identifier = identifier,
810        title      = title,
811        language   = language,
812        uuid       = uuid,
813        creator    = creator,
814        date       = os.date("!%Y-%m-%dT%H:%M:%SZ"),
815        coverpage  = idmaker(coverpage),
816        manifest   = concat(used,"\n"),
817        rootfile   = idmaker(htmlroot)
818    } ) )
819
820    -- t_toc is replaced by t_navtoc in >= 3
821
822    saveinfile("table of contents",joinfile(htmltarget,epubtoc), replacetemplate(t_toc, {
823        identifier = uuid, -- identifier,
824        title      = title,
825        author     = author,
826        root       = htmlroot,
827    } ) )
828
829    report("creating archive\n\n")
830
831    pushdir(epubpath)
832
833    removefile(epubfile)
834
835    local usedzipper = false
836
837    local function zipped(zipper)
838        local ok = os.execute(format(zipper.uncompressed,epubfile,epubmimetypes))
839        if ok == 0 then
840            os.execute(format(zipper.compressed,epubfile,metapath))
841            os.execute(format(zipper.compressed,epubfile,datapath))
842            usedzipper = zipper.name
843            return true
844        end
845    end
846
847    -- nice way
848
849    for i=1,#zippers do
850        if os.which(zippers[i].binary) and zipped(zippers[i]) then
851            break
852        end
853    end
854
855    -- trial and error
856
857    if not usedzipper then
858        for i=1,#zippers do
859            if zipped(zippers[i]) then
860                break
861            end
862        end
863    end
864
865    popdir()
866
867    if usedzipper then
868        local treefile = joinfile(epubpath,epubfile)
869        removefile(epubfile)
870        copyfile(treefile,epubfile)
871        if isfile(epubfile) then
872            removefile(treefile)
873        end
874        report("epub archive made using %s: %s",usedzipper,epubfile)
875    else
876        local list = { }
877        for i=1,#zippers do
878            list[#list+1] = zippers[i].name
879        end
880        report("no epub archive made, install one of: % | t",list)
881    end
882
883    if reportobsolete(oldfiles,newfiles,purge) then
884        obsolete = true
885    end
886
887    if obsolete and not purge then
888        report("use --purge to remove obsolete files")
889    end
890
891end
892
893--
894
895local a_exporthelp = environment.argument("exporthelp")
896local a_make       = environment.argument("make")
897local a_all        = environment.argument("all")
898local a_purge      = a_all or environment.argument("purge")
899local a_rename     = a_all or environment.argument("rename")
900local a_svgmath    = a_all or environment.argument("svgmath")
901local a_svgstyle   = environment.argument("svgstyle")
902
903if a_make and a_svgmath then
904    require("x-math-svg")
905end
906
907if a_make then
908    scripts.epub.make(a_purge,a_rename,a_svgmath,a_svgstyle)
909elseif a_exporthelp then
910    application.export(a_exporthelp,environment.files[1])
911else
912    application.help()
913end
914
915-- java -jar d:\epubcheck\epubcheck-3.0.1.jar -v 3.0 -mode xhtml mkiv-publications.tree\mkiv-publications.epub
916