From 8cd3bed99a3581a336526a347f925d9925ddc0d8 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Fri, 1 Nov 2019 21:03:38 +0000 Subject: tex4ebook (1nov19) git-svn-id: svn://tug.org/texlive/trunk@52604 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/support/tex4ebook/README | 139 ++++---- .../texmf-dist/doc/support/tex4ebook/changelog.tex | 100 ++++++ Master/texmf-dist/doc/support/tex4ebook/readme.tex | 137 +++++--- .../doc/support/tex4ebook/tex4ebook-doc.pdf | Bin 73368 -> 78095 bytes Master/texmf-dist/scripts/tex4ebook/exec_epub.lua | 372 ++++++++++----------- Master/texmf-dist/scripts/tex4ebook/exec_epub3.lua | 129 ++++++- Master/texmf-dist/scripts/tex4ebook/exec_mobi.lua | 3 +- Master/texmf-dist/scripts/tex4ebook/tex4ebook | 99 +++--- .../texmf-dist/tex/latex/tex4ebook/tex4ebook.4ht | 20 +- .../texmf-dist/tex/latex/tex4ebook/tex4ebook.sty | 45 ++- 10 files changed, 656 insertions(+), 388 deletions(-) (limited to 'Master') diff --git a/Master/texmf-dist/doc/support/tex4ebook/README b/Master/texmf-dist/doc/support/tex4ebook/README index 951dad5a573..796445674a2 100644 --- a/Master/texmf-dist/doc/support/tex4ebook/README +++ b/Master/texmf-dist/doc/support/tex4ebook/README @@ -2,24 +2,22 @@ # Introduction -`TeX4ebook` is bundle of Lua scripts and `LaTeX` packages for conversion of -LaTeX files to ebook formats, for example `epub`, `mobi` and `epub3`. `tex4ht` -is used as conversion engine. +`TeX4ebook` is a tool for conversion from \LaTeX\ to +ebook formats, such as EPUB, MOBI and EPUB 3. +It is based on `TeX4ht`^[https://tug.org/tex4ht/], +which provides instructions for the actual \LaTeX\ to HTML conversion, +and on `make4ht`^[https://ctan.org/pkg/make4ht?lang=en]. -Note that while `mobi` is supported by Amazon Kindle, most widespread ebook -reader, it doesn't support `mathml` and this means that math must re represented -as images. The same is true for `epub`. This is not a good thing, especially -for inline math, as you may experience wrong baselines. If your ebook contains -math, the only correct solution is to produce `epub3`, as it supports `mathml`. -The issue with `epub3` is, that majority of `e-ink` ebook readers doesn't -support this format, reader applications exists mainly for Android and Apple -devices. For books which contains mainly prose, all formats should be suitable, -but `epub3` supports most features from web standards, such as `CSS`. -As with `tex4ht`, the emphasis is on conversion of document's logical structure -and metadata, basic visual appearance is preserved as well, but you should use +The conversion is focused on the logical structure of the converted document +and metadata. Basic visual appearance is preserved as well, but you should use custom configurations if you want to make the document more visually appealing. -You can include custom `CSS` or fonts in configuration files. +You can include custom `CSS` or fonts in a configuration file. + +`TeX4ebook` supports the same features as `make4ht`, in particular build files and extensions. +These may be used for post-processing of the generated HTML files, or to configure the image conversion. +See the `make4ht` documentation to see the supported features. + ## License @@ -38,6 +36,7 @@ You don't have to modify your source files unless you want to use commands defined by `tex4ebook` in the document, or when your document uses a package which causes a compilation error. + If you want to use `tex4ebook` commands, add this line to your document preamble: @@ -51,9 +50,13 @@ But it is optional. You shouldn't need to modify your \TeX\ files # Command line options +`-a,--loglevel` + +: Set message log level. Possible values: debug, info, status, warning, error, fatal. Default: status. + `-c,--config` -: specify custom config file for `tex4ht` +: specify custom config file for `TeX4ht` **example config file**: File `sample.cfg` @@ -78,7 +81,11 @@ But it is optional. You shouldn't need to modify your \TeX\ files `-f,--format (default epub)` -: Output format. Epub, Epub3 and Mobi formats are supported. +: Output format. Possible values are `epub`, `epub3` and `mobi`. + +`-j,--jobname` + +: Specify the output file name, without file extension. `-l,--lua` @@ -97,7 +104,7 @@ But it is optional. You shouldn't need to modify your \TeX\ files `-s,--shell-escape` -: Enable shell escape in htlatex run. This may be needed if you run external +: Enable shell escape in the `htlatex` run. This is necessary for the execution of the external commands from your source files. `-t,--tidy` @@ -112,12 +119,11 @@ But it is optional. You shouldn't need to modify your \TeX\ files `-v,--version` : Print the version number. - # Configuration -`tex4ebook` uses `tex4ht`^[http://www.tug.org/tex4ht/] for conversion from LaTeX -to html. `tex4ht` is highly configurable using config files. Basic config file +`TeX4ebook` uses `TeX4ht`^[http://www.tug.org/tex4ht/] for conversion from LaTeX +to html. `TeX4ht` is highly configurable using config files. Basic config file structure is \Preamble{xhtml, comma separated list of options} @@ -127,16 +133,16 @@ structure is \EndPreamble Basic info about command configurations can be found in a -work-in-progres *tex4ht tutorial*^[https://github.com/michal-h21/helpers4ht/wiki/tex4ht-tutorial], -*tex4ht documentation*^[http://www.tug.org/applications/tex4ht/mn11.html], +work-in-progres *TeX4ht tutorial*^[https://github.com/michal-h21/helpers4ht/wiki/tex4ht-tutorial], +*TeX4ht documentation*^[http://www.tug.org/applications/tex4ht/mn11.html], and in series of blogposts on CV Radhakrishnan's blog: -*Configure part 1*^[http://www.cvr.cc/?p=323], -*Configure part 2*^[http://www.cvr.cc/?p=362], -*Low level commands*^[http://www.cvr.cc/?p=482]. +*Configure part 1*^[https://web.archive.org/web/20180908234227/http://www.cvr.cc/?p=323], +*Configure part 2*^[https://web.archive.org/web/20180908201057/http://www.cvr.cc/?p=362], +*Low level commands*^[https://web.archive.org/web/20180909101325/http://cvr.cc/?p=482]. Available options for `\Preamble` command are listed in the article -*TeX4ht: options*^[http://www.cvr.cc/?p=504]. *Comparison of tex4ebook and Pandoc output*^[https://github.com/richelbilderbeek/travis_tex_to_epub_example_1] +*TeX4ht: options*^[https://web.archive.org/web/20180813043722/http://cvr.cc/?p=504]. *Comparison of tex4ebook and Pandoc output*^[https://github.com/richelbilderbeek/travis_tex_to_epub_example_1] -Great source of tips for `tex4ht` configuring is *tex4ht tag on TeX.sx*^[http://tex.stackexchange.com/questions/tagged/tex4ht], there is also a *tag for tex4ebook*^[http://tex.stackexchange.com/questions/tagged/tex4ebook]. +A great source of tips for `TeX4ht` configuration is *tex4ht tag on TeX.sx*^[http://tex.stackexchange.com/questions/tagged/tex4ht]. There is also a *tag for tex4ebook*^[http://tex.stackexchange.com/questions/tagged/tex4ebook]. Examples of interesting questions are *including images and fonts in ebooks*^[http://tex.stackexchange.com/a/213165/2891] @@ -149,7 +155,7 @@ or *setting image size in em units instead of pt*^[http://tex.stackexchange.com/ \Configure{UniqueIdentifier}{identifier} -Every epub file should have unique identifier, like ISBN, DOI, URI etc. +Every EPUB file should have unique identifier, like ISBN, DOI, URI etc. Default identifier is URI, with value `http://example.com/\jobname`. \Configure{@author}{\let\footnote\@gobble} @@ -161,7 +167,7 @@ which don't belongs here, such as `\footnote`. \Configure{OpfScheme}{URI} Type of unique identifier, default type is URI. It is -used only in `epub`, it is deprecated for `epub3` +used only in the EPUB format, it is deprecated for EPUB 3. \Configure{resettoclevels}{list of section types in descending order} @@ -170,7 +176,7 @@ value is the whole document hierarchy, from `\part` to `\paragraph`. \Configure{DocumentLanguage}{language code} -Each ePub file must declare the document language. It is inferred from `babel` main +Each EPUB file must declare the document language. It is inferred from `babel` main language by default, but you can configure it when it doesn't work correctly. The `language code` should be in [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) form. @@ -203,11 +209,11 @@ Add item to `` section in the `OPF` file. \Configure{xmlns}{prefix}{uri} -Add xml name space to `xhtml` files. Useful in `EPUB 3` +Add `XML` name space to the generated `XHTML` files. Useful in `EPUB 3`. -## Commands available in config files +## Commands available in the config file `\OpfRegisterFile[filename]` @@ -244,53 +250,44 @@ See the `make4ht` documentation for an example and more information. # Troubleshooting +## Fixed layout EPUB + +The basic support for the Fixed layout EPUB 3 can be enabled using the following configurations: + + \Configure{OpfMetadata}{\HCode{pre-paginated}} + \Configure{OpfMetadata}{\HCode{landscape}} + \Configure{OpfMetadata}{\HCode{none}} + \Configure{@HEAD}{\HCode{\Hnewline}} + +Modify the dimensions in the `pre-paginated}} +\Configure{OpfMetadata}{\HCode{landscape}} +\Configure{OpfMetadata}{\HCode{none}} +\Configure{@HEAD}{\HCode{\Hnewline}} +\end{verbatim} + +Modify the dimensions in the +\texttt{\textless{}meta\ name="viewport\textgreater{}} element according +to your needs. + +\hypertarget{math-issues}{% +\subsection{Math issues}\label{math-issues}} + +Note that while \texttt{mobi} is supported by Amazon Kindle, most +widespread ebook reader, it doesn't support \texttt{MathML}. This means +that math must be represented as images. The same issue is true for the +EPUB format as well. This is problematic especially for the inline math, +as you may experience wrong vertical alignment of the math content and +surrounding text. If your ebook contains math, a better solution is to +produce the \texttt{epub3} format, as it supports \texttt{MathML}. The +issue with EPUB 3 is that majority of \texttt{e-ink} ebook readers don't +support it. Reader applications exists mainly for Android and Apple +devices. For books which contains mainly prose, all formats should be +suitable, but EPUB 3 supports most features from web standards, such as +\texttt{CSS}. + +\hypertarget{compilation-errors}{% +\subsection{Compilation errors}\label{compilation-errors}} + When compilation of the document breaks with error during \texttt{LaTeX} -run, it may be caused by some problem in \texttt{tex4ht} configuration. +run, it may be caused by some problem in \texttt{TeX4ht} configuration. Comment out line \texttt{\textbackslash{}usepackage\{tex4ebook\}} in your source file and run command: @@ -303,9 +340,9 @@ htlatex filename \end{verbatim} if same error as in \texttt{tex4ebook} run arises, the problem is in -some \texttt{tex4ht} configuration. Try to identify the source of +some \texttt{TeX4ht} configuration. Try to identify the source of problem and if you cannot find the solution, make minimal example -showing the error and ask for help either on \emph{tex4ht mailing +showing the error and ask for help either on \emph{TeX4ht mailing list}\footnote{\url{http://tug.org/mailman/listinfo/tex4ht}} or on \emph{TeX.sx}\footnote{\url{http://tex.stackexchange.com/}}. diff --git a/Master/texmf-dist/doc/support/tex4ebook/tex4ebook-doc.pdf b/Master/texmf-dist/doc/support/tex4ebook/tex4ebook-doc.pdf index 6c7fd83af08..8372406fd9d 100644 Binary files a/Master/texmf-dist/doc/support/tex4ebook/tex4ebook-doc.pdf and b/Master/texmf-dist/doc/support/tex4ebook/tex4ebook-doc.pdf differ diff --git a/Master/texmf-dist/scripts/tex4ebook/exec_epub.lua b/Master/texmf-dist/scripts/tex4ebook/exec_epub.lua index d92a8d103c9..24cc626aad3 100755 --- a/Master/texmf-dist/scripts/tex4ebook/exec_epub.lua +++ b/Master/texmf-dist/scripts/tex4ebook/exec_epub.lua @@ -2,6 +2,7 @@ module("exec_epub",package.seeall) local lfs = require("lfs") local os = require("os") local io = require("io") +local log = logging.new("exec_epub") --local ebookutils = require("ebookutils") local ebookutils = require "mkutils" -- font loading doesn't work, font database format changes often @@ -29,25 +30,19 @@ local function deletedir(dir) if file ~= "." and file ~= ".." then if lfs.attributes(file_path, 'mode') == 'file' then os.remove(file_path) - print('remove file',file_path) + log:info('remove file',file_path) elseif lfs.attributes(file_path, 'mode') == 'directory' then - print('dir', file_path) + log:info('dir', file_path) deletedir(file_path) end end end lfs.rmdir(dir) end - print('remove dir',dir) + log:info('remove dir',dir) end function prepare(params) - local randname=tostring(math.random(12000)) - -- if not ebookutils.file_exists("tex4ht.env") then - -- local env_file = kpse.find_file("epub2.env") - --print("Local env file: "..env_file) - -- ebookutils.copy(env_file,"tex4ht.env") - -- end local makedir= function(path) local current = lfs.currentdir() local dir = ebookutils.prepare_path(path .. "/") @@ -60,29 +55,45 @@ function prepare(params) lfs.chdir(current) end basedir = params.input.."-".. params.format - outputdir= basedir.."/"..outputdir_name --"outdir-"..randname --os.tmpdir() + outputdir= basedir.."/"..outputdir_name deletedir(basedir) makedir(outputdir) - -- lfs.mkdir(outputdir) - --ebookutils.mkdirectories(ebookutils.prepare_path(outputdir.."/")) - metadir = basedir .."/" .. metadir_name --"metadir-"..randname + metadir = basedir .."/" .. metadir_name makedir(metadir) if params.outdir ~= "" then destdir = params.outdir .. "/" makedir(destdir) end - --local dd = ebookutils.prepare_path(metadir.."/") - --for _,d in pairs(dd) do print("metadir path: "..d) end - -- lfs.mkdir(metadir) - --local status, msg = ebookutils.mkdirectories(ebookutils.prepare_path(metadir.."/")) - --if not status then print("make mmetadir error:" ..msg) end mimetype= basedir .. "/" ..mimetype_name --os.tmpname() - print(outputdir) - print(mimetype) tidy = params.tidy include_fonts = params.include_fonts params["t4ht_par"] = params["t4ht_par"] -- + "-d"..string.format(params["t4ht_dir_format"],outputdir) params.tex4ht_sty_par = params.tex4ht_sty_par .. ",uni-html4" + params.config_file.Make.params = params + local mode = params.mode + if params.config_file.Make:length() < 1 then + if mode == "draft" then + params.config_file.Make:htlatex() + else + params.config_file.Make:htlatex() + params.config_file.Make:htlatex() + params.config_file.Make:htlatex() + end + end + if #params.config_file.Make.image_patterns > 0 then + params["t4ht_par"] = params["t4ht_par"] .." -p" + end + params.config_file.Make:tex4ht() + params.config_file.Make:t4ht() + -- do some cleanup + params.config_file.Make:match("tmp$",function(filename, par) + -- detect if a tmp file was created for content from STDIN + if par.is_tmp_file then + -- and remove it + log:info("Removing temporary file", par.tex_file) + os.remove(par.tex_file) + end + end) return(params) end @@ -90,7 +101,7 @@ function run(out,params) --local currentdir= outputfilename=out outputfile = outputfilename..".epub" - print("Output file: "..outputfile) + log:info("Output file: "..outputfile) --lfs.chdir(metadir) local m= io.open(metadir.."/container.xml","w") m:write([[ @@ -107,27 +118,7 @@ media-type="application/oebps-package+xml"/> m=io.open(mimetype,"w") m:write("application/epub+zip") m:close() - local htlatex_run = "${htlatex} ${input} \"${config}${tex4ht_sty_par}\" \"${tex4ht_par}\" \"${t4ht_par}\" \"${latex_par}\"" % params - print("Make4ht run") - print("-------------------") - params.config_file.Make.params = params - local mode = params.mode - if params.config_file.Make:length() < 1 then - if mode == "draft" then - params.config_file.Make:htlatex() - else - params.config_file.Make:htlatex() - params.config_file.Make:htlatex() - params.config_file.Make:htlatex() - end - end - if #params.config_file.Make.image_patterns > 0 then - params["t4ht_par"] = params["t4ht_par"] .." -p" - end - params.config_file.Make:tex4ht() - params.config_file.Make:t4ht() params.config_file.Make:run() - print("-------------------") --[[for k,v in pairs(params.config_file.Make) do print(k.. " : "..type(v)) end--]] @@ -162,7 +153,7 @@ function make_opf() -- Find mimetype and make item tag for each converted file in the lg file local fname,ext = item:match("([%a%d%_%-]*)%p([%a%d]*)$") local mimetype = mimetypes[ext] or "" - if mimetype == "" then print("Mimetype for "..ext.." is not registered"); return nil end + if mimetype == "" then log:info("Mimetype for "..ext.." is not registered"); return nil end local dir_part = item:split("/") table.remove(dir_part,#dir_part) local id=table.concat(dir_part,"-")..fname.."_"..ext @@ -190,165 +181,164 @@ function make_opf() --local opf_second_part = outputdir .. "/content-part2.opf" if ebookutils.file_exists(opf_first_part) and ebookutils.file_exists(opf_second_part) - then - local h_first = io.open(opf_first_part,"r") - local h_second = io.open(opf_second_part,"r") - local opf_complete = {} - table.insert(opf_complete,h_first:read("*all")) - local used_html = find_all_files(opf_complete[1]) - -- local lg_file = ebookutils.parse_lg(outputfilename..".lg") - -- The lg_file has been already loaded by make4ht, it doesn't make sense to load it again - -- Furthermore, it is now possible to add new files from Lua build files - local lg_file = Make.lgfile or ebookutils.parse_lg(outputfilename..".lg") - local used_files = {} - for _,filename in ipairs(lg_file["files"]) do - -- we need to test the filenames in order to prevent duplicates - -- filenames are tested without paths, so there may be issues if - -- the same filename is used in different directories. Is that a problem? - used_files[filename] = true - end - local outside_spine = {} - local all_used_files = find_all_files(opf_complete[1],"([%a%d%-%_]+%.[%a%d]+)") - local used_paths = {} - local used_ids = {} - for _,k in ipairs(lg_file["files"]) do - local ext = k:match("%.([%a%d]*)$") - local parts = k:split "/" - local fn = parts[#parts] - local allow_in_spine = {html="",xhtml = "", xml = ""} - table.remove(parts,#parts) - --table.insert(parts,1,"OEBPS") - table.insert(parts,1,outputdir) - -- print("SSSSS "..fn.." ext .." .. ext) - --if string.find("jpg gif png", ext) and not all_used_files[k] then - local item,id = lg_item(k) - if item then - local path = table.concat(parts) - if not used_paths[path] then - ebookutils.mkdirectories(parts) - used_paths[path]=true - end - if allow_in_spine[ext] and tidy then - if tidyconf then - print("Tidy: "..k) - local run ="tidy -c -w 200 -q -utf8 -m -config " .. tidyconf .." " .. k - os.execute(run) - else - print "Tidy: Cannot load tidyconf.conf" - end - end - if not used_ids[id] then - ebookutils.copy(k, outputdir .. "/"..k) - if not all_used_files[fn] then - table.insert(opf_complete,item) - if allow_in_spine[ext] then - table.insert(outside_spine,id) - end + then + local h_first = io.open(opf_first_part,"r") + local h_second = io.open(opf_second_part,"r") + local opf_complete = {} + table.insert(opf_complete,h_first:read("*all")) + local used_html = find_all_files(opf_complete[1]) + -- local lg_file = ebookutils.parse_lg(outputfilename..".lg") + -- The lg_file has been already loaded by make4ht, it doesn't make sense to load it again + -- Furthermore, it is now possible to add new files from Lua build files + local lg_file = Make.lgfile or ebookutils.parse_lg(outputfilename..".lg") + local used_files = {} + for _,filename in ipairs(lg_file["files"]) do + -- we need to test the filenames in order to prevent duplicates + -- filenames are tested without paths, so there may be issues if + -- the same filename is used in different directories. Is that a problem? + used_files[filename] = true + end + local outside_spine = {} + local all_used_files = find_all_files(opf_complete[1],"([%a%d%-%_]+%.[%a%d]+)") + local used_paths = {} + local used_ids = {} + for _,k in ipairs(lg_file["files"]) do + local ext = k:match("%.([%a%d]*)$") + local parts = k:split "/" + local fn = parts[#parts] + local allow_in_spine = {html="",xhtml = "", xml = ""} + table.remove(parts,#parts) + --table.insert(parts,1,"OEBPS") + table.insert(parts,1,outputdir) + -- print("SSSSS "..fn.." ext .." .. ext) + --if string.find("jpg gif png", ext) and not all_used_files[k] then + local item,id = lg_item(k) + if item then + local path = table.concat(parts) + if not used_paths[path] then + ebookutils.mkdirectories(parts) + used_paths[path]=true + end + if allow_in_spine[ext] and tidy then + if tidyconf then + log:info("Tidy: "..k) + local run ="tidy -c -w 200 -q -utf8 -m -config " .. tidyconf .." " .. k + os.execute(run) + else + log:info "Tidy: Cannot load tidyconf.conf" + end + end + if not used_ids[id] then + ebookutils.copy(k, outputdir .. "/"..k) + if not all_used_files[fn] then + table.insert(opf_complete,item) + if allow_in_spine[ext] then + table.insert(outside_spine,id) end - end - used_ids[id] = true - end - end - for _,f in ipairs(lg_file["images"]) do - local f = f.output - local p, id = lg_item(f) - -- process the images only if they weren't registered in lg_file["files"] - -- they would be processed twice otherwise - if not used_files[f] and not used_ids[id] then - ebookutils.copy(f, outputdir .. "/"..f) - table.insert(opf_complete,p) + end end used_ids[id] = true - end - local end_opf = h_second:read("*all") - local spine_items = {} - for _,i in ipairs(outside_spine) do - table.insert(spine_items, - '' % {idref=i}) - end - table.insert(opf_complete,end_opf % {spine = table.concat(spine_items,"\n")}) - h_first:close() - h_second:close() - h_first = io.open(opf_first_part,"w") - local opf_completed = table.concat(opf_complete,"\n") - -- poor man's tidy: remove trailing whitespace befora xml tags - opf_completed = opf_completed:gsub("[ ]*<","<") - opf_completed = remove_empty_guide(opf_completed) - h_first:write(opf_completed) - h_first:close() - os.remove(opf_second_part) - --ebookutils.copy(outputfilename ..".css",outputdir.."/") - ebookutils.copy(opf_first_part,outputdir.."/"..opf_first_part) - --for c,v in pairs(lg_file["fonts"]) do - -- print(c, table.concat(v,", ")) - --end - --print(table.concat(opf_complete,"\n")) - else - print("Missing opf file") - end - end - local function find_zip() - if io.popen("zip -v","r"):close() then - return "zip" - elseif io.popen("miktex-zip -v","r"):close() then - return "miktex-zip" + end + end + for _,f in ipairs(lg_file["images"]) do + local f = f.output + local p, id = lg_item(f) + -- process the images only if they weren't registered in lg_file["files"] + -- they would be processed twice otherwise + if not used_files[f] and not used_ids[id] then + ebookutils.copy(f, outputdir .. "/"..f) + table.insert(opf_complete,p) + end + used_ids[id] = true + end + local end_opf = h_second:read("*all") + local spine_items = {} + for _,i in ipairs(outside_spine) do + table.insert(spine_items, + '' % {idref=i}) end - print "It appears you don't have zip command installed. I can't pack the ebook" + table.insert(opf_complete,end_opf % {spine = table.concat(spine_items,"\n")}) + h_first:close() + h_second:close() + h_first = io.open(opf_first_part,"w") + local opf_completed = table.concat(opf_complete,"\n") + -- poor man's tidy: remove trailing whitespace befora xml tags + opf_completed = opf_completed:gsub("[ ]*<","<") + opf_completed = remove_empty_guide(opf_completed) + h_first:write(opf_completed) + h_first:close() + os.remove(opf_second_part) + --ebookutils.copy(outputfilename ..".css",outputdir.."/") + ebookutils.copy(opf_first_part,outputdir.."/"..opf_first_part) + --for c,v in pairs(lg_file["fonts"]) do + -- print(c, table.concat(v,", ")) + --end + --print(table.concat(opf_complete,"\n")) + else + log:warning("Missing opf file") + end +end +local function find_zip() + if io.popen("zip -v","r"):close() then return "zip" + elseif io.popen("miktex-zip -v","r"):close() then + return "miktex-zip" end + log:warning "It appears you don't have zip command installed. I can't pack the ebook" + return "zip" +end - function pack_container() - local ncxfilename = outputdir .. "/" .. outputfilename .. ".ncx" - if os.execute("tidy -v") > 0 then - print("Warning:\n tidy command seems missing, you should install it" .. - " in order\n to make valid epub file") - print("Using regexp based cleaning") - local lines = {} - for line in io.lines(ncxfilename) do - local content = line:gsub("[ ]*<","<") - if content:len() > 0 then - table.insert(lines, content) - end +function pack_container() + local ncxfilename = outputdir .. "/" .. outputfilename .. ".ncx" + if os.execute("tidy -v") > 0 then + log:warning("tidy command seems missing, you should install it" .. + " in order\n to make valid epub file") + log:info("Using regexp based cleaning") + local lines = {} + for line in io.lines(ncxfilename) do + local content = line:gsub("[ ]*<","<") + if content:len() > 0 then + table.insert(lines, content) end - table.insert(lines,"") - local ncxfile = io.open(ncxfilename,"w") - ncxfile:write(table.concat(lines,"\n")) - ncxfile:close() - else - print("Tidy ncx ".. - os.execute("tidy -xml -i -q -utf8 -m " .. ncxfilename)) - print("Tidy opf ".. - os.execute("tidy -xml -i -q -utf8 -m " .. - outputdir .. "/" .. "content.opf")) - end - print(mimetype) - local zip = find_zip() - -- we need to remove the epub file if it exists already, because it may contain files which aren't used anymore - if ebookutils.file_exists(outputfile) then os.remove(outputfile) end - print("Pack mimetype " .. os.execute("cd "..basedir.." && "..zip.." -q0X "..outputfile .." ".. mimetype_name)) - print("Pack metadir " .. os.execute("cd "..basedir.." && "..zip.." -qXr9D " .. outputfile.." "..metadir_name)) - print("Pack outputdir " .. os.execute("cd "..basedir.." && "..zip.." -qXr9D " .. outputfile.." "..outputdir_name)) - print("Copy generated epub ") - ebookutils.cp(basedir .."/"..outputfile, destdir .. outputfile) + end + table.insert(lines,"") + local ncxfile = io.open(ncxfilename,"w") + ncxfile:write(table.concat(lines,"\n")) + ncxfile:close() + else + log:info("Tidy ncx ".. + os.execute("tidy -xml -i -q -utf8 -m " .. ncxfilename)) + log:info("Tidy opf ".. + os.execute("tidy -xml -i -q -utf8 -m " .. + outputdir .. "/" .. "content.opf")) + end + local zip = find_zip() + -- we need to remove the epub file if it exists already, because it may contain files which aren't used anymore + if ebookutils.file_exists(outputfile) then os.remove(outputfile) end + log:info("Pack mimetype " .. os.execute("cd "..basedir.." && "..zip.." -q0X "..outputfile .." ".. mimetype_name)) + log:info("Pack metadir " .. os.execute("cd "..basedir.." && "..zip.." -qXr9D " .. outputfile.." "..metadir_name)) + log:info("Pack outputdir " .. os.execute("cd "..basedir.." && "..zip.." -qXr9D " .. outputfile.." "..outputdir_name)) + log:info("Copy generated epub ") + ebookutils.cp(basedir .."/"..outputfile, destdir .. outputfile) end - function writeContainer() - make_opf() - pack_container() - end - local function deldir(path) - for entry in lfs.dir(path) do - if entry~="." and entry~=".." then - os.remove(path.."/"..entry) - end - end - os.remove(path) - --]] - end +function writeContainer() + make_opf() + pack_container() +end +local function deldir(path) + for entry in lfs.dir(path) do + if entry~="." and entry~=".." then + os.remove(path.."/"..entry) + end + end + os.remove(path) + --]] +end - function clean() - --deldir(outputdir) - --deldir(metadir) - --os.remove(mimetype) +function clean() + --deldir(outputdir) + --deldir(metadir) + --os.remove(mimetype) end diff --git a/Master/texmf-dist/scripts/tex4ebook/exec_epub3.lua b/Master/texmf-dist/scripts/tex4ebook/exec_epub3.lua index 53173be9e9c..edf6df01c22 100755 --- a/Master/texmf-dist/scripts/tex4ebook/exec_epub3.lua +++ b/Master/texmf-dist/scripts/tex4ebook/exec_epub3.lua @@ -1,6 +1,7 @@ module(...,package.seeall) local eb = require("exec_epub") local dom = require("luaxml-domobject") +local log = logging.new "exec_epub3" local ext = "xhtml" local outputdir = nil @@ -60,7 +61,10 @@ local function add_media_overlays(content) end return os.date("%H:%M:%S",time) end - local opfdom = dom.parse(content) + -- the second parameter for parse is table with void elements. the OPF format has no + -- void elements, so it needs to be empty, otherwise we may get parsing error because of + -- element, which is included in the default void elements + local opfdom = dom.parse(content, {}) local items = opfdom:query_selector("manifest item") local ref = {} local times = {} @@ -109,23 +113,136 @@ local function add_media_overlays(content) end +local function remove_spurious_TOC_elements(tocdom) + local function count_child_elements(el) + -- count children elements of the current element + local count = 0 + for _, curr_el in ipairs(el:get_children()) do + if curr_el:is_element() then count = count + 1 end + end + return count + end + -- modify the TOC to comply to epubcheck tests + -- add a blank
  • to empty
      + for _, el in ipairs(tocdom:query_selector("ol")) do + if count_child_elements(el) == 0 then + el:remove_node() + -- local newli = el:create_element("li") + -- local newspan = newli:create_element("span") + -- newli:add_child_node(newspan) + -- el:add_child_node(newli) + end + end + -- place child elements of the
    1. elements to an element, epubcheck reports + -- error for text nodes that are direct child of
    2. + for _, el in ipairs(tocdom:query_selector("li")) do + local text = {} + local link -- save the element + for i, child in ipairs(el._children) do + if child:is_text() then + -- trim spurious spaces + local childtext = child._text:gsub("^%s*",""):gsub("%s*$","") + -- save the text for the later use + table.insert(text, childtext) + child:remove_node() + elseif child:is_element() and child:get_element_name() == "a" then + link = child + table.insert(text, child:get_text()) + end + end + if link then + local content = table.concat(text, " ") + -- remove the original text + link._children = {} + local text = link:create_text_node(content) + link:add_child_node(text) + end + end + return tocdom + +end +local function cleanTOC(content) + -- remove spurious empty elements from the TOC, to make epubcheck happy + -- find the file with TOC ("properties" attribute set to "nav" + local opfdom = dom.parse(content,{}) + for _,item in ipairs(opfdom:query_selector("item")) do + local properties = item:get_attribute("properties") or "" + if properties:match("nav") then + local filename = item:get_attribute("href") + if filename then + filename = outputdir .. "/" .. filename + local f = io.open(filename, "r") + local t = f:read("*all") + f:close() + local tocdom = dom.parse(t) + tocdom = remove_spurious_TOC_elements(tocdom) + f = io.open(filename,"w") + f:write(tocdom:serialize()) + f:close() + end + end + end + + +end + +local function fix_properties(content) + -- some properties should be used only in the element, so we need to move them + -- here from the section + -- because why it should be easy, when you can just make messy specification + -- and of course my old code is extremely messy as well. + if content:match("page%-spread%-") or content:match("rendition") then + local spread_ids = {} + local opfdom = dom.parse(content,{}) + local update_properties = function(id, s) + local current_val = spread_ids[id] or {} + current_val[#current_val + 1] = s + spread_ids[id] = current_val + return "" + end + for _,item in ipairs(opfdom:query_selector("manifest item")) do + local properties = item:get_attribute "properties" + if properties then + local id = item:get_attribute "id" + properties = properties:gsub("(page%-spread%-[^%s]+)", function(s) return update_properties(id,s) end) + properties = properties:gsub("(rendition%:[^%s]+)", function(s) return update_properties(id,s) end) + -- properties attribute cannot be empty, we must disable it if + -- it doesn't contain anything after removing of the page spread + if properties:match("^%s*$") then properties = nil + end + item:set_attribute("properties", properties) + end + end + for _, item in ipairs(opfdom:query_selector("spine itemref")) do + local idref = item:get_attribute("idref") + local spread = spread_ids[idref] + if spread then + item:set_attribute("properties", table.concat(spread, " ")) + end + end + return opfdom:serialize() + end + return content +end + local function cleanOPF() -- in epub3, there must be table of contents -- if there is no toc in the document, we must add generic one local opf = "content.opf" local f = io.open(opf,"r") if not f then - print("Cannot open "..opf .. " for toc searching") + log:info("Cannot open "..opf .. " for toc searching") return nil end local content = f:read("*all") f:close() if content:find "properties[%s]*=[%s]*\"[^\"]*nav" then - print "TOC nav found" + log:info "TOC nav found" + cleanTOC(content) else - print "no TOC, using generic one" + log:info "no TOC, using a generic one" local inputfile = input .. "." .. ext - print("Main file name", inputfile) + log:info("Main file name: ".. inputfile) -- write toc file local toc_name = "generic_toc" .."."..ext local f = io.open(outputdir .. "/" .. toc_name, "w") @@ -142,6 +259,7 @@ local function cleanOPF() content = eb.remove_empty_guide(content) content = add_media_overlays(content) + content = fix_properties(content) f = io.open(outputdir .. "/" ..opf,"w") f:write(content) f:close() @@ -152,6 +270,7 @@ end function writeContainer() --local ret = eb.writeContainer() + log:info "write container" eb.make_opf() cleanOPF() local ret = eb.pack_container() diff --git a/Master/texmf-dist/scripts/tex4ebook/exec_mobi.lua b/Master/texmf-dist/scripts/tex4ebook/exec_mobi.lua index 11871ade769..ce10b6da760 100755 --- a/Master/texmf-dist/scripts/tex4ebook/exec_mobi.lua +++ b/Master/texmf-dist/scripts/tex4ebook/exec_mobi.lua @@ -1,6 +1,7 @@ module(...,package.seeall) local eb = require("exec_epub") local ebookutils = require("mkutils") +local log = logging.new "exec_mobi" function prepare(params) return eb.prepare(params) @@ -14,7 +15,7 @@ function writeContainer() local ret = eb.writeContainer() -- convert the epub file to mobi local epubpath = eb.basedir .. "/" .. eb.outputfile - print("Pack mobi "..os.execute("kindlegen " .. epubpath)) + log:info("Pack mobi "..os.execute("kindlegen " .. epubpath)) -- find the mobi filename local mobifile = epubpath:gsub("epub$", "mobi") local mobidist = eb.destdir .. eb.outputfile:gsub("epub$", "mobi") diff --git a/Master/texmf-dist/scripts/tex4ebook/tex4ebook b/Master/texmf-dist/scripts/tex4ebook/tex4ebook index a95bf44c236..731607fc5b4 100755 --- a/Master/texmf-dist/scripts/tex4ebook/tex4ebook +++ b/Master/texmf-dist/scripts/tex4ebook/tex4ebook @@ -5,6 +5,9 @@ kpse.set_program_name("luatex") require("lapp-mk4") -- require("ebookutils") +logging = require "make4ht-logging" +if os.type == "windows" then logging.use_colors = false end +local log = logging.new("tex4ebook") local ebookutils = require "mkutils" local mkparams = require "mkparams" local mk_config = require "make4ht-config" @@ -24,39 +27,37 @@ local output_formats={epub=true,mobi=true,epub3=true} local executor=nil local tidy = false local include_fonts = false + +-- message used for option parsing by the mkparams lib local arg_message = [[ tex4ebook - ebook generation support for LaTeX Usage: tex4ebook [switches] filename ["tex4ht.sty op."] ["tex4ht op."] ["t4ht op"] ["latex op"] + -a,--loglevel (default status) Set log level. + possible values: debug, info, status, warning, error, fatal -c,--config (default xhtml) Custom config file -d,--output-dir (default nil) Output directory -e,--build-file (default nil) If build file is different than `filename`.mk4 -f,--format (default epub) Output format. Supported values: epub, epub3, mobi -h,--help Display help message + -j,--jobname (default nil) Set the jobname -l,--lua Use lualatex for document compilation -m,--mode (default default) Switch which can be used in the makefile -r,--resolution (default 167) -s,--shell-escape Enable shell escape in htlatex run -t,--tidy Run html tidy on html output. May result in wrong spacing! - -v,--version Display version number -x,--xetex Use xelatex for document compilation + -v,--version Display version number + ]] - (string) Input file name - -Positional optional argumens: - ["tex4ht.sty op."] Additional parameters for tex4ht.sty - ["tex4ht op."] Options for tex4ht command - ["t4ht op"] Options for t4ht command - ["latex op"] Additional options for LaTeX +-- options for the template used by mkparams to generate the help message +local mkparams_parameters = { + progname = "tex4ebook", + issue_tracker = "https://github.com/michal-h21/tex4ebook/issues" +} -Documentation: https://tug.org/applications/tex4ht/mn.html -Issue tracker for tex4ht bugs: https://puszcza.gnu.org.ua/bugs/?group=tex4ht -Issue tracker for tex4ebook bugs: https://github.com/michal-h21/tex4ebook/issues -]] - --- This option is no longer available, all files must be unicode --- -u,--utf8 -local args=lapp(arg_message) +-- process the command line arguments +local args = mkparams.get_args(mkparams_parameters, arg_message) if args["filename"] == nil and args["version"] == false then print(arg_message) @@ -66,7 +67,7 @@ else end if args.version then - print "tex4ebook v0.2c" + print "tex4ebook v0.3" return end @@ -85,11 +86,6 @@ if args["include-fonts"] then include_fonts = true end --- local mathml = "," --- if args["mathml"] then --- -- mathml = ",mathml," --- tex4ht_sty_par = tex4ht_sty_par .. ",mathml" --- end local mode = args.mode or "default" if os.type=="unix" then @@ -109,25 +105,7 @@ if args.tidy then else tidy = false end --- Env file copying - ---[[if not ebookutils.file_exists("tex4ht.env") then -local env_file = kpse.find_file("epub2.env") -ebookutils.copy_filter(env_file,"tex4ht.env",function(s) return s % { -move = move_cmd, -copy = copy_cmd, -resolution = args.resolution -} end) -end--]] - ---print ("nazdar ${world}" % {world="svete"}) ---print(args.config) - -local input = ebookutils.remove_extension(input_file) -local config=ebookutils.remove_extension(args.config) --- local tex4ht_sty_par = config ..tex4ht_sty_par..","+args.format ---local sty_args = args[2] and ", " .. args[2] or "" local sty_args = "" if args[2] then sty_args = "," .. args[2] @@ -138,12 +116,17 @@ local t4ht_par = t4ht_par + args[4] local latex_par = latex_par + args[5] -- use default parameter processing from make4ht --- add some needed args +-- add some needed args (is it useful anymore?) args.filename=input_file + +-- let make4ht handle the common options local params = mkparams.process_args(args) +local input = params.input +log:status("Conversion started") +log:status("Input file: ".. params.tex_file) -local output_format = params.output_format --args.format +local output_format = params.output_format -- use epub as default output_format output_format = output_format or "epub" local extensions = ebookutils.load_extensions(params.extensions, output_format) @@ -152,7 +135,7 @@ local oldparams = { -- htlatex=latex_cmd -- ,input=input -- ,tex_file = tex_file - format= output_format + format = output_format -- ,latex_par=latex_par -- ,tex4ht_sty_par=tex4ht_sty_par -- ,tex4ht_par=tex4ht_par @@ -162,7 +145,6 @@ local oldparams = { ,tidy = tidy ,include_fonts = include_fonts ,resolution=args.resolution - ,mathml=mathml ,packages="\\RequirePackage{tex4ebook}" } @@ -175,25 +157,15 @@ params.tex4ht_sty_par=params.tex4ht_sty_par .. ",charset=utf-8,"..output_format params.tex4ht_par= " -cmozhtf -utf8" .. params.tex4ht_par -if output_formats[output_format] then - executor=require("exec_"..output_format) - params=executor.prepare(params) - if #extensions > 0 then - params = ebookutils.extensions_prepare_parameters(extensions,params) - end -else - print("Unknown output format: "..output_format) - return -end -- find tex4ebook configuration file local configname = "tex4ebook" local conffile = mk_config.find_config(configname) or mk_config.find_xdg_config(configname) if conffile then - print("Using configuration file: " .. conffile) + log:info("Using configuration file: " .. conffile) ebookutils.load_config(params, conffile) else - print "No conffile" + log:info "No conffile" end local build_file = input.. ".mk4" @@ -205,8 +177,20 @@ end local config_file = ebookutils.load_config(params, build_file) params["config_file"] = config_file + + +if output_formats[output_format] then + executor=require("exec_"..output_format) + params=executor.prepare(params) + if #extensions > 0 then + params = ebookutils.extensions_prepare_parameters(extensions,params) + end +else + log:error("Unknown output format: "..output_format) + return +end --config_file.Make:run() -print("${htlatex} ${input} \"${tex4ht_sty_par}\" \"${tex4ht_par}\" \"${t4ht_par}\" \"${latex_par}\"" % params) +-- print("${htlatex} ${input} \"${tex4ht_sty_par}\" \"${tex4ht_par}\" \"${t4ht_par}\" \"${latex_par}\"" % params) if #extensions > 0 then config_file.Make = ebookutils.extensions_modify_build(extensions, config_file.Make) end @@ -214,4 +198,5 @@ end executor.run(input,params) executor.writeContainer() executor.clean() +log:status("Conversion finished") --print(args[1]) diff --git a/Master/texmf-dist/tex/latex/tex4ebook/tex4ebook.4ht b/Master/texmf-dist/tex/latex/tex4ebook/tex4ebook.4ht index e4233fc55a4..e623ac8bc24 100644 --- a/Master/texmf-dist/tex/latex/tex4ebook/tex4ebook.4ht +++ b/Master/texmf-dist/tex/latex/tex4ebook/tex4ebook.4ht @@ -178,6 +178,10 @@ href="#1" media-type="\a:CoverMimeType" />}} \patch:sec:register{likesection} \patch:sec:register{likesubsection} \patch:sec:register{likesubsubsection} +\patch:sec:register{appendix} +\patch:sec:register{appendixsec} +\patch:sec:register{appendixsubsec} +\patch:sec:register{appendixsubsubsec} \:CheckOption{epub} \if:Option @@ -264,7 +268,7 @@ href="#1" media-type="\a:CoverMimeType" />}} \def\tmp:processlevel#1{ \bgroup \csxdef{tmp:resettoclevels#1}{} - \def\do##1{\csxdef{tmp:resettoclevels#1}{\csname tmp:resettoclevels#1\endcsname ##1,like##1,}} + \def\do##1{\csxdef{tmp:resettoclevels#1}{\csname tmp:resettoclevels#1\endcsname ##1,like##1,\appendix:type{##1}}} \def\tmp:parse##1#1##2\nil{ \docsvlist{#1##2} } @@ -273,6 +277,8 @@ href="#1" media-type="\a:CoverMimeType" />}} \navsection{#1}{\csname tmp:resettoclevels#1\endcsname} % define hierarchy also for starred sections \navsection{like#1}{\csname tmp:resettoclevels#1\endcsname} + % and for appendices + \ifcsdef{appendix:type:#1}{\navsection{\csuse{appendix:type:#1}}{\csname tmp:resettoclevels#1\endcsname}}{} } % define children levels for each sectioning level @@ -292,6 +298,14 @@ href="#1" media-type="\a:CoverMimeType" />}} \ifx\:temp\z@ro\else#1\fi } +% to create correct hiearchical NCX for appendices, we register corresponding appendix types +% for various section types +\def\appendix:type#1{\ifcsdef{appendix:type:#1}{\csuse{appendix:type:#1},}{}} +\def\def:appendix:type#1#2{\csdef{appendix:type:#1}{#2}} +\def:appendix:type{chapter}{appendix} +\def:appendix:type{section}{appendixsec} +\def:appendix:type{subsection}{appendixsubsec} +\def:appendix:type{subsubsection}{appendixsubsubsec} % \def\ncx:head{% @@ -312,7 +326,7 @@ href="#1" media-type="\a:CoverMimeType" />}} % define toc levels which should be included in the NCX file \NewConfigure{resettoclevels}{1} -\Configure{resettoclevels}{part,appendix,chapter,appendixsec,section,appendixsubsec,subsection,appendixsubsubsec,subsubsection,paragraph} +\Configure{resettoclevels}{part,chapter,section,subsection,subsubsection,paragraph} \def\:tempa{% \EndP @@ -369,6 +383,8 @@ href="#1" media-type="\a:CoverMimeType" />}} \def\print:opf:scheme#1{ opf:scheme="#1"} \def\:tempa{% +% French language definition makes the : character active, which can cause compilation error +\ifdefined\XeTeXcharclass\XeTeXcharclass `\: \z@\fi% \bgroup \setcounter{tocdepth}{3} % Basic sctructure of the opf file diff --git a/Master/texmf-dist/tex/latex/tex4ebook/tex4ebook.sty b/Master/texmf-dist/tex/latex/tex4ebook/tex4ebook.sty index c3726ee1671..27a46b756ec 100644 --- a/Master/texmf-dist/tex/latex/tex4ebook/tex4ebook.sty +++ b/Master/texmf-dist/tex/latex/tex4ebook/tex4ebook.sty @@ -1,10 +1,10 @@ -% Package odsfile. Author Michal Hoftich +% Package tex4ebook. Author Michal Hoftich % This package is subject of LPPL license, version 1.3 \ProvidesPackage{tex4ebook}[2016/03/31 version 0.1d] \RequirePackage{etoolbox} \RequirePackage{kvoptions} \RequirePackage{graphicx} -% Command for generating NCX file. Hard work is odne in the file +% Command for generating NCX file. The hard work is done in the file % tex4ebook.4ht \def\ncxtable{} % Command for generating OPF file @@ -122,18 +122,41 @@ \def\Author{Anonymous} % We need to save values of title, author etc. -%\AtBeginDocument{ -\let\origtitle\title -\renewcommand\title[1]{% - \origtitle{#1} - \let\Title\@title +\let\tf@orig@title\title + +% normal \title +\newcommand\tf@title[1]{% + \tf@orig@title{#1}% + \let\Title\@title% +} + +% \title with optional argument +\newcommand\tf@opttitle[2][]{% + \tf@orig@title[#1]{#2}% + \let\Title\@title% +} + +% some classes (amsart) define \title command with optional argument +\renewcommand\title{% +\@ifnextchar[\tf@opttitle\tf@title% } -\let\origauthor\author -\renewcommand\author[1]{% - \origauthor{#1}% - \let\Author\@author +\let\tf@orig@author\author +\newcommand\tf@author[1]{% + \tf@orig@author{#1}% + \let\Author\@author% } + +\newcommand\tf@optauthor[2][]{% + \tf@orig@author[#1]{#2}% + \let\Author\@author% +} + +% support optiona argument for \author as well +\renewcommand\author{% +\@ifnextchar[\tf@optauthor\tf@author% +} + %\let\Author\@author \let\Date\today \let\origdate\date -- cgit v1.2.3