├── .gitignore ├── .travis.yml ├── tex4ebook-doc.tex ├── tex4ebook-exec_azw.lua ├── tex4ebook-exec_azw3.lua ├── INSTALL.md ├── backup ├── make4ht.lua └── ebookutils.lua ├── tex4ebook-exec_mobi.lua ├── tex4ebook-tidyconf.conf ├── Makefile ├── tex4ebook.sty ├── tex4ebook ├── tex4ebook-epub3.4ht ├── tex4ebook-exec_epub3.lua ├── CHANGELOG.md ├── README.md ├── tex4ebook.4ht └── tex4ebook-exec_epub.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *.aux 2 | *.log 3 | *.lg 4 | *.xref 5 | *.tmp 6 | *.toc 7 | *.css 8 | *.out 9 | *.4ct 10 | *.4tc 11 | *.swp 12 | *.dvi 13 | *.idv 14 | *.epub 15 | *.html 16 | *.opf 17 | *.ncx 18 | *.fls 19 | *.pdf 20 | *.fdb_latexmk 21 | readme.tex 22 | changelog.tex 23 | tags 24 | tex4ebook-doc-epub3/ 25 | tex4ebook-doc-epub/ 26 | make4ht-eb.lua 27 | build/ 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | 3 | install: 4 | - sudo apt-get install -qq luatex texlive-base texlive-luatex pandoc latexmk texlive texlive-xetex tex4ht texlive-latex-extra texlive-fonts-extra texlive-fonts-recommended fonts-lmodern tex-gyre fonts-texgyre 5 | # make4ht 6 | - git clone https://github.com/michal-h21/make4ht 7 | - cd make4ht 8 | - sudo make justinstall 9 | - cd .. 10 | 11 | 12 | script: 13 | # From https://github.com/michal-h21/tex4ebook/issues/37#issuecomment-354447097 14 | - git fetch --tags 15 | # Install globally 16 | - make 17 | - sudo make install 18 | # Run 19 | - tex4ebook tex4ebook-doc.tex 20 | -------------------------------------------------------------------------------- /tex4ebook-doc.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | 3 | 4 | \usepackage[english]{babel} 5 | \usepackage{hyperref} 6 | \ifdefined\HCode 7 | \usepackage[T1]{fontenc} 8 | \usepackage[utf8]{inputenc} 9 | \author{\href{mailto:michal.h21@gmail.com}{Michal Hoftich}} 10 | \else 11 | \usepackage{luatexbase} 12 | \usepackage{fontspec} 13 | \setmainfont{TeX Gyre Schola} 14 | \author{Michal Hoftich\footnote{\url{michal.h21@gmail.com}}} 15 | \fi 16 | \usepackage{microtype} 17 | \providecommand\tightlist{\relax} 18 | \ifdefined\gitdate\else\def\gitdate{Date undefined}\fi 19 | \ifdefined\version\else\def\version{Version undefined}\fi 20 | 21 | \title{The \texttt{tex4ebook} package} 22 | % \author{Michal Hoftich\thanks{\url{michal.h21@gmail.com}}} 23 | \date{Version \version\\\gitdate} 24 | \begin{document} 25 | \maketitle 26 | \tableofcontents 27 | \input{readme} 28 | 29 | \input{changelog} 30 | \end{document} 31 | -------------------------------------------------------------------------------- /tex4ebook-exec_azw.lua: -------------------------------------------------------------------------------- 1 | module(...,package.seeall) 2 | local eb = require("tex4ebook-exec_epub") 3 | local mobi = require("tex4ebook-exec_mobi") 4 | local ebookutils = require("mkutils") 5 | local log = logging.new "exec_azw" 6 | 7 | function prepare(params) 8 | return eb.prepare(params) 9 | end 10 | 11 | function run(out,params) 12 | return eb.run(out, params) 13 | end 14 | 15 | function writeContainer() 16 | local ret = eb.writeContainer() 17 | -- convert the epub file to azw 18 | local epubpath = eb.basedir .. "/" .. eb.outputfile 19 | 20 | -- find the azw filename 21 | local azwfile = eb.outputfile:gsub("epub$", "azw") 22 | local azwdist = eb.destdir .. azwfile 23 | local status = mobi.kindlegen(epubpath, azwfile) 24 | if status then 25 | -- copy the azw file to the destination directory 26 | -- the destination directory will be created by the epub writer, so it is possible to use 27 | -- the cp function which doesn't try to create directory 28 | if azwfile ~= azwdist then 29 | ebookutils.cp(azwfile, azwdist) 30 | end 31 | end 32 | 33 | return ret 34 | end 35 | 36 | function clean() 37 | return eb.clean() 38 | end 39 | -------------------------------------------------------------------------------- /tex4ebook-exec_azw3.lua: -------------------------------------------------------------------------------- 1 | module(...,package.seeall) 2 | local eb = require("tex4ebook-exec_epub") 3 | local mobi = require("tex4ebook-exec_mobi") 4 | local ebookutils = require("mkutils") 5 | local log = logging.new "exec_azw" 6 | 7 | function prepare(params) 8 | return eb.prepare(params) 9 | end 10 | 11 | function run(out,params) 12 | return eb.run(out, params) 13 | end 14 | 15 | function writeContainer() 16 | local ret = eb.writeContainer() 17 | -- convert the epub file to azw 18 | local epubpath = eb.basedir .. "/" .. eb.outputfile 19 | 20 | -- find the azw filename 21 | local azwfile = eb.outputfile:gsub("epub$", "azw3") 22 | local azwdist = eb.destdir .. azwfile 23 | -- local command = "kindlegen " .. epubpath .. " -o " .. azwfile 24 | -- log:info("Pack azw ".. command) 25 | -- local status, output = ebookutils.execute(command) 26 | local status = mobi.kindlegen(epubpath, azwfile) 27 | if status then 28 | -- copy the azw file to the destination directory 29 | -- the destination directory will be created by the epub writer, so it is possible to use 30 | -- the cp function which doesn't try to create directory 31 | if azwfile ~= azwdist then 32 | ebookutils.cp(azwfile, azwdist) 33 | end 34 | end 35 | 36 | return ret 37 | end 38 | 39 | function clean() 40 | return eb.clean() 41 | end 42 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | Installation 2 | ------------ 3 | 4 | The stable version of `tex4ebook` is distributed by TeX distributions, it is included in both TeX Live and Miktex. 5 | A working \TeX\ distribution that includes [\TeX4ht](https://tug.org/tex4ht/) is required to run 6 | `tex4ebook`, as it depends on \LaTeX\ and various programs and packages provided by \TeX\ distributions. 7 | The development version may be installed using the following instructions. 8 | 9 | > This package depends on 10 | > [`make4ht`](https://github.com/michal-h21/make4ht#instalation) now, please 11 | > install it first. 12 | > 13 | > It also depends on `tidy` and `zip` commands, both are available for Unix 14 | > and Windows platforms, `zip` is distributed with TeX Live on Windows. 15 | > You need [Pandoc](http://pandoc.org/) in order to make documentation. 16 | 17 | On Unix systems, clone this repository to your disc and run command 18 | 19 | make 20 | make install 21 | 22 | `tex4ebook` is installed to `/usr/local/bin` directory by default. The 23 | directory can be changed by passing it's location to the `BIN_DIR` variable: 24 | 25 | make install BIN_DIR=~/.local/bin/ 26 | 27 | For Windows settings, see a 28 | [guide](https://d800fotos.wordpress.com/2015/01/19/create-e-books-from-latex-tex-files-ebook-aus-latex-tex-dateien-erstellen/) by Volker Gottwald. 29 | 30 | If you want to produce ebooks for Amazon Kindle (MOBI, AZW and AZW3), you need 31 | to install Kindlegen or Calibre. 32 | 33 | 34 | -------------------------------------------------------------------------------- /backup/make4ht.lua: -------------------------------------------------------------------------------- 1 | -- Simple make system for tex4ht 2 | --kpse.set_program_name("luatex") 3 | module("make4ht",package.seeall) 4 | 5 | Make = {} 6 | --Make.params = {} 7 | Make.build_seq = {} 8 | Make.add = function(self,name,fn,par) 9 | local par = par or {} 10 | self.params = self.params or {} 11 | Make[name] = function(self,p,typ) 12 | local params = {} 13 | for k,v in pairs(self.params) do params[k] = v end 14 | for k,v in pairs(par) do params[k] = v; print("setting param "..k) end 15 | local typ = typ or "make" 16 | local p = p or {} 17 | local fn = fn 18 | for k,v in pairs(p) do 19 | params[k]=v 20 | end 21 | -- print( fn % params) 22 | local command = { 23 | name=name, 24 | type=typ, 25 | command = fn, 26 | params = params 27 | } 28 | table.insert(self.build_seq,command) 29 | end 30 | end 31 | 32 | Make.length = function(self) 33 | return #self.build_seq 34 | end 35 | Make.run = function(self) 36 | local return_codes = {} 37 | for _,v in ipairs(self.build_seq) do 38 | --print("sekvence: "..v.name) 39 | local params = self.params or {} 40 | for p,n in pairs(v.params) do params[p] = n end 41 | --for c,_ in pairs(params) do print("build param: "..c) end 42 | local command = v.command % params 43 | print("Make4ht: " .. command) 44 | local status = os.execute(command) 45 | table.insert(return_codes,{name=v.name,status=status}) 46 | end 47 | return return_codes 48 | end 49 | 50 | --[[Make:add("hello", "hello ${world}", {world = "world"}) 51 | Make:add("ajaj", "ajaj") 52 | Make:hello() 53 | Make:hello{world="světe"} 54 | Make:hello() 55 | Make:run() 56 | --]] 57 | -------------------------------------------------------------------------------- /tex4ebook-exec_mobi.lua: -------------------------------------------------------------------------------- 1 | module(...,package.seeall) 2 | local eb = require("tex4ebook-exec_epub") 3 | local ebookutils = require("mkutils") 4 | local log = logging.new "exec_mobi" 5 | 6 | function prepare(params) 7 | return eb.prepare(params) 8 | end 9 | 10 | function run(out,params) 11 | return eb.run(out, params) 12 | end 13 | 14 | function kindlegen(source, outputfile) 15 | -- try to run kindlegen first 16 | local command = "kindlegen " .. source .. " -o " .. outputfile 17 | local status, output = ebookutils.execute(command) 18 | log:debug("running kindlegen: " .. command, status) 19 | -- if we cannot find kindlegen, try ebook-convert 20 | if not output:match("Amazon") then 21 | log:debug("kindlegen failed, trying epub-convert") 22 | local ebookcmd = "ebook-convert " .. source .. " " .. outputfile 23 | status, output = ebookutils.execute(ebookcmd) 24 | if status > 0 then 25 | log:error("Conversion to the output format failed") 26 | log:error("Do you have either kindlegen or ebook-convert installed?") 27 | return false 28 | end 29 | end 30 | return true 31 | end 32 | 33 | function writeContainer() 34 | local ret = eb.writeContainer() 35 | -- convert the epub file to mobi 36 | local epubpath = eb.basedir .. "/" .. eb.outputfile 37 | -- find the mobi filename 38 | local mobifile = eb.outputfile:gsub("epub$", "mobi") 39 | local mobidist = eb.destdir .. eb.outputfile:gsub("epub$", "mobi") 40 | log:info("Convert Epub to mobi") 41 | local status = kindlegen(epubpath, mobifile) 42 | if status then 43 | -- copy the mobi file to the destination directory 44 | -- the destination directory will be created by the epub writer, so it is possible to use 45 | -- the cp function which doesn't try to create directory 46 | if mobifile ~= mobidist then 47 | ebookutils.cp(mobifile, mobidist) 48 | end 49 | end 50 | return ret 51 | end 52 | 53 | function clean() 54 | return eb.clean() 55 | end 56 | 57 | -------------------------------------------------------------------------------- /tex4ebook-tidyconf.conf: -------------------------------------------------------------------------------- 1 | show-warnings: no 2 | numeric-entities:yes 3 | new-inline-tags:span,a,math,mi, mo, mn ,abs ,and ,annotation ,annotation-xml ,apply ,approx ,arccos ,arccosh ,arccot ,arccoth ,arccsc ,arccsch ,arcsec ,arcsech ,arcsin ,arcsinh ,arctan ,arctanh ,arg ,bind ,bvar ,card ,cartesianproduct ,cbytes ,ceiling ,cerror ,ci ,cn ,codomain ,complexes ,compose ,condition ,conjugate ,cos ,cosh ,cot ,coth ,cs ,csc ,csch ,csymbol ,curl ,declare ,degree ,determinant ,diff ,divergence ,divide ,domain ,domainofapplication ,el ,emptyset ,eq ,equivalent ,eulergamma ,exists ,exp ,exponentiale ,factorial ,factorof ,false ,floor ,fn ,forall ,gcd ,geq ,grad ,gt ,ident ,image ,imaginary ,imaginaryi ,implies ,in ,infinity ,int ,integers ,intersect ,interval ,inverse ,lambda ,laplacian ,lcm ,leq ,limit ,list ,ln ,log ,logbase ,lowlimit ,lt ,maction ,malign ,maligngroup ,malignmark ,malignscope ,math ,matrix ,matrixrow ,max ,mean ,median ,menclose ,merror ,mfenced ,mfrac ,mfraction ,mglyph ,mi ,min ,minus ,mlabeledtr ,mlongdiv ,mmultiscripts ,mn ,mo ,mode ,moment ,momentabout ,mover ,mpadded ,mphantom ,mprescripts ,mroot ,mrow ,ms ,mscarries ,mscarry ,msgroup ,msline ,mspace ,msqrt ,msrow ,mstack ,mstyle ,msub ,msubsup ,msup ,mtable ,mtd ,mtext ,mtr ,munder ,munderover ,naturalnumbers ,neq ,none ,not ,notanumber ,note ,notin ,notprsubset ,notsubset ,or ,otherwise ,outerproduct ,partialdiff ,pi ,piece ,piecewise ,plus ,power ,primes ,product ,prsubset ,quotient ,rationals ,real ,reals ,reln ,rem ,root ,scalarproduct ,sdev ,sec ,sech ,selector ,semantics ,sep ,set ,setdiff ,share ,sin ,sinh ,subset ,sum ,tan ,tanh ,tendsto ,times ,transpose ,true ,union ,uplimit ,variance ,vector ,vectorproduct ,xor ,bdi ,command ,details ,dialog ,summary ,figure ,figcaption ,footer ,header ,mark ,meter ,progress ,ruby ,rt ,rp ,time ,wbr ,altGlyph ,altGlyphDef ,altGlyphItem ,animate ,animateColor ,animateMotion ,animateTransform ,circle ,clipPath ,color-profile ,cursor ,defs ,desc ,ellipse ,feBlend ,feColorMatrix ,feComponentTransfer ,feComposite ,feConvolveMatrix ,feDiffuseLighting ,feDisplacementMap ,feDistantLight ,feFlood ,feFuncA ,feFuncB ,feFuncG ,feFuncR ,feGaussianBlur ,feImage ,feMerge ,feMergeNode ,feMorphology ,feOffset ,fePointLight ,feSpecularLighting ,feSpotLight ,feTile ,feTurbulence ,filter ,font ,font-face ,font-face-format ,font-face-name ,font-face-src ,font-face-uri ,foreignObject ,g ,glyph ,glyphRef ,hkern ,image ,line ,linearGradient ,marker ,mask ,metadata ,missing-glyph ,mpath ,path ,pattern ,polygon ,polyline ,radialGradient ,rect ,script ,set ,stop ,style ,svg ,switch ,symbol ,text ,textPath ,title ,tref ,tspan ,use 4 | new-blocklevel-tags: aside,section,article,nav 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | tex_content = tex4ebook $(wildcard *.sty) $(wildcard *.4ht) $(wildcard *.tex) tex4ebook-tidyconf.conf 2 | lua_content = $(wildcard *.lua) 3 | doc_file = tex4ebook-doc.pdf 4 | TEXMFHOME = $(shell kpsewhich -var-value=TEXMFHOME) 5 | INSTALL_DIR = $(TEXMFHOME)/tex/latex/tex4ebook 6 | LUA_DIR = $(TEXMFHOME)/scripts/lua/tex4ebook 7 | MANUAL_DIR = $(TEXMFHOME)/doc/latex/tex4ebook 8 | BIN_DIR = /usr/local/bin 9 | # expand the bin directory 10 | SYSTEM_DIR = $(realpath $(BIN_DIR)) 11 | EXECUTABLE = $(SYSTEM_DIR)/tex4ebook 12 | BUILD_DIR = build 13 | BUILD_TEX4EBOOK = $(BUILD_DIR)/tex4ebook/ 14 | VERSION:= undefined 15 | DATE:= undefined 16 | ifeq ($(strip $(shell git rev-parse --is-inside-work-tree 2>/dev/null)),true) 17 | VERSION:= $(shell git --no-pager describe --abbrev=0 --tags --always ) 18 | DATE:= $(firstword $(shell git --no-pager show --date=short --format="%ad" --name-only)) 19 | endif 20 | 21 | # use sudo for install to a destination directory outside of $HOME 22 | ifeq ($(findstring home,$(SYSTEM_DIR)),home) 23 | SUDO:= 24 | else 25 | SUDO:=sudo 26 | endif 27 | 28 | ifeq ("$(wildcard $(EXECUTABLE))","") 29 | INSTALL_COMMAND:=$(SUDO) ln -s $(INSTALL_DIR)/tex4ebook $(EXECUTABLE) 30 | else 31 | INSTALL_COMMAND:= 32 | endif 33 | 34 | 35 | all: doc 36 | 37 | .PHONY: tags 38 | 39 | tags: 40 | ifeq ($(strip $(shell git rev-parse --is-inside-work-tree 2>/dev/null)),true) 41 | git fetch --tags 42 | endif 43 | 44 | doc: $(doc_file) readme.tex 45 | 46 | 47 | tex4ebook-doc.pdf: tex4ebook-doc.tex readme.tex changelog.tex tags 48 | latexmk -pdf -pdflatex='lualatex "\def\version{${VERSION}}\def\gitdate{${DATE}}\input{%S}"' tex4ebook-doc.tex 49 | 50 | readme.tex: README.md 51 | pandoc -f markdown+definition_lists+inline_notes+autolink_bare_uris -t LaTeX README.md > readme.tex 52 | 53 | changelog.tex: CHANGELOG.md 54 | pandoc -f markdown+definition_lists -t LaTeX CHANGELOG.md > changelog.tex 55 | 56 | build: doc $(tex_content) $(lua_content) 57 | @rm -rf build 58 | @mkdir -p $(BUILD_TEX4EBOOK) 59 | @cp $(tex_content) $(lua_content) tex4ebook-doc.pdf $(BUILD_TEX4EBOOK) 60 | @cat tex4ebook | sed -e "s/{{version}}/${VERSION}/" > $(BUILD_TEX4EBOOK)tex4ebook 61 | @cat tex4ebook.sty | sed -e "s/{{version}}/${VERSION}/" | sed -e "s/{{date}}/${DATE}/" > $(BUILD_TEX4EBOOK)tex4ebook.sty 62 | @cp README.md $(BUILD_TEX4EBOOK)README 63 | cd $(BUILD_DIR) && zip -r tex4ebook.zip tex4ebook 64 | 65 | install: doc $(tex_content) $(lua_content) 66 | mkdir -p $(INSTALL_DIR) 67 | mkdir -p $(MANUAL_DIR) 68 | mkdir -p $(LUA_DIR) 69 | cp $(tex_content) $(INSTALL_DIR) 70 | cp $(lua_content) $(LUA_DIR) 71 | cp $(doc_file) $(MANUAL_DIR) 72 | chmod +x $(INSTALL_DIR)/tex4ebook 73 | $(INSTALL_COMMAND) 74 | 75 | -------------------------------------------------------------------------------- /tex4ebook.sty: -------------------------------------------------------------------------------- 1 | % Package tex4ebook. Author Michal Hoftich 2 | % This package is subject of LPPL license, version 1.3 3 | \ProvidesPackage{tex4ebook}[{{date}} version {{version}}] 4 | \RequirePackage{etoolbox} 5 | \RequirePackage{kvoptions} 6 | \RequirePackage{graphicx} 7 | % Command for generating NCX file. The hard work is done in the file 8 | % tex4ebook.4ht 9 | \def\ncxtable{} 10 | % Command for generating OPF file 11 | \def\opftable{} 12 | 13 | %Declare RFC3066 language code for babel language name 14 | \def\DeclareLanguageEbook#1#2{% 15 | \csgdef{rfclang#1}{#2}% 16 | } 17 | \def\GetLanguage{% 18 | \ifx\bbl@main@language\@undefined en-US% 19 | \else% 20 | \ifcsdef{rfclang\bbl@main@language}{\csuse{rfclang\bbl@main@language}}{}% 21 | \fi% 22 | } 23 | 24 | \DeclareLanguageEbook{UKenglish}{en-GB} 25 | \DeclareLanguageEbook{USenglish}{en-US} 26 | \DeclareLanguageEbook{acadian}{fr} 27 | \DeclareLanguageEbook{albanian}{sq} 28 | \DeclareLanguageEbook{american}{en-US} 29 | \DeclareLanguageEbook{amharic}{am} 30 | \DeclareLanguageEbook{arabic}{ar} 31 | \DeclareLanguageEbook{armenian}{hy} 32 | \DeclareLanguageEbook{australian}{en-US} 33 | \DeclareLanguageEbook{austrian}{de} 34 | \DeclareLanguageEbook{basque}{eu} 35 | \DeclareLanguageEbook{bengali}{bn} 36 | \DeclareLanguageEbook{brazilian}{pt} 37 | \DeclareLanguageEbook{brazil}{pt} 38 | \DeclareLanguageEbook{breton}{br} 39 | \DeclareLanguageEbook{british}{en-GB} 40 | \DeclareLanguageEbook{bulgarian}{bg} 41 | \DeclareLanguageEbook{canadian}{en-US} 42 | \DeclareLanguageEbook{canadien}{fr} 43 | \DeclareLanguageEbook{catalan}{ca} 44 | \DeclareLanguageEbook{croatian}{hr} 45 | \DeclareLanguageEbook{czech}{cs-CZ} 46 | \DeclareLanguageEbook{danish}{da} 47 | \DeclareLanguageEbook{divehi}{dv} 48 | \DeclareLanguageEbook{dutch}{nl} 49 | \DeclareLanguageEbook{english}{en} 50 | \DeclareLanguageEbook{esperanto}{eo} 51 | \DeclareLanguageEbook{estonian}{et} 52 | \DeclareLanguageEbook{finnish}{f\/i} 53 | \DeclareLanguageEbook{francais}{fr} 54 | \DeclareLanguageEbook{french}{fr} 55 | \DeclareLanguageEbook{galician}{gl} 56 | \DeclareLanguageEbook{germanb}{de} 57 | \DeclareLanguageEbook{german}{de} 58 | \DeclareLanguageEbook{greek}{el} 59 | \DeclareLanguageEbook{hebrew}{he} 60 | \DeclareLanguageEbook{hindi}{hi} 61 | \DeclareLanguageEbook{hungarian}{hu} 62 | \DeclareLanguageEbook{icelandic}{is} 63 | \DeclareLanguageEbook{interlingua}{ia} 64 | \DeclareLanguageEbook{irish}{ga} 65 | \DeclareLanguageEbook{italian}{it} 66 | \DeclareLanguageEbook{kannada}{kn} 67 | \DeclareLanguageEbook{khmer}{km} 68 | \DeclareLanguageEbook{korean}{ko} 69 | \DeclareLanguageEbook{lao}{lo} 70 | \DeclareLanguageEbook{latin}{la} 71 | \DeclareLanguageEbook{latvian}{lv} 72 | \DeclareLanguageEbook{lithuanian}{lt} 73 | \DeclareLanguageEbook{lowersorbian}{dsb} 74 | \DeclareLanguageEbook{magyar}{hu} 75 | \DeclareLanguageEbook{malayalam}{ml} 76 | \DeclareLanguageEbook{marathi}{mr} 77 | \DeclareLanguageEbook{naustrian}{de} 78 | \DeclareLanguageEbook{newzealand}{en} 79 | \DeclareLanguageEbook{ngerman}{de} 80 | \DeclareLanguageEbook{norsk}{no} 81 | \DeclareLanguageEbook{norwegiannynorsk}{nn} 82 | \DeclareLanguageEbook{nynorsk}{no} 83 | \DeclareLanguageEbook{occitan}{oc} 84 | \DeclareLanguageEbook{oldchurchslavonic}{cu} 85 | \DeclareLanguageEbook{persian}{fa} 86 | \DeclareLanguageEbook{polish}{pl-PL} 87 | \DeclareLanguageEbook{polutonikogreek}{el} 88 | \DeclareLanguageEbook{portuges}{pt} 89 | \DeclareLanguageEbook{portuguese}{pt} 90 | \DeclareLanguageEbook{romanian}{ro} 91 | \DeclareLanguageEbook{romansh}{rm} 92 | \DeclareLanguageEbook{russian}{ru} 93 | \DeclareLanguageEbook{samin}{se} 94 | \DeclareLanguageEbook{sanskrit}{sa} 95 | \DeclareLanguageEbook{scottish}{gd} 96 | \DeclareLanguageEbook{serbian}{sr} 97 | \DeclareLanguageEbook{serbo-croatian}{sh} 98 | \DeclareLanguageEbook{slovak}{sk} 99 | \DeclareLanguageEbook{slovene}{sl} 100 | \DeclareLanguageEbook{slovenian}{sl} 101 | \DeclareLanguageEbook{spanish}{es} 102 | \DeclareLanguageEbook{swedish}{sv} 103 | \DeclareLanguageEbook{tamil}{ta} 104 | \DeclareLanguageEbook{telugu}{te} 105 | \DeclareLanguageEbook{thai}{th} 106 | \DeclareLanguageEbook{tibetan}{bo} 107 | \DeclareLanguageEbook{turkish}{tr} 108 | \DeclareLanguageEbook{turkmen}{tk} 109 | \DeclareLanguageEbook{ukrainian}{uk} 110 | \DeclareLanguageEbook{uppersorbian}{hsb} 111 | \DeclareLanguageEbook{urdu}{ur} 112 | \DeclareLanguageEbook{vietnamese}{vi} 113 | \DeclareLanguageEbook{welsh}{cy} 114 | 115 | \AtEndDocument{% 116 | \ncxtable 117 | \opftable 118 | } 119 | 120 | % Default empty values 121 | \def\Title{Unnamed} 122 | \def\Author{Anonymous} 123 | 124 | % normal \title 125 | \newcommand\tf@title[1]{% 126 | \tf@orig@title{#1}% 127 | \let\Title\@title% 128 | } 129 | 130 | % \title with optional argument 131 | \newcommand\tf@opttitle[2][]{% 132 | \tf@orig@title[#1]{#2}% 133 | \let\Title\@title% 134 | } 135 | 136 | \newcommand\tf@author[1]{% 137 | \tf@orig@author{#1}% 138 | \gdef\Author{#1}% 139 | } 140 | 141 | \newcommand\tf@optauthor[2][]{% 142 | \tf@orig@author[#1]{#2}% 143 | \gdef\Author{#2}% 144 | } 145 | 146 | \AddToHook{class/after}{% 147 | % We need to save values of title, author etc. 148 | \let\tf@orig@title\title 149 | 150 | % some classes (amsart) define \title command with optional argument 151 | \renewcommand\title{% 152 | \@ifnextchar[\tf@opttitle\tf@title% 153 | } 154 | 155 | \let\tf@orig@author\author 156 | % support optiona argument for \author as well 157 | \renewcommand\author{% 158 | \@ifnextchar[\tf@optauthor\tf@author% 159 | } 160 | 161 | \let\Date\today 162 | \let\tf@orig@date\date 163 | \renewcommand\date[1]{% 164 | \tf@orig@date{#1}% 165 | \global\let\Date\@date% 166 | } 167 | 168 | % Fixes for AMS classess 169 | \ifx\authors\@empty% 170 | \def\@maketitle@hook{ 171 | \global\let\Author\authors 172 | } 173 | \fi% 174 | } 175 | 176 | \newcommand\coverimage[2][]{\includegraphics[#1]{#2}} 177 | 178 | \newcommand\epubpage{\clearpage} 179 | 180 | \endinput 181 | -------------------------------------------------------------------------------- /backup/ebookutils.lua: -------------------------------------------------------------------------------- 1 | module("ebookutils",package.seeall) 2 | 3 | --local make4ht = require("make4ht") 4 | local make4ht = require("make4ht-eb") 5 | --template engine 6 | function interp(s, tab) 7 | local tab = tab or {} 8 | return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end)) 9 | end 10 | --print( interp("${name} is ${value}", {name = "foo", value = "bar"}) ) 11 | 12 | function addProperty(s,prop) 13 | if prop ~=nil then 14 | return s .." "..prop 15 | else 16 | return s 17 | end 18 | end 19 | getmetatable("").__mod = interp 20 | getmetatable("").__add = addProperty 21 | 22 | --print( "${name} is ${value}" % {name = "foo", value = "bar"} ) 23 | -- Outputs "foo is bar" 24 | 25 | function string:split(sep) 26 | local sep, fields = sep or ":", {} 27 | local pattern = string.format("([^%s]+)", sep) 28 | self:gsub(pattern, function(c) fields[#fields+1] = c end) 29 | return fields 30 | end 31 | 32 | function remove_extension(path) 33 | local found, len, remainder = string.find(path, "^(.*)%.[^%.]*$") 34 | if found then 35 | return remainder 36 | else 37 | return path 38 | end 39 | end 40 | 41 | -- 42 | 43 | function file_exists(file) 44 | local f = io.open(file, "rb") 45 | if f then f:close() end 46 | return f ~= nil 47 | end 48 | 49 | -- searching for converted images 50 | function parse_lg(filename) 51 | print("Parse LG") 52 | local outputimages,outputfiles,status={},{},nil 53 | if not file_exists(filename) then 54 | print("Cannot read log file: "..filename) 55 | else 56 | local usedfiles={} 57 | local fonts = {} 58 | local used_fonts = {} 59 | for line in io.lines(filename) do 60 | line:gsub("==> ([%a%d%p%.%-%_]*)",function(k) table.insert(outputimages,k)end) 61 | line:gsub("File: (.*)", function(k) 62 | if not usedfiles[k] then 63 | table.insert(outputfiles,k) 64 | usedfiles[k] = true 65 | end 66 | end) 67 | line:gsub("htfcss: ([^%s]+)(.*)",function(k,r) 68 | local fields = {} 69 | r:gsub("[%s]*([^%:]+):[%s]*([^;]+);",function(c,v) 70 | fields[c] = v 71 | end) 72 | fonts[k] = fields 73 | end) 74 | 75 | line:gsub('Font("([^"]+)","([%d]+)","([%d]+)","([%d]+)"',function(n,s1,s2,s3) 76 | table.insert(used_fonts,{n,s1,s2,s3}) 77 | end) 78 | end 79 | status=true 80 | end 81 | return {files = outputfiles, images = outputimages, htfonts = fonts,fonts = used_fonts},status 82 | end 83 | 84 | function copy_filter(src,dest, filter) 85 | local src_f=io.open(src,"rb") 86 | local dst_f=io.open(dest,"w") 87 | local contents = src_f:read("*all") 88 | local filter = filter or function(s) return s end 89 | src_f:close() 90 | dst_f:write(filter(contents)) 91 | dst_f:close() 92 | end 93 | 94 | local cp_func = os.type == "unix" and "cp" or "copy" 95 | function copy(src,dest) 96 | local command = string.format("%s %s %s", cp_func, src, dest) 97 | if cp_func == "copy" then command = command:gsub("/",'\\') end 98 | print("Copy: "..command) 99 | os.execute(command) 100 | end 101 | 102 | mkdirectories = function(dirs) 103 | local currdir = lfs.currentdir() 104 | --print("Path: "..path) 105 | --local dirs = path:split("/") 106 | --table.remove(dirs,#dirs) 107 | for _,dir in ipairs(dirs) do 108 | local status = lfs.chdir(dir) 109 | if not status then 110 | local status, msg = lfs.mkdir(dir) 111 | if not status then 112 | print("Path making error: "..msg) 113 | break 114 | else 115 | lfs.chdir(dir) 116 | end 117 | end 118 | print(dir) 119 | end 120 | --lfs.copy(currdir.."/".."make.lua",currdir.."/"..path.."/make.lua") 121 | lfs.chdir(currdir) 122 | --os.execute("cp make.lua "..path) 123 | end 124 | 125 | 126 | -- Config loading 127 | local function run(untrusted_code, env) 128 | if untrusted_code:byte(1) == 27 then return nil, "binary bytecode prohibited" end 129 | local untrusted_function = nil 130 | if not loadstring then 131 | untrusted_function, message = load(untrusted_code, nil, "t",env) 132 | else 133 | untrusted_function, message = loadstring(untrusted_code) 134 | end 135 | if not untrusted_function then return nil, message end 136 | if not setfenv then setfenv = function(a,b) return true end end 137 | setfenv(untrusted_function, env) 138 | return pcall(untrusted_function) 139 | end 140 | 141 | local main_settings = {} 142 | main_settings.fonts = {} 143 | local env = {} 144 | env.Font = function(s) 145 | local font_name = s["name"] 146 | if not font_name then return nil, "Cannot find font name" end 147 | env.settings.fonts[font_name] = s 148 | end 149 | 150 | env.Make = make4ht.Make 151 | env.Make.params = env.settings 152 | env.Make:add("test","no takže ${tex4ht_sty_par} ${htlatex} ${input} ${config}") 153 | env.Make:add("htlatex", "${htlatex} ${latex_par} --jobname=${input} '\\makeatletter\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else\\expandafter\\Link\\fi}\\def\\Link#1.a.b.c.{\\g@addto@macro\\@documentclasshook{\\RequirePackage[#1${mathml}html]{tex4ht}\\RequirePackage{tex4ebook}}\\let\\HCode\\documentstyle\\def\\documentstyle{\\let\\documentstyle\\HCode\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\def\\HCode####1{\\documentstyle[tex4ht,}\\@ifnextchar[{\\HCode}{\\documentstyle[tex4ht]}}}\\makeatother\\HCode '${config}${tex4ht_sty_par}'.a.b.c.\\input ${input}'") 154 | env.Make:add("tex4ht","tex4ht ${input} ${tex4ht_par}") 155 | env.Make:add("t4ht","t4ht ${input} ${t4ht_par}") 156 | 157 | function load_config(settings, config_name) 158 | local settings = settings or main_settings 159 | env.settings = settings 160 | local config_name = config_name or "config.lua" 161 | local f = io.open(config_name,"r") 162 | if not f then return env, "Cannot open config file" end 163 | local code = f:read("*all") 164 | assert(run(code,env)) 165 | return env 166 | end 167 | 168 | 169 | -------------------------------------------------------------------------------- /tex4ebook: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env texlua 2 | -- Package tex4ebook. Author Michal Hoftich 3 | -- This package is subject of LPPL license, version 1.3 4 | -- 5 | kpse.set_program_name("luatex") 6 | require("lapp-mk4") 7 | -- require("ebookutils") 8 | logging = require "make4ht-logging" 9 | if os.type == "windows" then logging.use_colors = false end 10 | local log = logging.new("tex4ebook") 11 | local ebookutils = require "mkutils" 12 | local mkparams = require "mkparams" 13 | local mk_config = require "make4ht-config" 14 | 15 | -- Setting 16 | local latex_cmd="latex" 17 | local copy_cmd="copy" 18 | local move_cmd="move" 19 | local env_param="%%" 20 | local htlatex_call="" 21 | -- These correspond to htlatex parameters 22 | local tex4ht_sty_par="" 23 | local tex4ht_par="" 24 | local t4ht_par="" 25 | local latex_par="" 26 | local output_formats={epub=true,mobi=true,epub3=true,azw=true, azw3=true} 27 | local executor=nil 28 | local tidy = false 29 | local include_fonts = false 30 | 31 | -- message used for option parsing by the mkparams lib 32 | local arg_message = [[ 33 | tex4ebook - ebook generation support for LaTeX 34 | Usage: 35 | tex4ebook [switches] filename ["tex4ht.sty op."] ["tex4ht op."] ["t4ht op"] ["latex op"] 36 | -a,--loglevel (default status) Set log level. 37 | possible values: debug, info, status, warning, error, fatal 38 | -c,--config (default xhtml) Custom config file 39 | -d,--output-dir (default nil) Output directory 40 | -B,--build-dir (default nil) Build directory 41 | -e,--build-file (default nil) If build file is different than `filename`.mk4 42 | -f,--format (default epub) Output format. Supported values: epub, epub3, mobi 43 | -h,--help Display help message 44 | -j,--jobname (default nil) Set the jobname 45 | -l,--lua Use lualatex for document compilation 46 | -m,--mode (default default) Switch which can be used in the makefile 47 | -r,--resolution (default nil) This option is obsolete, use build files instead! 48 | -s,--shell-escape Enable shell escape in htlatex run 49 | -t,--tidy Run html tidy on html output. May result in wrong spacing! 50 | -x,--xetex Use xelatex for document compilation 51 | -v,--version Display version number 52 | ]] 53 | 54 | -- options for the template used by mkparams to generate the help message 55 | local mkparams_parameters = { 56 | progname = "tex4ebook", 57 | issue_tracker = "https://github.com/michal-h21/tex4ebook/issues" 58 | } 59 | 60 | -- process the command line arguments 61 | local args = mkparams.get_args(mkparams_parameters, arg_message) 62 | 63 | if args["filename"] == nil and args["version"] == false then 64 | print(arg_message) 65 | return 66 | else 67 | input_file=args["filename"] 68 | end 69 | 70 | if args.version then 71 | print "tex4ebook {{version}}" 72 | return 73 | end 74 | 75 | if args.lua then 76 | latex_cmd="dvilualatex" 77 | elseif args.xetex then 78 | latex_cmd="xelatex --no-pdf" 79 | end 80 | 81 | 82 | if args["shell-escape"] then 83 | latex_par = latex_par .. " -shell-escape" 84 | end 85 | 86 | if args["include-fonts"] then 87 | include_fonts = true 88 | end 89 | 90 | local mode = args.mode or "default" 91 | 92 | if os.type=="unix" then 93 | env_param="$" 94 | copy_cmd="cp" 95 | move_cmd="mv" 96 | t4ht_dir_format="%s/" 97 | else 98 | env_param="%%" 99 | copy_cmd="copy" 100 | move_cmd="move" 101 | t4ht_dir_format="%s" 102 | end 103 | 104 | if args.tidy then 105 | tidy = true 106 | else 107 | tidy = false 108 | end 109 | 110 | local sty_args = "" 111 | if args[2] then 112 | sty_args = "," .. args[2] 113 | end 114 | local tex4ht_sty_par = tex4ht_sty_par + sty_args --args[2] 115 | local tex4ht_par = tex4ht_par +args[3] 116 | local t4ht_par = t4ht_par + args[4] 117 | local latex_par = latex_par + args[5] 118 | 119 | -- use default parameter processing from make4ht 120 | -- add some needed args (is it useful anymore?) 121 | args.filename=input_file 122 | 123 | -- let make4ht handle the common options 124 | local params = mkparams.process_args(args) 125 | local input = params.input 126 | log:status("Conversion started") 127 | log:status("Input file: ".. params.tex_file) 128 | 129 | if params.builddir ~= "" then 130 | mkutils.make_path(params.builddir) 131 | end 132 | 133 | 134 | 135 | local output_format = params.output_format 136 | -- use epub as default output_format 137 | output_format = output_format or "epub" 138 | -- load common_domfilters extension by default 139 | local extensions = ebookutils.add_extensions("+common_domfilters", params.extensions) 140 | extensions = ebookutils.load_extensions(extensions, output_format) 141 | -- but also support tex4ebook!s own parameters 142 | local oldparams = { 143 | -- htlatex=latex_cmd 144 | -- ,input=input 145 | -- ,tex_file = tex_file 146 | format = output_format 147 | -- ,latex_par=latex_par 148 | -- ,tex4ht_sty_par=tex4ht_sty_par 149 | -- ,tex4ht_par=tex4ht_par 150 | -- ,t4ht_par=t4ht_par 151 | -- ,mode = mode 152 | ,t4ht_dir_format=t4ht_dir_format 153 | ,tidy = tidy 154 | ,include_fonts = include_fonts 155 | ,resolution=args.resolution 156 | ,packages="\\RequirePackage{tex4ebook}" 157 | } 158 | 159 | if oldparams.resolution~="nil" then 160 | log:warning("The resolution parameter is deprecated. Please use a build file") 161 | log:warning("See information about Make:image() command in Make4ht documenation") 162 | end 163 | 164 | -- extend params 165 | for k,v in pairs(oldparams) do 166 | params[k] = v 167 | end 168 | 169 | params.tex4ht_sty_par=params.tex4ht_sty_par .. ",charset=utf-8,"..output_format 170 | params.tex4ht_par= " -cmozhtf -utf8" .. params.tex4ht_par 171 | 172 | 173 | 174 | -- find tex4ebook configuration file 175 | local configname = "tex4ebook" 176 | local conffile = mk_config.find_config(configname) or mk_config.find_xdg_config(configname) 177 | if conffile then 178 | log:info("Using configuration file: " .. conffile) 179 | ebookutils.load_config(params, conffile) 180 | else 181 | log:info "No conffile" 182 | end 183 | 184 | local build_file = input.. ".mk4" 185 | 186 | if args["build-file"] and args["build-file"] ~= "nil" then 187 | build_file = args["build-file"] 188 | end 189 | 190 | local config_file = ebookutils.load_config(params, build_file) 191 | 192 | params["config_file"] = config_file 193 | 194 | 195 | if output_formats[output_format] then 196 | executor=require("tex4ebook-exec_"..output_format) 197 | params=executor.prepare(params) 198 | if #extensions > 0 then 199 | params = ebookutils.extensions_prepare_parameters(extensions,params) 200 | end 201 | else 202 | log:error("Unknown output format: "..output_format) 203 | return 204 | end 205 | --config_file.Make:run() 206 | -- print("${htlatex} ${input} \"${tex4ht_sty_par}\" \"${tex4ht_par}\" \"${t4ht_par}\" \"${latex_par}\"" % params) 207 | if #extensions > 0 then 208 | config_file.Make = ebookutils.extensions_modify_build(extensions, config_file.Make) 209 | end 210 | 211 | executor.run(input,params) 212 | executor.writeContainer() 213 | executor.clean() 214 | log:status("Conversion finished") 215 | logging.exit_status() 216 | --print(args[1]) 217 | -------------------------------------------------------------------------------- /tex4ebook-epub3.4ht: -------------------------------------------------------------------------------- 1 | %\typeout{!!!!!!!!!!!!!!!!!!!!!!!!!!!epub3!!!!!!!!!!!!} 2 | \Configure{NcxDoctype}{} 3 | \Configure{VERSION}{} 4 | 5 | % we must remove attributes from opf namespace 6 | \def\print:opf:scheme#1{} 7 | \Configure{OpfScheme}{} 8 | 9 | \Configure{OpfMetadata}{\HCode{}\:iso:date\HCode{}} 10 | \def\opf:package:lang{xml:lang="\GetLanguage"} 11 | 12 | %%%%%%%%%%%%%%%%%%%%%%% 13 | %% Configure toc nav %% 14 | %%%%%%%%%%%%%%%%%%%%%%% 15 | \Configure{tableofcontents}{ 16 | \a:NavMap 17 | \resettoclevels{part,appendix,chapter,section,subsection,subsubsection} 18 | \navsection{part}{part,appendix,chapter,section,subsection,subsubsection} 19 | \navsection{appendix}{appendix,chapter,section,subsection,subsubsection} 20 | \navsection{chapter}{appendix,chapter,section,subsection,subsubsection} 21 | \navsection{section}{section,subsection,subsubsection} 22 | \navsection{subsection}{subsection,subsubsection} 23 | \navsection{subsubsection}{subsubsection} 24 | %\HtmlParOff 25 | \Configure{toTocLink}{}{} 26 | }{\b:NavMap}{}{}{} 27 | %%%%%%%%%%% 28 | \newcount\:toccount 29 | \Configure{NavMap}{\ifvmode\IgnorePar\fi\EndP\boolfalse{tocnoempty}\global\advance\:toccount by1\HCode{}} 35 | %%%%%%%%%%% 36 | \Configure{NavSection}{% 37 | \booltrue{tocnoempty} 38 | \HCode{
  • }}{\HCode{
      \Hnewline}}{\ }{\Tg
    \Tg
  • } 39 | % Disable numbering of the TOC by the reading system, numbers are added by tex4ht 40 | \Css{nav.toc ol, nav ol.toc{list-style: none;}} 41 | %%%% End toc nav configuration 42 | \def\CoverMetadata#1{% 43 | \special{t4ht+@File: #1}% 44 | \Configure{OpfManifest}{\HCode{}}% 45 | } 46 | %\Configure{CoverImage}{% 47 | % \HPage{}% 48 | % \Configure{OpfManifest}{\HCode{}}% 49 | %\box0=\hbox\bgroup 50 | % \ifvmode \IgnorePar\fi \EndP% 51 | % \HCode{
    \Hnewline}} 52 | %} 53 | % {%\ifvmode \IgnorePar\fi \EndP% 54 | %\HCode{
    \Hnewline}% 55 | %\EndHPage{} 56 | %\egroup 57 | %} 58 | %\:CheckOption{mathml} 59 | %\if:Option 60 | \apptocmd{\a:DviMath}{\opf:add:property{mathml}}{}{}% 61 | %\fi 62 | \Configure{DOCTYPE}{\HCode{\Hnewline}} 63 | \Configure{xmlns}{}{http://www.w3.org/1999/xhtml} 64 | \Configure{xmlns}{epub}{http://www.idpf.org/2007/ops} 65 | %\Configure{xmlns}{m}{http://www.w3.org/1998/Math/MathML} 66 | %\Configure{HTML}{\HCode{\Hnewline}}{\HCode{\Hnewline}} 67 | \Configure{HTML}{\HCode{\Hnewline}}{\HCode{\Hnewline}} 69 | \Configure{@HEAD}{} 70 | \Configure{@HEAD}{\HCode{\Hnewline}} 71 | \Configure{@HEAD}{\HCode{\Hnewline}} 73 | \Configure{@HEAD}{\HCode{\Hnewline}} 76 | \Configure{EpubVersion}{3.0} 77 | \Configure{OpfItemProperties}{properties="}{"} 78 | 79 | % Daisy configuration 80 | 81 | \:CheckOption{daisy-}\if:Option\else 82 | % This configuration is used to generate the DAISY accessibility metadata 83 | % https://kb.daisy.org/publishing/docs/metadata/schema.org/index.html 84 | \Configure{OpfMetadata}{\HCode{EPUB Accessibility 1.1 - WCAG 2.0 Level AA}} 85 | % from https://kb.daisy.org/publishing/docs/metadata/schema.org/index.html#ex-01 86 | \Configure{OpfMetadata}{\HCode{textual}} 87 | \Configure{OpfMetadata}{\HCode{visual}} 88 | \Configure{OpfMetadata}{\HCode{textual}} 89 | \Configure{OpfMetadata}{\HCode{alternativeText}} 90 | \Configure{OpfMetadata}{\HCode{MathML}} 91 | % highContrastDisplay, DisplayTransformability, index, PrintPageNumbers, alternativeText, MathML, LongDescription, readingOrder, structuralNavigation, tableOfContents 92 | \Configure{OpfMetadata}{\HCode{noFlashingHazard}} 93 | \Configure{OpfMetadata}{\HCode{noSoundHazard}} 94 | \Configure{OpfMetadata}{\HCode{noMotionSimulationHazard}} 95 | % I didn't find that specific wording (accessibilityhazard) in the descriptions of WCAG, but in the error messages during the publication process with my service party 96 | \Configure{OpfMetadata}{\HCode{This publication conforms to WCAG 2.0 Level AA.}} 97 | \fi 98 | 99 | % Structural elements 100 | 101 | \:CheckOption{fn-in} 102 | \if:Option 103 | \else 104 | % Foootnote configuration for epub3 105 | % Footnotes are printed directly after the paragraph they appeared in 106 | % footnotebox - configure box in which footnotes are printed 107 | % default configuration doesn't work in ibooks, don't know why 108 | \NewConfigure{footnotebox}{2} 109 | \Configure{footnotebox}{\ifvmode\IgnorePar\fi\HCode{
    \Hnewline}}% 110 | {\ifvmode\IgnorePar\fi\HCode{\Hnewline
    \Hnewline}}% 111 | \newbox\footnotebox% 112 | % We must create new link command, so footnote mark can link to footnote text 113 | \LinkCommand\fnlink{aside,href,id,class="footnote" epub:type="footnote" role="doc-footnote"} 114 | \Configure{footnotemark}{\NoFonts\Link[ epub:type="noteref" role="doc-noteref"]{fn\FNnum x\minipageNum}{}}{\EndLink\EndNoFonts}% 115 | \Configure{footnotetext}{\SaveEndP\global\setbox\footnotebox=\vtop\bgroup\NoFonts% 116 | \ifvoid\footnotebox\else\unvbox\footnotebox\fi% 117 | \bgroup% 118 | \IgnorePar% 119 | \fnlink{}{fn\FNnum x\minipageNum}\par\AnchorLabel% 120 | }{\EndNoFonts} 121 | {% 122 | \ifvmode\IgnorePar\fi\EndP\HCode{\Hnewline}% 123 | \Endfnlink\egroup\egroup\RecallEndP}% 124 | 125 | \def\printfn{% 126 | \ifvoid\footnotebox\else% 127 | \a:footnotebox% 128 | \box\footnotebox% 129 | \b:footnotebox% 130 | \fi% 131 | } 132 | 133 | % configure HtmlPar to print footnotebox. 134 | \Configure{HtmlPar} 135 | {\EndP\printfn\HCode{

    }} 136 | {\EndP\printfn\HCode{

    }} 137 | {\HCode{

    \Hnewline}} 138 | {\HCode{

    \Hnewline}} 139 | 140 | \Css{.footnote{font-size:small;}} 141 | \Css{.footnotes hr{width:30\%;margin:0 auto 0 0;}} 142 | \Css{p + section.footnotes{margin-bottom: 1rem;}} 143 | \fi 144 | -------------------------------------------------------------------------------- /tex4ebook-exec_epub3.lua: -------------------------------------------------------------------------------- 1 | module(...,package.seeall) 2 | local eb = require("tex4ebook-exec_epub") 3 | local dom = require("luaxml-domobject") 4 | local log = logging.new "exec_epub3" 5 | 6 | local ext = "xhtml" 7 | local outputdir = nil 8 | local input = nil 9 | function prepare(params) 10 | local basedir = params.input.."-".. params.format 11 | local outputdir_name="OEBPS" 12 | outputdir= basedir.."/"..outputdir_name 13 | input = params.input 14 | params.ext = ext 15 | params.tex4ht_sty_par = params.tex4ht_sty_par .. ",html5" 16 | params.packages = params.packages .. string.format("\\Configure{ext}{%s}",ext) 17 | return eb.prepare(params) 18 | end 19 | 20 | function run(out,params) 21 | return eb.run(out, params) 22 | end 23 | 24 | 25 | local function makeTOC(document) 26 | local template = [[ 27 | 28 | 32 | TOC 33 | 34 | 42 | 43 | 44 | ]] % {document=document} 45 | return template 46 | end 47 | 48 | local function add_media_overlays(content) 49 | local add_meta = function(package, attributes, text) 50 | local meta = package:create_element("meta",attributes) 51 | local dur_el = meta:create_text_node(text) 52 | meta:add_child_node(dur_el) 53 | package:add_child_node(meta) 54 | end 55 | -- calculate total audio time 56 | local calc_times = function(times) 57 | local time = 0 58 | for _, curr in ipairs(times) do 59 | -- smil file contains timestamps in the H:M:S format, we need to parse it 60 | local hours, minutes, seconds = curr:match("(%d+):(%d+):(%d+)") 61 | time = time + os.time({year=1970, day=1, month=1, hour=hours, min=minutes, sec=seconds}) 62 | end 63 | return os.date("%H:%M:%S",time) 64 | end 65 | -- the second parameter for parse is table with void elements. the OPF format has no 66 | -- void elements, so it needs to be empty, otherwise we may get parsing error because of 67 | -- element, which is included in the default void elements 68 | local opfdom = dom.parse(content, {}) 69 | local items = opfdom:query_selector("manifest item") 70 | local ref = {} 71 | local times = {} 72 | local package = opfdom:query_selector("metadata")[1] 73 | -- we must read all smil files and find references to html files 74 | -- it is necessary to add media-overlay attribute to the referenced items 75 | for _, item in ipairs(items) do 76 | local href = item:get_attribute("href") 77 | ref[href] = item 78 | -- we must read audio length from the smil file and add it as a property 79 | if href:match("smil$") then 80 | local f = io.open(outputdir .. "/" .. href, "r") 81 | if not f then break end 82 | local smil = f:read("*all") 83 | f:close() 84 | local smildom = dom.parse(smil) 85 | local audios = smildom:query_selector("audio") 86 | local last = audios[#audios] 87 | -- add audio duration to the metadata section 88 | if last then 89 | local duration = last:get_attribute("clipend") 90 | if duration then 91 | -- todo: calculate total audio length 92 | table.insert(times, duration) 93 | local audio_id = item:get_attribute("id") 94 | add_meta(package, {property="media:duration", refines="#"..audio_id}, duration) 95 | end 96 | end 97 | 98 | -- add the media-overlay attribute 99 | local textref = smil:match('epub:textref="(.-)"') 100 | local id = item:get_attribute("id") 101 | local referenced = ref[textref] 102 | if referenced then 103 | referenced:set_attribute("media-overlay", id) 104 | end 105 | end 106 | end 107 | -- calculate length of all media overlay audio files 108 | if #times > 0 then 109 | local totaltime = calc_times(times) 110 | add_meta(package,{property="media:duration"}, totaltime) 111 | end 112 | local serialized = opfdom:serialize() 113 | return serialized 114 | end 115 | 116 | 117 | -- elements that shouldn't be put inside in TOC 118 | local stop_toc_processing_elements = { 119 | ol = true, 120 | ul = true 121 | } 122 | 123 | local function remove_spurious_TOC_elements(tocdom) 124 | local function count_child_elements(el) 125 | -- count children elements of the current element 126 | local count = 0 127 | for _, curr_el in ipairs(el:get_children()) do 128 | if curr_el:is_element() then count = count + 1 end 129 | end 130 | return count 131 | end 132 | -- modify the TOC to comply to epubcheck tests 133 | -- add a blank
  • to empty
      134 | for _, el in ipairs(tocdom:query_selector("ol")) do 135 | if count_child_elements(el) == 0 then 136 | el:remove_node() 137 | end 138 | end 139 | -- place child elements of the
    1. elements to an element, epubcheck reports 140 | -- error for text nodes that are direct child of
    2. 141 | for _, el in ipairs(tocdom:query_selector("li")) do 142 | 143 | local newa = el:create_element("a") 144 | local newchildren = {newa} 145 | -- we want to stop putting content as child of when it 146 | -- finds child TOC entries 147 | local keep_processing = true 148 | for i, child in ipairs(el._children) do 149 | child_name = child:get_element_name() 150 | -- put contents of
    3. to a new element 151 | if child:is_element() and child_name == "a" then 152 | -- set id and href of the new element, if it isn't set already 153 | if not newa:get_attribute("href") then 154 | local id = child:get_attribute("id") 155 | local href = child:get_attribute("href") 156 | newa:set_attribute("id", id) 157 | newa:set_attribute("href", href) 158 | end 159 | -- copy contents to the new element 160 | for _, x in ipairs(child._children or {}) do newa:add_child_node(x:copy_node()) end 161 | 162 | elseif stop_toc_processing_elements[child_name] then 163 | -- don't put child toc entries to the new 164 | keep_processing = false 165 | newchildren[#newchildren+1] = child 166 | elseif keep_processing == true then 167 | -- put every node before
        or