From 3479a92321ed7fe7e2133d3daec2d4f5fd53fbc6 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Sat, 2 Nov 2019 03:01:48 +0000 Subject: CTAN sync 201911020301 --- support/make4ht/README | 1168 +++++++++------- support/make4ht/changelog.tex | 258 +++- support/make4ht/domfilters/make4ht-aeneas.lua | 3 +- support/make4ht/domfilters/make4ht-collapsetoc.lua | 198 +++ .../make4ht/domfilters/make4ht-joincharacters.lua | 8 +- support/make4ht/domfilters/make4ht-joincolors.lua | 3 +- .../make4ht/domfilters/make4ht-odtimagesize.lua | 3 +- support/make4ht/domfilters/make4ht-odtpartable.lua | 9 + support/make4ht/domfilters/make4ht-tablerows.lua | 58 + support/make4ht/extensions/common_domfilters.lua | 29 +- support/make4ht/extensions/dvisvgm_hashes.lua | 7 +- support/make4ht/extensions/preprocess_input.lua | 108 ++ support/make4ht/extensions/staticsite.lua | 3 +- support/make4ht/extensions/tidy.lua | 3 +- support/make4ht/filters/make4ht-domfilter.lua | 7 +- .../filters/make4ht-entities-to-unicode.lua | 8 +- support/make4ht/filters/make4ht-mathjaxnode.lua | 11 +- support/make4ht/filters/make4ht-staticsite.lua | 6 +- support/make4ht/filters/make4ht-svg-height.lua | 4 +- support/make4ht/formats/docbook.lua | 3 +- support/make4ht/formats/odt.lua | 23 +- support/make4ht/make4ht | 32 +- support/make4ht/make4ht-doc.pdf | Bin 110638 -> 126657 bytes support/make4ht/make4ht-doc.tex | 17 +- support/make4ht/make4ht-errorlogparser.lua | 97 ++ support/make4ht/make4ht-htlatex.lua | 75 ++ support/make4ht/make4ht-indexing.lua | 190 +++ support/make4ht/make4ht-lib.lua | 29 +- support/make4ht/make4ht-logging.lua | 105 ++ support/make4ht/make4ht-xtpipes.lua | 15 +- support/make4ht/mkparams.lua | 134 +- support/make4ht/mkutils.lua | 131 +- support/make4ht/readme.tex | 1401 ++++++++++++-------- support/tex4ebook/README | 139 +- support/tex4ebook/changelog.tex | 100 ++ support/tex4ebook/exec_epub.lua | 372 +++--- support/tex4ebook/exec_epub3.lua | 129 +- support/tex4ebook/exec_mobi.lua | 3 +- support/tex4ebook/readme.tex | 137 +- support/tex4ebook/tex4ebook | 99 +- support/tex4ebook/tex4ebook-doc.pdf | Bin 73368 -> 78095 bytes support/tex4ebook/tex4ebook.4ht | 20 +- support/tex4ebook/tex4ebook.sty | 45 +- 43 files changed, 3590 insertions(+), 1600 deletions(-) create mode 100644 support/make4ht/domfilters/make4ht-collapsetoc.lua create mode 100644 support/make4ht/domfilters/make4ht-odtpartable.lua create mode 100644 support/make4ht/domfilters/make4ht-tablerows.lua create mode 100644 support/make4ht/extensions/preprocess_input.lua create mode 100644 support/make4ht/make4ht-errorlogparser.lua create mode 100644 support/make4ht/make4ht-htlatex.lua create mode 100644 support/make4ht/make4ht-indexing.lua create mode 100644 support/make4ht/make4ht-logging.lua (limited to 'support') diff --git a/support/make4ht/README b/support/make4ht/README index a99a108d69..df14833239 100644 --- a/support/make4ht/README +++ b/support/make4ht/README @@ -2,93 +2,209 @@ # Introduction -`make4ht` is a simple build system for `tex4ht`, \TeX\ to XML converter. It provides a command line tool -that drive the conversion process. It also provides a library which can be used to create -customized conversion tools. An example of such conversion tool is -[tex4ebook](https://github.com/michal-h21/tex4ebook) for conversion of \TeX\ to +`make4ht` is a build system for \TeX4ht, \TeX\ to XML converter. It provides a command line tool +that drives the conversion process. It also provides a library that can be used to create +customized conversion tools. An example of such a tool is +[tex4ebook](https://github.com/michal-h21/tex4ebook), a tool for conversion from \TeX\ to ePub and other e-book formats. -## How it works +The basic conversion from \LaTeX\ to `HTML` using `make4ht` can be executed using the following command: -### The issues with default `tex4ht` conversion commands + $ make4ht filename.tex +It will produce a file named `filename.html` if the compilation goes without fatal errors. -`tex4ht` system supports several output formats, most notably `XHTML`, `HTML 5` and `ODT`. -The conversion can be invoked using several commands. These commands invoke LaTeX\ or Plain TeX -with special instructions to load `tex4ht.sty` package. The \TeX\ run produces special `DVI` file -which contains the code for desired output format. Produced `DVI` file is then processed and -desired output files are created. -The basic command provided by `tex4ht` is named `htlatex`. It compiles \LaTeX\ +# Command line options {#clioptions} +\label{sec:clioptions} + + make4ht - build system for TeX4ht + Usage: + make4ht [options] 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 + -b,--backend (default tex4ht) Backend used for xml generation. + possible values: tex4ht or lua4ht + -c,--config (default xhtml) Custom config file + -d,--output-dir (default "") Output directory + -e,--build-file (default nil) If build filename is different + than `filename`.mk4 + -f,--format (default nil) Output file format + -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 + -n,--no-tex4ht Disable DVI file processing with tex4ht command + -s,--shell-escape Enables running external programs from LaTeX + -u,--utf8 For output documents in utf8 encoding + -x,--xetex Use xelatex for document compilation + -v,--version Print version number + (string) Input filename + + +It is still possible to invoke `make4ht` in the same way as is invoked `htlatex`: + + $ make4ht filename "customcfg, charset=utf-8" "-cunihtf -utf8" "-dfoo" + +Note that this will not use `make4ht` routines for the output directory handling. +See section \ref{sec:output-dir} for more information about this issue. +To use these routines, change the previous listing to: + + $ make4ht -d foo filename "customcfg, charset=utf-8" "-cunihtf -utf8" + +This call has the same effect as the following: + + $ make4ht -u -c customcfg -d foo filename + + +Output directory doesn't have to exist, it will be created automatically. +Specified path can be relative to the current directory, or absolute: + + $ make4ht -d use/current/dir/ filename + $ make4ht -d ../gotoparrentdir filename + $ make4ht -d ~/gotohomedir filename + $ make4ht -d c:\documents\windowspathsareworkingtoo filename + +The short options that don't take parameters can be collapsed: + + + $ make4ht -ulc customcfg -d foo filename + + +To pass output from the other commands to `make4ht` use the `-` character as a +filename. It is best to use this feature together with the `--jobname` or `-j` +option. + + $ cat hello.tex | make4ht -j world - + +By default, `make4ht` tries to be quiet, so it hides most of the command line +messages and the output from the executed commands. It will display only status +messages, warnings and errors. The logging level can be selected using the +`--loglevel` or `-a` options. If the compilation fails, it may be useful to display more +information using the `info` or `debug` levels. + + + $ make4ht -a debug faulty.tex + + + +# Why `make4ht`? -- `htlatex` issues + + +\TeX4ht\ system supports several output formats, most notably `XHTML`, `HTML 5` +and `ODT`, but it also supports `TEI` or `Docbook`. + +The conversion can be invoked using several scripts, which are distributed with \TeX4ht. +They differ in parameters passed to the underlying commands. + +These scripts invoke \LaTeX\ or Plain \TeX\ with special instructions to load +the `tex4ht.sty` package. The \TeX\ run produces a special `DVI` file +that contains the code for the desired output format. The produced `DVI` file +is then processed using the `tex4ht` command, which in conjunction with the +`t4ht` command produces the desired output files. + +## Passing command line arguments + +The basic conversion script provided by \TeX4ht\ system is named `htlatex`. It compiles \LaTeX\ files to `HTML` with this command sequence: - latex $latex_options 'code for loading tex4ht.sty \input{filename}' - latex $latex_options 'code for loading tex4ht.sty \input{filename}' - latex $latex_options 'code for loading tex4ht.sty \input{filename}' - tex4ht $tex4ht_options filename - t4ht $t4ht_options filename + $ latex $latex_options 'code for loading tex4ht.sty \input{filename}' + $ latex $latex_options 'code for loading tex4ht.sty \input{filename}' + $ latex $latex_options 'code for loading tex4ht.sty \input{filename}' + $ tex4ht $tex4ht_options filename + $ t4ht $t4ht_options filename The options for various parts of the system can be passed on the command line: - htlatex filename "tex4ht.sty options" "tex4ht_options" "t4ht_options" "latex_options" + $ htlatex filename "tex4ht.sty options" "tex4ht_options" "t4ht_options" "latex_options" For basic `HTML` conversion it is possible to use the most basic invocation: - htlatex filename.tex + $ htlatex filename.tex -It can be much more involved for `HTML 5` output in `UTF-8` encoding: +It can be much more involved for the `HTML 5` output in `UTF-8` encoding: - htlatex filename.tex "xhtml,html5,charset=utf-8" "-cmozhtf -utf8" + $ htlatex filename.tex "xhtml,html5,charset=utf-8" " -cmozhtf -utf8" `make4ht` can simplify it: - make4ht -uf html5 filename.tex + $ make4ht -u filename.tex + +The `-u` option requires the `UTF-8` encoding. `HTML 5` is used as the default +output format by `make4ht`. -Another issue is the fixed compilation order and hard-coded number of LaTeX invocations. +More information about the command line arguments can be found in section +\ref{sec:clioptions}. -When you need to run a program which interact with LaTeX, such as `Makeindex` -or `Bibtex`, you need to create a new script based on `htlatex`, or run -`htlatex` twice, which means that LaTeX will be invoked six times. -This can lead to significantly long compilation times. `make4ht` provides build files and -extensions, which can be used for interaction with external tools. -It is also possible to have several compilation modes. When you just add new text to a document, -which doesn't contain cross-references, don't add new stuff to the table of contents, etc., -it is possible to use the `draft` mode which will invoke LaTeX only once. It -can save quite a lot of the compilation time: +## Compilation sequence - make4ht -um draft -f html5 filename.tex +`htlatex` has a fixed compilation order and a hard-coded number of \LaTeX\ invocations. -There are also issues with a behaviour of the `t4ht` application. It reads file -`filename.lg`, generated by `tex4ht`, where are instructions about generated -files, `CSS` instructions, calls to external applications, instructions for -image conversions etc. It can be instructed to copy generated files to some -output directory, but it doesn't preserve directory structure, so when you -have images in a subdirectory, they will be copied to the output directory. +It is not possible to execute additional commands during the compilation. +When we want to run a program that interacts with \LaTeX, such as `Makeindex` +or `Bibtex`, we have two options. The first option is to create a new script based on +`htlatex` and add the wanted commands to the modified script. The second option +is to execute `htlatex`, then the additional and then `htlatex` again. The +second option means that \LaTeX\ will be invoked six times, as each call to +`htlatex` executes three calls to \LaTeX. This can lead to significantly long +compilation times. + +`make4ht` provides a solution for this issue using a build file, or extensions. +These can be used for interaction with external tools. + +`make4ht` also provides compilation modes, which enables to select commands that +should be executed using a command line option. + +There is a built-in `draft` mode, which invokes \LaTeX\ only once, instead of +the default three invocations. It is useful for the compilations of the +document before its final stage, when it is not important that all +cross-references work. It can save quite a lot of the compilation time: + + $ make4ht -um draft filename.tex + +More information about the build files can be found in section \ref{sec:buildfiles}. + +## Handling of the generated files +\label{sec:output-dir} + +There are also issues with the behavior of the `t4ht` application. It reads the +`.lg` file generated by the `tex4ht` command. This file contains +information about the generated files, `CSS` instructions, calls to the external +applications, instructions for image conversions, etc. + + +`t4ht` can be instructed to copy the generated files to an output directory, but +it doesn't preserve the directory structure. When the images are placed in a +subdirectory, they will be copied to the output directory, losing the directory structure. Links will be pointing to a non-existing subdirectory. The following command should copy all output files to the correct destinations. - make4ht -d outputdir filename.tex + $ make4ht -d outputdir filename.tex + +## Image conversion and post-processing of the generated files -The image conversion is configured in the -[env file](http://www.tug.org/applications/tex4ht/mn35.html#index35-73001), -which has really strange syntax based and the rules are -[os dependent](http://www.tug.org/applications/tex4ht/mn-unix.html#index27-69005). +\TeX4ht\ can convert parts of the document to images. This is useful +for diagrams or complicated math, for example. + +By default, the image conversion is configured in a +[`.env` file](http://www.tug.org/applications/tex4ht/mn35.html#index35-73001). +It has a bit of strange syntax, with +[operating system dependent](http://www.tug.org/applications/tex4ht/mn-unix.html#index27-69005) rules. `make4ht` provides simpler means for the image conversion in the build files. +It is possible to change the image conversion parameters without a need to modify the `.env` file. +The process is described in section \ref{sec:imageconversion}. + +It is also possible to post-process the generated output files. The post-processing can be done +either using external programs such as `XSLT` processors and `HTML Tidy` or +using `Lua` functions. More information can be found in section \ref{sec:postprocessing}. -With `make4ht` build files, we have simple mean to fix these issues. We can -change image conversion parameters without the need to modify the `env file`, -or execute actions on the output files. These actions can be either external -programs such as `xslt` processors or `HTML tidy` or `Lua` functions. -The idea is to make system controlled by a build file. Because `Lua` -interpreter is included in modern TeX distributions and `Lua` is ideal language -for such task, it was chosen as language in which the build scripts are written. # Output file formats and extensions -The default output format used by `make4ht` is `html5`. Different +The default output format used by `make4ht` is `html5`. A different format can be requested using the `--format` option. Supported formats are: - `xhtml` @@ -107,22 +223,10 @@ may post-process the output files or request additional commands for the compila The extensions can be enabled or disabled by appending `+EXTENSION` or `-EXTENSION` after the output format name: - make4ht -uf html5+tidy filename.tex + $ make4ht -uf html5+tidy filename.tex Available extensions: -latexmk_build - -: use `Latexmk` for \LaTeX\ compilation. - -tidy - -: clean the `HTML` files using the `tidy` command. - -dvisvgm_hashes - -: efficient generation of SVG pictures using Dvisvgm. It can utilize -multiple processor cores and generates only changed images. common\_filters @@ -131,13 +235,22 @@ common\_filters common\_domfilters : clean the HTML file using DOM filters. It is more powerful than -`common_filters`. Used DOM filters are `fixinlines`, `idcolons` and -`joincharacters`. +`common_filters`. Used DOM filters are `fixinlines`, `idcolons`, +`joincharacters`, and `tablerows`. + +dvisvgm\_hashes + +: efficient generation of SVG pictures using Dvisvgm. It can utilize +multiple processor cores and generates only changed images. join\_colors : load the `joincolors` domfilter for all HTML files. +latexmk\_build + +: use [Latexmk](https://ctan.org/pkg/latexmk?lang=en) for the \LaTeX\ compilation. + mathjaxnode : use [mathjax-node-page](https://github.com/pkra/mathjax-node-page/) to @@ -146,21 +259,35 @@ mathjaxnode odttemplate -: automatically load the `odttemplate` filter. +: it automatically loads the `odttemplate` filter (page \pageref{sec:odttemplate}). + +preprocess\_input + +: compilation of the formats + supported by [Knitr](https://yihui.name/knitr/) (`.Rnw`, `.Rtex`, `.Rmd`, `.Rrst`) + and also Markdown and reStructuredText formats. It requires +[R](https://www.r-project.org/) + [Knitr](https://yihui.name/knitr/) +installation, it requires also [Pandoc](https://pandoc.org/) for formats based on Markdown or +reStructuredText. staticsite -: build the document in form suitable for static site generators like [Jekyll](https://jekyllrb.com/). +: build the document in a form suitable for static site generators like [Jekyll](https://jekyllrb.com/). + +tidy + +: clean the `HTML` files using the `tidy` command. # Build files +\label{sec:buildfiles} -`make4ht` supports build files. These are `Lua` scripts which can adjust -the build process. You can request external applications like `bibtex` or `makeindex`, +`make4ht` supports build files. These are `Lua` scripts that can adjust +the build process. They can request external applications like `BibTeX` or `Makeindex`, pass options to the commands, modify the image conversion process, or post-process the generated files. `make4ht` tries to load default build file named as `filename + .mk4 extension`. -You can choose different build file with `-e` or `--build-file` command line +It is possible to select a different build file with `-e` or `--build-file` command line option. Sample build file: @@ -168,185 +295,205 @@ Sample build file: Make:htlatex() Make:match("html$", "tidy -m -xml -utf8 -q -i ${filename}") -`Make:htlatex()` is preconfigured command for calling LaTeX with `tex4ht` -loaded on the input file. In this case, it will be called one time. After -compilation, the `tidy` command is executed on the output `HTML` file. +`Make:htlatex()` is preconfigured command for calling \LaTeX\ with the `tex4ht.sty` package +loaded. In this example, it will be executed only once. After the +compilation, the `tidy` command is executed on the output `HTML` files. -Note that you don't have to call `tex4ht` and `t4ht` commands explicitly in the +Note that it is not necessary to call `tex4ht` and `t4ht` commands explicitly in the build file, they are called automatically. ## User commands -You can add more commands like `Make:htlatex` using `Make:add` command: +It is possible to add more commands like `Make:htlatex` using the `Make:add` command: - Make:add("name", "command", {parameters}, repetition) + Make:add("name", "command", {settings table}, repetition) -The `name` and `command` parameters are required, rest of the parameters are optional. +This defines the `name` command, which can be then executed using `Make:name()` +command in the build file. -This defines `name` command, which can be then executed as `Make:name()` command. +The `name` and `command` parameters are required, the rest of the parameters are optional. -### Provided commands +The defined command receives a table with settings as a parameter at the call time. +The default settings are provided by `make4ht`. Additional settings can be +declared in the `Make:add` commands, user can also override the default settings +when the command is executed in the build file: -`Make:htlatex` + Make:name({hello="world"}) -: One call to TeX engine with special configuration for `tex4ht` loading. +More information about settings, including the default settings provided by +`make4ht`, can be found in section \ref{sec:settings} on page +\pageref{sec:settings}. -`Make:latexmk` -: Use `Latexmk` for the document compilation. `tex4ht` will be loaded automatically. +### The `command` function +\label{sec:commandfunction} -`Make:tex4ht` +The `command` parameter can be either a string template or function: -: Process the `DVI` file and creates the output files. + Make:add("text", "echo hello, input file: ${input}") -`Make:t4ht` +The template can get a variable value from the parameters table using a +`${var_name}` placeholder. Templates are executed using the operating system, so +they should invoke existing OS commands. -: Creates the CSS file. -### Command function -The `command` parameter can be either string template or function: - Make:add("text", "echo hello, input file: ${input}") - Make:add("function", function(params) +### The `settings table` table + + +The `settings table` parameter is optional. If it is present, it should be +a table with new settings available in the command. It can also override the default +`make4ht` settings for the defined command. + + Make:add("sample_function", function(params) for k, v in pairs(params) do print(k..": "..v) end, {custom="Hello world"} ) -The template can get variable value from the parameters table using a -`${var_name}` placeholder. Templates are executed using operating system, so -they should invoke existing OS commands. Function commands may execute system -commands using `os.execute` function. - - -### Parameters table -`parameters` parameter is optional, it can be table or `nil` value, which -should be used if you want to use the `repetition` parameter, but don't want to -modify the parameters table. +### Repetition -The table with default parameters is passed to all commands, they can be accessed from command functions -or templates. When you specify your own parameters in the command definition, these additional -parameters are added to the default parameters table for this particular -command. You can override the default parameters in the parameters table. +The `repetition` parameter specifies the maximum number of executions of the +particular command. This is used for instance for `tex4ht` and `t4ht` +commands, as they should be executed only once in the compilation. They would +be executed multiple times when they are included in the build file, as they +are called by `make4ht` by default. Because these commands allow only one +`repetition`, the second execution is blocked. +### Expected exit code +You can set the expected exit code from a command with a `correct_exit` key in the +settings table. The compilation will be terminated when the command returns a +different exit code. -The default parameters are following: + Make:add("biber", "biber ${input}", {correct_exit=0}) -`htlatex` +Commands that execute lua functions can return the numerical values using the `return` statement. -: used compiler -`input` +This mechanism isn't used for \TeX, because it doesn't differentiate between fatal and non-fatal errors. +It returns the same exit code in all cases. Because of this, log parsing is used for a fatal error detection instead. +Error code value `1` is returned in the case of a fatal error, `0` is used +otherwise. The `Make.testlogfile` function can be used in the build file to +detect compilation errors in the TeX log file. -: it is output file name in fact -`tex_file` +## Provided commands -: input TeX file +`Make:htlatex` -`latex_par` +: One call to the TeX engine with special configuration for loading of the `tex4ht.sty` package. -: parameters to `latex` +`Make:latexmk` -`packages` +: Use `Latexmk` for the document compilation. `tex4ht.sty` will be loaded automatically. -: insert additional LaTeX code which is inserted before `\documentclass`. - Useful for passing options to packages or additional packages loading +`Make:tex4ht` -`tex4ht_sty_par` +: Process the `DVI` file and create output files. -: parameters to `tex4ht.sty` +`Make:t4ht` -`tex4ht_par` +: Create the CSS file and generate images. -: parameters to the `tex4ht` application +`Make:biber` -`t4ht_par` +: Process bibliography using the `biber` command. -: parameters to the `t4ht` application +`Make:bibtex` -`outdir` +: Process bibliography using the `bibtex` command. -: the output directory +`Make:xindy` -`repetition` +: Generate index using Xindy index processor. -: limit number of command execution. +`Make:makeindex` -`correct_exit` +: Generate index using the Makeindex command. -: expected `exit code` from the command. The compilation will be terminated - if the command `exit code` is different. +`Make:xindex` +: Generate index using the Xindex command. -### Repetition -Repetition is number which specifies a maximal number of executions of the -particular command. This is used for instance for `tex4ht` and `t4ht` -commands, as they should be executed only once in the compilation. They would -be executed multiple times if you include them in the build file because they -are called by `make4ht` by default. Because these commands allow only one -`repetition`, the second execution will be blocked. +## File matches +\label{sec:postprocessing} -### Expected exit code +Another type of action that can be specified in the build file is +`Make:match`. It can be used to post-process the generated files: -You can set the expected exit code from a command with a `correct_exit` key in the -parameters table. The compilation will be stopped when the command returns a -different exit code. + Make:match("html$", "tidy -m -xml -utf8 -q -i ${filename}") -This mechanism isn't used for LaTeX (for all TeX engines and formats, in -fact), because it doesn't differentiate between fatal and non-fatal errors, and -it returns the same exit code in all cases. Log parsing is used because of -that, error code `1` is returned in the case of fatal error, `0` is used -otherwise. The `Make.testlogfile` function can be used in the build file to -detect compilation errors in the TeX log file. +The above example will clean all output `HTML` files using the `tidy` command. -## File matches +The `Make:match` action tests output filenames using a `Lua` pattern matching function. +It executes a command or a function, specified in the second argument, on files +whose filenames match the pattern. -Another type of action which can be specified in the build file is -`match`. It can be called on the generated files: +The commands to be executed can be specified as strings. They can contain +`${var_name}` placeholders, which are replaced with corresponding variables +from the `settings` table. The templating system was described in +subsection \ref{sec:commandfunction}. There is an additional variable +available in this table, called `filename`. It contains the name of the current +output file. - Make:match("html$", "tidy -m -xml -utf8 -q -i ${filename}") -It tests output file names with `Lua` pattern matching and on matched items will -execute a command or a function, specified in the second argument. Commands may be -specified as strings, the templates will be expanded, `${var_name}` placeholders -will be replaced with corresponding variables from the `parameters` table, -described in the previous subsection. One additional variable is available: -`filename`. It contains the name of the current output file. +If a function is used instead, it will get two parameters. The first one is the +current filename, the second one is the `settings` table. -The above example will clean all output `HTML` files using the `tidy` command. + Make:match("html$", function(filename, settings) + print("Post-processing file: ".. filename) + print("Available settings") + for k,v in pairs(settings) + print(k,v) + end + return true + end) -If function is used instead, it will get two parameters. -The first one is a current filename, the second one -table with parameters. +Multiple post-processing actions can be executed on each filename. The Lua +action functions can return an exit code. If the exit code is false, the execution +of the post-processing chain for the current file will be terminated. +### Filters +\label{sec:filters} +To make it easier to post-process the generated files using the `match` +actions, `make4ht` provides a filtering mechanism thanks to the +`make4ht-filter` module. -### Filters +The `make4ht-filter` module returns a function that can be used for the filter +chain building. Multiple filters can be chained into a pipeline. Each filter +can modify the string that is passed to it from the previous filters. The +changes are then saved to the processed file. -Some default `match` actions which can be used are available from the -`make4ht-filter` module. It contains some functions which are useful for -fixing some `tex4ht` bugs or shortcomings. +Several built-in filters are available, it is also possible to create new ones. -Example: +Example that use only the built-in filters: local filter = require "make4ht-filter" local process = filter{"cleanspan", "fixligatures", "hruletohr"} Make:htlatex() + Make:match("html$",process) + +Function `filter` accepts also function arguments, in this case this function +takes file contents as a parameter and modified contents are returned. + +Example with custom filter: + + local filter = require "make4ht-filter" + local changea = function(s) return s:gsub("a","z") end + local process = filter{"cleanspan", "fixligatures", changea} Make:htlatex() Make:match("html$",process) -The `make4ht-filter` module return a function which can be used for the filter -chain building. Multiple filters can be chained, each of them can modify the string -which was modified by the previous filters. The changes are then saved to the -processed file. +In this example, spurious span elements are joined, ligatures are decomposed, +and then all letters "a" are replaced with "z" letters. -Built-in filters are: +Built-in filters are the following: cleanspan @@ -363,19 +510,18 @@ fixligatures hruletohr : `\hrule` commands are translated to series of underscore characters - by `tex4ht`, this filter translate these underscores to `
` elements + by \TeX4ht, this filter translates these underscores to `
` elements entites -: convert prohibited named entities to numeric entities (currently, only - ` `, as it causes validation errors, and `tex4ht` is producing it - sometimes) +: convert prohibited named entities to numeric entities (only + ` ` currently). fix-links : replace colons in local links and `id` attributes with underscores. Some cross-reference commands may produce colons in internal links, which results in - validation error. + a validation error. mathjaxnode @@ -388,36 +534,24 @@ odttemplate : use styles from another `ODT` file serving as a template in the current document. It works for the `styles.xml` file in the `ODT` file. During the compilation, this file is named as `\jobname.4oy`. + \label{sec:odttemplate} staticsite -: create HTML files in format suitable for static site generators such as [Jekyll](https://jekyllrb.com/) +: create HTML files in a format suitable for static site generators such as [Jekyll](https://jekyllrb.com/) svg-height : some SVG images produced by `dvisvgm` seem to have wrong dimensions. This filter tries to set the correct image size. -Function `filter` accepts also function arguments, in this case this function -takes file contents as a parameter and modified contents are returned. - -Example: - - local filter = require "make4ht-filter" - local changea = function(s) return s:gsub("a","z") end - local process = filter{"cleanspan", "fixligatures", changea} - Make:htlatex() - Make:htlatex() - Make:match("html$",process) - -In this example, spurious span elements are joined, ligatures are decomposed, -and then all letters 'a' are replaced with 'z' letters. ### DOM filters -DOM filters use the [`LuaXML`](https://ctan.org/pkg/luaxml) library to modify +DOM filters are variants of filters that use the +[`LuaXML`](https://ctan.org/pkg/luaxml) library to modify directly the XML object. This enables more powerful -operations than the regex based filters from the previous section. +operations than the regex-based filters from the previous section. Example: @@ -431,7 +565,11 @@ Available DOM filters: aeneas : [Aeneas](https://www.readbeyond.it/aeneas/) is a tool for automagical synchronization of text and audio. - This filter modifies the HTML code to support the synchronization. + This filter modifies the HTML code to support synchronization. + +collapsetoc + +: collapse table of contents to contain only top-level sectioning level and sections on the current page. fixinlines @@ -443,296 +581,350 @@ idcolons joincharacters -: join consecutive `` or `` elements. +: join consecutive `` or `` elements. This DOM filter supersedes the `cleanspan` filter. joincolors -: many `` elements with unique `id` attribute are created when \LaTeX\ colors are being used in the document. - CSS rule is added for each of these elements, which may result in - substantial grow of the CSS file. This filter replace these rules with a +: many `` elements with unique `id` attributes are created when \LaTeX\ colors are being used in the document. + A CSS rule is added for each of these elements, which may result in + substantial growth of the CSS file. This filter replaces these rules with a common one for elements with the same color value. odtimagesize : set correct dimensions for images in the ODT format. It is loaded by default for the ODT output. +odtpartable + +: resolve tables nested inside paragraphs, which is invalid in the ODT format. + +tablerows + +: remove spurious rows from HTML tables. + t4htlinks : fix hyperlinks in the ODT format. -### make4ht-aeneas-config package -Companion for the `aeneas` DOM filter is the `make4ht-aeneas-config` plugin. It -can be used to write Aeneas configuration file or execute Aeneas on the -generated HTML files. -Available functions: +## Image conversion +\label{sec:imageconversion} -write\_job(parameters) +It is possible to convert parts of the \LaTeX\ input as pictures. It can be used +for preserving the appearance of math or diagrams, for example. -: write Aenas job configuration to `config.xml` file. See the [Aeneas - documentation](https://www.readbeyond.it/aeneas/docs/clitutorial.html#processing-jobs) - for more information about jobs. +These pictures are stored in a special `DVI` file, which can be processed by +a `DVI` to image commands, such as `dvipng` or `dvisvgm`. -execute(parameters) +This conversion is normally configured in the `tex4ht.env` file. This file +is system dependent and it has quite an unintuitive syntax. +The configuration is processed by the `t4ht` application and the conversion +command is called for all pictures. -: execute Aeneas. +It is possible to disable `t4ht` image processing and configure image +conversion in the build file using the `image` action: -process\_files(parameters) + Make:image("png$", + "dvipng -bg Transparent -T tight -o ${output} -pp ${page} ${source}") -: process the audio and generated subtitle files. +`Make:image` takes two parameters, a `Lua` pattern to match the image name, and +the action. -By default, the `smil` file is created. It is assumed that there is audio file -in `mp3` format named as the TeX file. It is possible to use different formats -and file names using mapping. +Action can be either a string template with the conversion command +or a function that takes a table with parameters as an argument. -The configuration options can be passed directly to the functions or set using -`filter_settings "aeneas-config" {parameters}` function. +There are three parameters: + - `output` - output image filename + - `source` - `DVI` file with the pictures + - `page` - page number of the converted image -Available parameters: +## The `mode` variable +The `mode` variable available in the build process contains +contents of the `--mode` command line option. It can be used to run some commands +conditionally. For example: -lang + if mode == "draft" then + Make:htlatex{} + else + Make:htlatex{} + Make:htlatex{} + Make:htlatex{} + end -: document language. It is interfered from the HTML file, so it is not necessary to set it. +In this example (which is the default configuration used by `make4ht`), +\LaTeX\ is called only once when `make4ht` is called with the `draft` mode: + + make4ht -m draft filename -map +## The `settings` table +\label{sec:settings} -: mapping between HTML, audio and subtitle files. More info bellow. +It is possible to access the parameters outside commands, file matches +and image conversion functions. For example, to convert the document to +the `OpenDocument Format (ODT)`, the following settings can be used. They are +based on the `oolatex` command: -text\_type + settings.tex4ht_sty_par = settings.tex4ht_sty_par ..",ooffice" + settings.tex4ht_par = settings.tex4ht_par .. " ooffice/! -cmozhtf" + settings.t4ht_par = settings.t4ht_par .. " -cooxtpipes -coo " -: type of the input. The `aeneas` DOM filter produces `unparsed` text type. +(Note that it is possible to use the `--format odt` option +which is superior to the previous code. This example is intended just as an +illustration) -id\_sort +There are some functions to simplify access to the settings: -: sorting of id attributes. Default value is `numeric`. +`set_settings{parameters}` -id\_regex +: overwrite settings with values from a passed table -: regular expression to parse the id attributes. +`settings_add{parameters}` -sub\_format +: add values to the current settings -: generated subtitle format. Default `smil`. +`filter_settings "filter name" {parameters}` +: set settings for a filter -Additional parameters for the job configuration file: +`get_filter_settings(name)` -- description -- prefix -- config\_name -- keep\_config +: get settings for a filter +For example, it is possible to simplify the sample from the previous code listings: -It is possible to generate multiple HTML files from the LaTeX source. For -example, `tex4ebook` generates separate file for each chapter or section. It is -possible to set options for each HTML file, in particular names of the -corresponding audio files. This mapping is done using `map` parameter. + settings_add { + tex4ht_sty_par =",ooffice", + tex4ht_par = " ooffice/! -cmozhtf", + t4ht_par = " -cooxtpipes -coo " + } -Example: +Settings for filters and extensions can be set using `filter_settings`: - filter_settings "aeneas-config" { - map = { - ["sampleli1.html"] = {audio_file="sample.mp3"}, - ["sample.html"] = false - } + + filter_settings "test" { + hello = "world" } -Table keys are the configured file names. It is necessary to insert them as -`["filename.html"]`, because of Lua syntax rules. +These settings can be retrieved in the extensions and filters using the `get_filter_settings` function: -This example maps audio file `sample.mp3` to a section subpage. The main HTML -file, which may contain title and table of contents doesn't have a -corresponding audio file. + function test(input) + local options = get_filter_settings("test") + print(options.hello) + return input + end + +### Default settings -Filenames of the sub files corresponds to the chapter numbers, so they are not -stable when a new chapter is added. It is possible to request file names -interfered from the chapter titles using the `sec-filename` option or `tex4ht`. +The default parameters are the following: -Available `map` options: +`htlatex` +: used \TeX\ engine -audio\_file +`input` -: the corresponding audio file +: content of `\jobname`, see also the `tex_file` parameter. -sub\_file +`interaction` -: name of the generated subtitle file +: interaction mode for the \TeX\ engine. The default value is `batchmode` to + suppress user input on compilation errors. It also suppresses most of the \TeX\ + compilation log output. Use the `errorstopmode` for the default behavior. -The following options are same as their counter-parts from the main parameters table and generally don't need to be set: +`tex_file` -- prefix -- file\_desc -- file\_id -- text\_type -- id\_sort -- id\_prefix -- sub\_format +: input \TeX\ filename +`latex_par` -Full example: +: command line parameters to the \TeX\ engine +`packages` - local domfilter = require "make4ht-domfilter" - local aeneas_config = require "make4ht-aeneas-config" - - filter_settings "aeneas-config" { - map = { - ["krecekli1.xhtml"] = {audio_file="krecek.mp3"}, - ["krecek.xhtml"] = false - } - } - - local process = domfilter {"aeneas"} - Make:match("html$", process) +: additional \LaTeX\ code inserted before `\documentclass`. + Useful for passing options to packages used in the document or to load additional packages. - if mode == "draft" then - aeneas_config.process_files {} - else - aeneas_config.execute {} - end +`tex4ht_sty_par` +: options for `tex4ht.sty` +`tex4ht_par` +: command line options for the `tex4ht` command -## Image conversion +`t4ht_par` -It is possible to convert parts of LaTeX input to pictures, it is used -for example for math or diagrams in `tex4ht`. +: command line options for the `t4ht` command -These pictures are stored in a special `dvi` file, which can be processed by -the `dvi to image` commands. +`outdir` -This conversion is normally configured in the `env file`, -which is system dependent and which has a bit unintuitive syntax. -This configuration is processed by the `t4ht` application and conversion -commands are called for all pictures. +: the output directory -It is possible to disable `t4ht` image processing and configure image -conversion in the build file: +`correct_exit` - Make:image("png$", - "dvipng -bg Transparent -T tight -o ${output} -pp ${page} ${source}") +: expected `exit code` from the command. The compilation will be terminated + if the exit code of the executed command has a different value. -`Make:image` takes two parameters, pattern to match image name and action. -Action can be either string template with conversion command, -or function which takes a table with parameters as an argument. +# Configuration file {#configfile} -There are three parameters: +It is possible to globally modify the build settings using the configuration +file. It is a special version of a build file where the global settings can be set. - - `output` - output image file name - - `source` - `dvi` file with the pictures - - `page` - page number of the converted image +Common tasks for the configuration file can be a declaration of the new commands, +loading of the default filters or specification of a default build sequence. -## The `mode` variable +One additional functionality not available in the build files are commands for +enabling and disabling of extensions. -There is global `mode` variable available in the build file. It contains -contents of the `--mode` command line option. It can be used to run some commands -conditionally. For example: - if mode == "draft" then - Make:htlatex{} - else - Make:htlatex{} - Make:htlatex{} - Make:htlatex{} - end +## Location -In this example (which is the default configuration used by `make4ht`), -LaTeX is called only once when `make4ht` is called with `draft` mode: - - make4ht -m draft filename +The configuration file can be saved either in the +`$HOME/.config/make4ht/config.lua` file, or in the `.make4ht` file placed in +the current directory or it's parent directories (up to the `$HOME` directory). -## The `settings` table +## Additional commands -You may want to access to the parameters also outside commands, file matches -and image conversion functions. For example, if you want to convert your file to -the `OpenDocument Format (ODT)`, you can use the following settings, based on the `oolatex` -command: +There are two additional commands: - settings.tex4ht_sty_par = settings.tex4ht_sty_par ..",ooffice" - settings.tex4ht_par = settings.tex4ht_par .. " ooffice/! -cmozhtf" - settings.t4ht_par = settings.t4ht_par .. " -cooxtpipes -coo " +`Make:enable_extension(name)` -There are some functions to ease access to the settings: +: require extension -`set_settings{parameters}` +`Make:disable_extension(name)` -: overwrite settings with values from a passed table +: disable extension -`settings_add{parameters}` +## Example -: add values to the current settings +The following example of the configuration file adds support for the `biber` command, requires +`common_domfilters` extension and requires MathML +output for math. -`filter_settings "filter name" {parameters}` + Make:add("biber", "biber ${input}") + Make:enable_extension "common_domfilters" + settings_add { + tex4ht_sty_par =",mathml" + } -: set settings for a filter + - settings_add { - tex4ht_sty_par =",ooffice", - tex4ht_par = " ooffice/! -cmozhtf", - t4ht_par = " -cooxtpipes -coo " - } +# List of available settings for filters and extensions. -Settings for filters and extensions can be set using `filter_settings`: +These settings may be set using `filter_settings` function in a build file or in the `make4ht` configuration file. - - filter_settings "test" { - hello = "world" - } -These settings can be read in the extensions and filters using `get_filter_settings`: - function test(input) - local options = get_filter_settings("test") - print(options.hello) - return input - end - -## List of available settings for filters and extensions. +## Indexing commands + +The indexing commands (like `xindy` or `makeindex`) use some common settings. + +idxfile + +: name of the `.idx` file. Default value is `\jobname.idx`. + +indfile + +: name of the `.ind` file. Default value is the same as `idxfile` with the file extension changed to `.ind`. + +Each indexing command can have some additional settings. + +### The `xindy` command + +encoding + +: text encoding of the `.idx` file. Default value is `utf8`. + +language -These settings may be set using `filter_settings` function. +: index language. Default language is English. -### The `tidy` extension +modules + +: table with names of additional `Xindy` modules to be used. + +### The `makeindex` command + +options + + +: additional command line options for the Makeindex command. + +### The `xindex` command + +options + + +: additional command line options for the Xindex command. + +language + +: document language + +## The `tidy` extension options : command line options for the `tidy` command. Default value is `-m -utf8 -w 512 -q`. -### The `fixinlines` dom filter +## The `collapsetoc` dom filter + +`toc_query` + +: CSS selector for selecting the table of contents container. + +`title_query` + +: CSS selector for selecting all elements that contain the section ID attribute. + +`toc_levels` + +: table containing a hierarchy of classes used in TOC + +Default values: + + filter_settings "collapsetoc" { + toc_query = ".tableofcontents", + title_query = ".partHead a, .chapterHead a, .sectionHead a, .subsectionHead a", + toc_levels = {"partToc", "chapterToc", "sectionToc", "subsectionToc", "subsubsectionToc"} + } + +## The `fixinlines` dom filter inline\_elements -: table of inline elements which shouldn't be direct descendants of the `body` element. The element names should be table keys, the values should be true. +: table of inline elements that shouldn't be direct descendants of the `body` element. The element names should be table keys, the values should be true. Example filter_settings "fixinlines" {inline_elements = {a = true, b = true}} -### The `joincharacters` dom filter +## The `joincharacters` dom filter -charelements +charclasses -: table of elements which should be joined if several instances with the same value of `class` attribute are side by side. +: table of elements that should be concatenated when two or more of such elements with the same value of the `class` attribute are placed one after another. Example - filter_settings "joincharacters" { charclases = { span=true, mn = true}} + filter_settings "joincharacters" { charclasses = { span=true, mn = true}} -### The `mathjaxnode` filter {#mathjaxsettings} +## The `mathjaxnode` filter {#mathjaxsettings} options @@ -746,12 +938,12 @@ Example cssfilename -: `mjpage` puts some CSS code into the HTML pages. `mathjaxnode` extracts this information and saves it to a standalone CSS file. Default CSS filename is `mathjax-chtml.css` +: the `mjpage` command puts some CSS code into the HTML pages. `mathjaxnode` extracts this information and saves it to a standalone CSS file. Default CSS filename is `mathjax-chtml.css` fontdir -: directory with MathJax font files. This option enables use of local fonts, which - is usefull in Epub conversion, for example. The font directory should be +: directory with MathJax font files. This option enables the use of local fonts, which + is useful in the conversion to ePub, for example. The font directory should be sub-directory of the current directory. Only TeX font is supported at the moment. Example @@ -761,21 +953,8 @@ Example fontdir="fonts/TeX/woff/" } -### The `aeneas` filter - -skip\_elements - -: List of CSS selectors that match elements which shouldn't be processed. Default value: `{ "math", "svg"}`. - -id\_prefix - -: prefix used in the ID attribute forming. - -sentence\_match - -: Lua pattern used to match a sentence. Default value: `"([^%.^%?^!]*)([%.%?!]?)"`. -### The `staticsite` filter and extension +## The `staticsite` filter and extension site\_root @@ -783,15 +962,22 @@ site\_root map -: table where keys are patterns that match filenames, value contains destination directoryfor matched files, relative to the `site_root` (it is possible to use `..` to swich to parent directory). +: a hash table where keys contain patterns that match filenames and values contain +destination directory for the matched files. The destination directories are +relative to the `site_root` (it is possible to use `..` to switch to a parent +directory). file\_pattern -: pattern used for filename generation. It is possible to use string templates and format strings for `os.date` function. Default value of `%Y-%m-%d-${input}` creates names in the form of `YYYY-MM-DD-file_name`. +: a pattern used for filename generation. It is possible to use string templates +and format strings for `os.date` function. The default pattern `%Y-%m-%d-${input}` +creates names in the form of `YYYY-MM-DD-file_name`. header -: table with variables to be set in the YAML header in HTML files. If the table value is a function, it is executed with current parameters and HTML page DOM object as arguments. +: table with variables to be set in the YAML header in HTML files. If the +table value is a function, it is executed with current parameters and HTML page +DOM object as arguments. Example: @@ -811,7 +997,7 @@ Example: } } -### The `dvisvgm_hashes` extension +## The `dvisvgm_hashes` extension options @@ -819,108 +1005,182 @@ options cpu_cnt -: number of processor cores used for conversion. The extension tries to detect the available cores automatically by default. +: the number of processor cores used for the conversion. The extension tries to detect the available cores automatically by default. parallel_size -: number of pages used in each Dvisvgm call. The extension detects changed pages in the DVI file and construct multiple calls to Dvisvgm with only changed pages. +: the number of pages used in each Dvisvgm call. The extension detects changed +pages in the DVI file and constructs multiple calls to Dvisvgm with only changed +pages. scale : SVG scaling. -### The `odttemplate` filter and extension +## The `odttemplate` filter and extension template : filename of the template `ODT` file -`odttemplate` can also get the template filename from the `odttemplate` option from `tex4ht_sty_par` parameter. It can be set useing following command line call, for example: +`odttemplate` can also get the template filename from the `odttemplate` option from `tex4ht_sty_par` parameter. It can be set using the following command line call: make4ht -f odt+odttemplate filename.tex "odttemplate=template.odt" +## The `aeneas` filter +skip\_elements -# Configuration file {#configfile} +: List of CSS selectors that match elements that shouldn't be processed. Default value: `{ "math", "svg"}`. -It is possible to globally modify the build settings using the configuration -file. New compilation commands can be added, extensions can be loaded or -disabled and settings can be set. +id\_prefix -## Location +: prefix used in the ID attribute forming. -The configuration file can be saved either in -`$HOME/.config/make4ht/config.lua` or in `.make4ht` in the current directory or -it's parents (up to `$HOME`). +sentence\_match -## Additional commands +: Lua pattern used to match a sentence. Default value: `"([^%.^%?^!]*)([%.%?!]?)"`. -There are two additional commands: +## The `make4ht-aeneas-config` package -`Make:enable_extension(name)` +Companion for the `aeneas` DOM filter is the `make4ht-aeneas-config` plugin. It +can be used to write the Aeneas configuration file or execute Aeneas on the +generated HTML files. -: require extension +Available functions: -`Make:disable_extension(name)` +write\_job(parameters) -: disable extension +: write Aenas job configuration to `config.xml` file. See the [Aeneas + documentation](https://www.readbeyond.it/aeneas/docs/clitutorial.html#processing-jobs) + for more information about jobs. -## Example +execute(parameters) -The following configuration add support for the `biber` command, requires -`common_domfilters` extension and requires MathML -output for math. +: execute Aeneas. - Make:add("biber", "biber ${input}") - Make:enable_extension "common_domfilters" - settings_add { - tex4ht_sty_par =",mathml" +process\_files(parameters) + +: process the audio and generated subtitle files. + + +By default, a `SMIL` file is created. It is assumed that there is an audio file +in the `mp3` format, named as the \TeX\ file. It is possible to use different formats +and filenames using mapping. + +The configuration options can be passed directly to the functions or set using +`filter_settings "aeneas-config" {parameters}` function. + + +### Available parameters + + +lang + +: document language. It is interfered from the HTML file, so it is not necessary to set it. + +map + +: mapping between HTML, audio and subtitle files. More info below. + +text\_type + +: type of input. The `aeneas` DOM filter produces an `unparsed` text type. + +id\_sort + +: sorting of id attributes. The default value is `numeric`. + +id\_regex + +: regular expression to parse the id attributes. + +sub\_format + +: generated subtitle format. The default value is `smil`. + + +### Additional parameters for the job configuration file + +- description +- prefix +- config\_name +- keep\_config + + + +It is possible to generate multiple HTML files from the \LaTeX\ source. For +example, `tex4ebook` generates a separate file for each chapter or section. It is +possible to set options for each HTML file, in particular names of the +corresponding audio files. This mapping is done using the `map` parameter. + +Example: + + filter_settings "aeneas-config" { + map = { + ["sampleli1.html"] = {audio_file="sample.mp3"}, + ["sample.html"] = false + } } -# Command line options +Table keys are the configured filenames. It is necessary to insert them as +`["filename.html"]`, because of Lua syntax rules. - make4ht - build system for tex4ht - Usage: - make4ht [options] filename ["tex4ht.sty op." "tex4ht op." - "t4ht op" "latex op"] - -b,--backend (default tex4ht) Backend used for xml generation. - possible values: tex4ht or lua4ht - -c,--config (default xhtml) Custom config file - -d,--output-dir (default "") Output directory - -e,--build-file (default nil) If build file name is different - than `filename`.mk4 - -f,--format (default nil) Output file format - -l,--lua Use lualatex for document compilation - -m,--mode (default default) Switch which can be used in the makefile - -n,--no-tex4ht Disable dvi file processing with tex4ht command - -s,--shell-escape Enables running external programs from LaTeX - -u,--utf8 For output documents in utf8 encoding - -v,--version Print version number - -x,--xetex Use xelatex for document compilation - (string) Input file name +This example maps audio file `sample.mp3` to a section subpage. The main HTML +file, which may contain title and table of contents doesn't have a +corresponding audio file. +Filenames of the subfiles correspond to the chapter numbers, so they are not +stable when a new chapter is added. It is possible to request filenames +derived from the chapter titles using the `sec-filename` option for `tex4ht.sty`. -You can still invoke `make4ht` in the same way as `htlatex`: +### Available `map` options - make4ht filename "customcfg, charset=utf-8" "-cunihtf -utf8" "-dfoo" -Note that this will not use `make4ht` routines for output directory making and -copying. If you want to use them, change the line above to: +audio\_file - make4ht -d foo filename "customcfg, charset=utf-8" "-cunihtf -utf8" +: the corresponding audio file -This call has the same effect as the following: +sub\_file + +: name of the generated subtitle file + +The following options are the same as their counterparts from the main parameters table and generally, don't need to be set: + +- prefix +- file\_desc +- file\_id +- text\_type +- id\_sort +- id\_prefix +- sub\_format + + +### Full example + + + local domfilter = require "make4ht-domfilter" + local aeneas_config = require "make4ht-aeneas-config" + + filter_settings "aeneas-config" { + map = { + ["krecekli1.xhtml"] = {audio_file="krecek.mp3"}, + ["krecek.xhtml"] = false + } + } + + local process = domfilter {"aeneas"} + Make:match("html$", process) + + if mode == "draft" then + aeneas_config.process_files {} + else + aeneas_config.execute {} + end - make4ht -u -c customcfg -d foo filename -Output directory doesn't have to exist, it will be created automatically. -Specified path can be relative to current directory, or absolute: - make4ht -d use/current/dir/ filename - make4ht -d ../gotoparrentdir filename - make4ht -d ~/gotohomedir filename - make4ht -d c:\documents\windowspathsareworkingtoo filename # Troubleshooting @@ -932,24 +1192,26 @@ Sometimes, you may get a similar error: It may be caused by a following `make4ht` invocation: - make4ht hello.tex "customcfg,charset=utf-8" "-cunihtf -utf8" -d foo + $ make4ht hello.tex "customcfg,charset=utf-8" "-cunihtf -utf8" -d foo The command line option parser is confused by mixing options for `make4ht` and -`tex4ht` in this case and tries to interpret the `-cunihtf -utf8`, which are -options for `tex4ht` command as `make4ht` options. To fix that, you can either -move the `-d foo` directly after `make4ht` command: +\TeX4ht\ in this case. It tries to interpret the `-cunihtf -utf8`, which are +options for the `tex4ht` command, as `make4ht` options. To fix that, try to +move the `-d foo` directly after the `make4ht` command: - make4ht -d foo hello.tex "customcfg,charset=utf-8" "-cunihtf -utf8" + $ make4ht -d foo hello.tex "customcfg,charset=utf-8" "-cunihtf -utf8" -Another option is to add a space before `tex4ht` options: +Another option is to add a space before the `tex4ht` options: - make4ht hello.tex "customcfg,charset=utf-8" " -cunihtf -utf8" -d foo + $ make4ht hello.tex "customcfg,charset=utf-8" " -cunihtf -utf8" -d foo The former way is preferable, though. ## Filenames containing spaces -`tex4ht` cannot handle filenames containing spaces. `make4ht` thus replaces spaces in input file names with underscores, so generated XML files use underscores instead of spaces as well. +`tex4ht` command cannot handle filenames containing spaces. to fix this issue, `make4ht` +replaces spaces in the input filenames with underscores. The generated +XML filenames use underscores instead of spaces as well. ## Filenames containing non-ASCII characters diff --git a/support/make4ht/changelog.tex b/support/make4ht/changelog.tex index 8d46424dec..f2b0fd7735 100644 --- a/support/make4ht/changelog.tex +++ b/support/make4ht/changelog.tex @@ -2,6 +2,262 @@ \section{Changelog}\label{changelog}} \begin{itemize} +\item + 2019/11/01 + + \begin{itemize} + \tightlist + \item + version 0.3 released + \item + added \texttt{Make:makeindex}, \texttt{Make:xindex} and + \texttt{Make:bibtex} commands. + \end{itemize} +\item + 2019/10/25 + + \begin{itemize} + \tightlist + \item + modified the \texttt{Make:xindy} command to use the indexing + mechanism. + \end{itemize} +\item + 2019/10/24 + + \begin{itemize} + \tightlist + \item + added functions for preparing and cleaning of the index files in + \texttt{make4ht-indexing.lua}. + \end{itemize} +\item + 2019/10/23 + + \begin{itemize} + \tightlist + \item + replaced \texttt{os.execute} function with \texttt{mkutils.execute}. + It uses the logging mechanism for the output. + \item + finished transforming of filters, extensions and formats to the + logging system. + \end{itemize} +\item + 2019/10/22 + + \begin{itemize} + \tightlist + \item + added \texttt{tablerows} domfilter. + \item + added the \texttt{tablerows} domfilter to the + \texttt{common\_domfilters} extension. + \item + converted most of the filters to use the logging mechanism. + \end{itemize} +\item + 2019/10/20 + + \begin{itemize} + \tightlist + \item + added \texttt{status} log level. + \end{itemize} +\item + 2019/10/18 + + \begin{itemize} + \tightlist + \item + converted most print commands to use the logging mechanism. + \item + added \texttt{output} log level used for printing of the commands + output. + \end{itemize} +\item + 2019/10/17 + + \begin{itemize} + \tightlist + \item + added \texttt{-\/-loglevel} CLI parameter. + \item + added logging mechanism. + \item + moved \texttt{htlatex} related code to \texttt{make4ht-htlatex.lua} + from \texttt{mkutils.lua} + \end{itemize} +\item + 2019/10/11 + + \begin{itemize} + \tightlist + \item + added \texttt{xindy} settings. + \item + added simple regular expression to detect errors in the log file, + because log parsing can be slow. + \end{itemize} +\item + 2019/10/09 + + \begin{itemize} + \tightlist + \item + added the \texttt{interaction} parameter for the \texttt{htlatex} + command. The default value is \texttt{batchmode} to suppress the + user input on errors, and to suppress full log output to the + terminal. + \item + added the \texttt{make4ht-errorlogparser} module. It is used to + parse errors in the \texttt{htlatex} run unless \texttt{interaction} + is set to \texttt{errorstopmode}. + \end{itemize} +\item + 2019/10/08 + + \begin{itemize} + \tightlist + \item + set up Github Actions pipeline to compile the documentation to HTML + and publish it at https://www.kodymirus.cz/make4ht/make4ht-doc.html. + \end{itemize} +\item + 2019/10/07 + + \begin{itemize} + \tightlist + \item + don't move the \texttt{common\_domfilters} extension to the first + place in the file matches pipeline. We may want to run \texttt{tidy} + or regex filters first, to fix XML validation errors. + \end{itemize} +\item + 2019/10/04 + + \begin{itemize} + \tightlist + \item + added HTML documentation. + \end{itemize} +\item + 2019/09/27 + + \begin{itemize} + \tightlist + \item + don't convert Latin 1 entities to Unicode in the + \texttt{entities\_to\_unicode} extension. + \end{itemize} +\item + 2019/09/20 + + \begin{itemize} + \tightlist + \item + fixed bugs in the temporary directory handling for the ODT output. + \end{itemize} +\item + 2019/09/13 + + \begin{itemize} + \tightlist + \item + added \texttt{preprocess\_input} extension. It enables compilation + of formats supported by \href{https://yihui.name/knitr/}{Knitr} + (\texttt{.Rnw}, \texttt{.Rtex}, \texttt{.Rmd}, \texttt{.Rrst}) and + also Markdown and reStructuredText formats. + \end{itemize} +\item + 2019/09/12 + + \begin{itemize} + \tightlist + \item + added support for the ODT files in \texttt{common\_domfilters} + extension. + \item + renamed \texttt{charclases} option for the \texttt{joincharacters} + DOM filter to \texttt{charclasses}. + \item + don't execute the \texttt{fixentities} filter before Xtpipes, it + makes no sense. + \end{itemize} +\item + 2019/09/11 + + \begin{itemize} + \tightlist + \item + added support for Biber in the build files. + \end{itemize} +\item + 2019/08/28 + + \begin{itemize} + \tightlist + \item + added support for input from \texttt{stdin}. + \end{itemize} +\item + 2019/08/27 + + \begin{itemize} + \tightlist + \item + fixed \texttt{-jobname} detection regex. + \item + added function \texttt{handle\_jobname}. + \item + added the \texttt{-\/-jobname} command line option. + \end{itemize} +\item + 2019/08/26 + + \begin{itemize} + \tightlist + \item + quote file names and paths in \texttt{xtpipes} and \texttt{tidy} + invocation. + \end{itemize} +\item + 2019/08/25 + + \begin{itemize} + \tightlist + \item + the issue tracker link in the help message is now configurable. + \item + fixed bug in the XeTeX handling: the \texttt{.xdv} argument for + \texttt{tex4ht} wasn't used if command line arguments for + \texttt{tex4ht} were present. + \end{itemize} +\item + 2019/07/03 + + \begin{itemize} + \tightlist + \item + new DOM filter: \texttt{odtpartable}. It fixes tables nested in + paragraphs in the ODT format. + \end{itemize} +\item + 2019/06/13 + + \begin{itemize} + \tightlist + \item + new DOM extension: \texttt{collapsetoc}. + \end{itemize} +\item + 2019/05/29 + + \begin{itemize} + \tightlist + \item + new module: \texttt{make4ht-indexing} for working with index files. + \end{itemize} \item 2019/05/24 @@ -18,7 +274,7 @@ \begin{itemize} \tightlist \item - fixed infinite loop bug in the \texttt{dvisvgm\_hashes} extension + fixed infinite loop bug in the \texttt{dvisvgm\_hashes} extension. \end{itemize} \item 2019/04/09 diff --git a/support/make4ht/domfilters/make4ht-aeneas.lua b/support/make4ht/domfilters/make4ht-aeneas.lua index 0b93c95840..4a21c89332 100644 --- a/support/make4ht/domfilters/make4ht-aeneas.lua +++ b/support/make4ht/domfilters/make4ht-aeneas.lua @@ -5,6 +5,7 @@ -- local cssquery = require "luaxml-cssquery" local mkutils = require "mkutils" +local log = logging.new "aeneas" -- Table of CSS selectors to be skipped. local skip_elements = { "math", "svg"} @@ -111,7 +112,7 @@ local function aeneas(dom, par) then local idtitle idtitle, id = make_id(id, id_prefix) - print(el._name, first_child._text) + log:debug(el._name, first_child._text) el:set_attribute("id", idtitle) return el end diff --git a/support/make4ht/domfilters/make4ht-collapsetoc.lua b/support/make4ht/domfilters/make4ht-collapsetoc.lua new file mode 100644 index 0000000000..0b77b9dbbb --- /dev/null +++ b/support/make4ht/domfilters/make4ht-collapsetoc.lua @@ -0,0 +1,198 @@ +local domobject = require "luaxml-domobject" + +local filter = require "make4ht-filter" +local log = logging.new "collapsetoc" + + +local toc_levels = {"partToc", "chapterToc", "sectionToc", "subsectionToc", "subsubsectionToc"} + +local debug_print = function(...) log:debug(...) end + + +-- return toc element type and it's id +local function get_id(el) + local name = el:get_attribute "class" + local id + local a = el:query_selector "a" or {} + local first = a[1] + if first then + local href = first:get_attribute "href" + id = href:match("#(.+)$") + end + return name, id +end + +local function remove_sections(part_elements, currentpart) + -- we need to remove toc entries from the previous part if the + -- current document isn't part of it + if currentpart == false then + for _, part in ipairs(part_elements) do + part:remove_node() + end + end +end + +local function make_toc_selector(toc_levels) + local level_classes = {} + for _, l in ipairs(toc_levels) do + level_classes[#level_classes+1] = "." .. l + end + return table.concat(level_classes, ", ") +end + +local function find_toc_levels(toc) + -- find toc levels used in the document + -- it ecpects that sectioning levels appears in the TOC in the descending order + local levels, used = {}, {} + local level = 1 + -- we still expect the standard class names + local toc_selector = make_toc_selector(toc_levels) + for _, el in ipairs(toc:query_selector(toc_selector)) do + local class = el:get_attribute("class") + if not used[class] then + table.insert(levels, class) + used[class] = level + level = level + 1 + end + end + return levels, used +end + +local function remove_levels(toc, matched_ids) + -- sort the matched sections according to their levels + local levels, level_numbers = find_toc_levels(toc) + debug_print("remove levels", #levels) + -- for _, level in ipairs(levels) do + -- print(level, level_numbers[level], matched_ids[level]) + -- end + local keep_branch = false + local matched_levels = {} + local toc_selector = make_toc_selector(toc_levels) + for _, el in ipairs(toc:query_selector(toc_selector)) do + local name, id = get_id(el) + -- get the current toc hiearchy level + local level = level_numbers[name] + -- get the matched id for the current level + local level_id = matched_ids[name] + local matched = level_id == id + local remove = true + -- we will use this for toc elements at lower hiearchy than is the top sectioning level on the page + if matched then keep_branch = true end + -- find the parent level to the current section level + local parent_level = toc_levels[level - 1] + local parent_matched = matched_levels[parent_level] + if matched then + debug_print("match",name, id, level_id, level, #levels) + keep_branch = true + remove = false + elseif level==1 then + -- don't remove the top level + debug_print("part",name, id, level_id, level) + remove = false + matched_levels = {} + if not matched then keep_branch = false end + elseif keep_branch then + -- if level >= (#levels - 1) then + if level > matched_ids._levels then + debug_print("level",name, id, level_id, level, parent_level, parent_matched) + remove = false + elseif matched_ids.ids[id] then + debug_print("matched id",name, id, level_id, level, parent_level, parent_matched) + remove = false + elseif parent_matched then + debug_print("parent_matched",name, id, level_id, level, parent_level, parent_matched) + keep_branch = false + remove = false + end + -- else + -- print("remove", name, id, level_id, level, #matched_ids) + -- end + elseif parent_matched then + debug_print("parent_matched alternative",name, id, level_id, level, parent_level, parent_matched) + remove = false + else + debug_print("else",name, id, level_id, level, keep_branch) + keep_branch = false + end + matched_levels[name] = matched + if remove then + el:remove_node() + --print(name,id, level_id, matched) + end + end + +end + + + + + +-- local process = filter{ function(s) + -- local dom = domobject.parse(s) +local function collapse_toc(dom, par) + -- set options + local options = get_filter_settings "collapsetoc" + local toc_query = par.toc_query or options.toc_query or ".tableofcontents" + local title_query = par.title_query or options.title_query or ".partHead a, .chapterHead a, .sectionHead a, .subsectionHead a" + toc_levels = par.toc_levels or options.toc_levels or toc_levels + -- keep track of current id of each sectioning level + local current_ids, matched_ids = {}, {_levels = 0, ids = {}} + -- search sectioning elements + local titles = dom:query_selector(title_query) + local section_ids = {} + for _, x in ipairs(titles) do + -- get their id attributes and save them in a table + section_ids[#section_ids+1] = x:get_attribute("id") + end + + -- we need to retrieve the first table of contents + local toctables = dom:query_selector(toc_query) or {} + -- process only when we got a TOC + debug_print("toc query", toc_query, #toctables) + if #toctables > 0 then + local tableofcontents = toctables[1] + -- all toc entries are in span elements + local toc = tableofcontents:query_selector("span") + local currentpart = false + local part_elements = {} + for _, el in ipairs(toc) do + -- get sectioning level and id of the current TOC entry + local name, id = get_id(el) + -- set the id of the current sectioning level + current_ids[name] = id + for _, sectid in ipairs(section_ids) do + -- detect if the current TOC entry match some sectioning element in the current document + if id == sectid then + currentpart = true + -- save the current id as a matched id + matched_ids.ids[id] = true + -- copy the TOC hiearchy for the current toc level + for i, level in ipairs(toc_levels) do + -- print("xxx",i, level, current_ids[level]) + matched_ids[level] = current_ids[level] + -- set the maximum matched level + if i > matched_ids._levels then matched_ids._levels = i end + if level == name then break end + end + debug_print("match", id) + end + end + end + remove_levels(tableofcontents, matched_ids) + + -- remove sections from the last part + -- remove_sections(part_elements,currentpart) + -- remove unneeded br elements + local br = tableofcontents:query_selector("br") + for _, el in ipairs(br) do el:remove_node() end + -- remove unneded whitespace + for _, el in ipairs(tableofcontents:get_children()) do + if el:is_text() then el:remove_node() end + end + end + return dom +end + +return collapse_toc + +-- Make:match("html$", process) diff --git a/support/make4ht/domfilters/make4ht-joincharacters.lua b/support/make4ht/domfilters/make4ht-joincharacters.lua index 99d3058d7d..30ed6e1382 100644 --- a/support/make4ht/domfilters/make4ht-joincharacters.lua +++ b/support/make4ht/domfilters/make4ht-joincharacters.lua @@ -1,4 +1,4 @@ -local charclases = { +local charclasses = { span=true, mn = true, } @@ -8,7 +8,7 @@ local function join_characters(obj,par) -- tex4ht to just one object. local par = par or {} local options = get_filter_settings "joincharacters" - local charclases = options.charclases or par.charclases or charclases + local charclasses = options.charclasses or par.charclasses or charclasses obj:traverse_elements(function(el) local get_name = function(curr) @@ -18,7 +18,7 @@ local function join_characters(obj,par) return next_el:get_attribute("class") end local is_span = function(next_el) - return charclases[get_name(next_el)] + return charclasses[get_name(next_el)] end local function get_next(curr, class) @@ -33,7 +33,7 @@ local function join_characters(obj,par) end end -- loop over all elements and test if the current element is in a list of - -- processed elements (charclases) + -- processed elements (charclasses) if is_span(el) then local next_el = get_next(el) -- loop over the following elements and test whether they are of the same type diff --git a/support/make4ht/domfilters/make4ht-joincolors.lua b/support/make4ht/domfilters/make4ht-joincolors.lua index 94a3a054e6..730f66a7a0 100644 --- a/support/make4ht/domfilters/make4ht-joincolors.lua +++ b/support/make4ht/domfilters/make4ht-joincolors.lua @@ -1,4 +1,5 @@ local cssfiles = {} +local log = logging.new "joincolors" -- keep mapping between span ids and colors @@ -45,7 +46,7 @@ local function process_css_files(dom) for _, el in ipairs(dom:query_selector("link")) do local href = el:get_attribute("href") or "" if not cssfiles[href] and href:match("css$") then - print("Load CSS file ", href) + log:debug("Load CSS file ", href) cssfiles[href] = true process_css(href) end diff --git a/support/make4ht/domfilters/make4ht-odtimagesize.lua b/support/make4ht/domfilters/make4ht-odtimagesize.lua index 4ce8f0d92e..2b87372fdb 100644 --- a/support/make4ht/domfilters/make4ht-odtimagesize.lua +++ b/support/make4ht/domfilters/make4ht-odtimagesize.lua @@ -1,3 +1,4 @@ +local log = logging.new "odtimagesize" -- set correct dimensions to frames around images return function(dom) local frames = dom:query_selector("draw|frame") @@ -9,7 +10,7 @@ return function(dom) local height = image:get_attribute("svg:height") if widht then frame:set_attribute("svg:width", width) end if height then frame:set_attribute("svg:height", height) end - print("image dimensions", width, height) + log:debug("image dimensions", width, height) end end return dom diff --git a/support/make4ht/domfilters/make4ht-odtpartable.lua b/support/make4ht/domfilters/make4ht-odtpartable.lua new file mode 100644 index 0000000000..f4e84b55d9 --- /dev/null +++ b/support/make4ht/domfilters/make4ht-odtpartable.lua @@ -0,0 +1,9 @@ +-- find all tables inside paragraphs, replace the found paragraphs with the child table +return function(dom) + for _,table in ipairs(dom:query_selector("text|p table|table")) do + -- replace the paragraph by its child element + local parent = table:get_parent() + parent:replace_node(table) + end + return dom +end diff --git a/support/make4ht/domfilters/make4ht-tablerows.lua b/support/make4ht/domfilters/make4ht-tablerows.lua new file mode 100644 index 0000000000..155c24529a --- /dev/null +++ b/support/make4ht/domfilters/make4ht-tablerows.lua @@ -0,0 +1,58 @@ +local log = logging.new ("tablerows") +return function(dom) + local is_empty_row = function(row) + local not_empty = false + local element_count = 0 + -- ignore hline rows + local row_class = row:get_attribute("class") + if row_class == "hline" or row_class == "cline" then return false end + -- detect if the row contain only one empty child + for _,child in ipairs(row:get_children() or {}) do + if child:is_element() then + element_count = element_count + 1 + -- empty rows contain only one element, it is not empty otherwise + if element_count > 1 then return false end + -- detect if it contains only whitespace + not_empty = child:get_text():gsub("%s","") ~= "" or not_empty + end + end + -- print("element count", element_count, not_empty) + return element_count == 1 and not_empty == false + end + local is_not_styled = function(row, css) + -- get the id attribute and escape it, so it can be used in regexp + local id = row:get_attribute("id") + if not id then return true end -- no styling without id + local search_term = "%#" .. id:gsub("%-", "%%-") + -- if the CSS file contains the row id ( elements can also have id + -- that matches this pattern, so we should keep the row if we match them too) + return not css:match(search_term) + end + local load_css_files = function() + -- the empty rows can be styled using CSS, for example configuration for + -- Booktabs does that. We shouldn't remove such rows. + local cssfiles = {} + for _, link in ipairs(dom:query_selector("head link")) do + local src = link:get_attribute("href") + if src then + local f = io.open(src, "r") + if f then + local contents = f:read("*all") + f:close() + table.insert(cssfiles, contents) + end + end + end + return table.concat(cssfiles, "\n") + end + local css = load_css_files() + for _, tbl in ipairs(dom:query_selector("table")) do + -- find the empty rows + for _, row in ipairs(tbl:query_selector("tr")) do + if is_empty_row(row) and is_not_styled(row, css) then row:remove_node() end + end + + end + return dom +end + diff --git a/support/make4ht/extensions/common_domfilters.lua b/support/make4ht/extensions/common_domfilters.lua index 7c9a2dfaea..9399492d90 100644 --- a/support/make4ht/extensions/common_domfilters.lua +++ b/support/make4ht/extensions/common_domfilters.lua @@ -1,25 +1,34 @@ local M = {} +-- this variable will hold the output format name +local current_format + local filter = require "make4ht-domfilter" -- local process = filter {"fixinlines", "idcolons", "joincharacters" } -- filters support only html formats function M.test(format) - if format == "odt" then return false end + current_format = format + -- if format == "odt" then return false end return true end function M.modify_build(make) - local process = filter {"fixinlines", "idcolons", "joincharacters"} - make:match("html$", process) - local matches = make.matches - -- the filters should be first match to be executed, especially if tidy - -- should be executed as well - if #matches > 1 then - local last = matches[#matches] - table.insert(matches, 1, last) - matches[#matches] = nil + -- number of filters that should be moved to the beginning + local count = 0 + if current_format == "odt" then + -- some formats doesn't make sense in the ODT format + local process = filter {"joincharacters"} + local charclasses = {mn = true, ["text:span"] = true} + make:match("4oo$", process, {charclasses= charclasses}) + -- match math documents + make:match("4om$", process, {charclasses= charclasses}) + count = 2 + else + local process = filter {"fixinlines", "idcolons", "joincharacters", "tablerows"} + make:match("html$", process) + count = 1 end return make end diff --git a/support/make4ht/extensions/dvisvgm_hashes.lua b/support/make4ht/extensions/dvisvgm_hashes.lua index 83010ac388..87f58abd57 100644 --- a/support/make4ht/extensions/dvisvgm_hashes.lua +++ b/support/make4ht/extensions/dvisvgm_hashes.lua @@ -1,6 +1,7 @@ local dvireader = require "make4ht-dvireader" local mkutils = require "mkutils" local filter = require "make4ht-filter" +local log = logging.new "dvisvgm_hashes" local dvisvgm_par = {} @@ -149,7 +150,7 @@ end local function execute_dvisvgm(idvfile, pages) if #pages < 1 then return nil, "No pages to convert" end local command, logs = prepare_command(idvfile, pages) - print(command) + log:info(command) os.execute(command) local generated_pages = {} for _, dvisvgmlog in ipairs(logs) do @@ -184,7 +185,7 @@ local function get_dvi_pages(arg) local output_name = make_hashed_name(arg.input, hash) output_map[tex4ht_name] = output_name if not mkutils.file_exists(output_name) then - print(output_name) + log:debug("output file: ".. output_name) to_convert[#to_convert+1] = page end end @@ -246,7 +247,7 @@ function M.modify_build(make) function(str) return str:gsub('src="([^"]+)', function(filename) local newname = output_map[filename] or filename - print("newname", newname) + log:debug("newname", newname) return 'src="'.. newname end) end diff --git a/support/make4ht/extensions/preprocess_input.lua b/support/make4ht/extensions/preprocess_input.lua new file mode 100644 index 0000000000..3155a8a781 --- /dev/null +++ b/support/make4ht/extensions/preprocess_input.lua @@ -0,0 +1,108 @@ +-- preprocess R literate sources or Markdown files to LaTeX +local M = {} +local log = logging.new "preprocess_input" +local mkutils = require "mkutils" + +local commands = { + knitr = { command = 'Rscript -e "library(knitr); knit(\'${tex_file}\', output=\'${tmp_file}\')"'}, + pandoc = { command = 'pandoc -f ${input_format} -s -o \'${tmp_file}\' -t latex \'${tex_file}\''} +} +local filetypes = { + rnw = {sequence = {"knitr"} }, + rtex = {sequence = {"knitr"}}, + rmd = {sequence = {"knitr", "pandoc"}, options = {input_format = "markdown"}}, + rrst = {sequence = {"knitr", "pandoc"}, options = {input_format = "rst"}}, + md = {sequence = {"pandoc"}, options = {input_format = "markdown"}}, + rst = {sequence = {"pandoc"}, options = {input_format = "rst"}}, +} + + +local function execute_sequence(sequence, arg, make) + -- keep track of all generated tmp files + local temp_files = {} + -- the temporary file for the current compilation step + -- should become the tex_file for the next one. It doesn't + -- matter that it isn't TeX file in some cases + local previous_temp + for _, cmd_name in ipairs(sequence) do + local tmp_name = os.tmpname() + temp_files[#temp_files+1] = tmp_name + -- make the temp file name accessible to the executed commands + arg.tmp_file = tmp_name + -- the current temporary file should become tex_file in the next step + -- in the first execution of the compilation sequence we will use the + -- actual input file name + arg.tex_file = previous_temp or arg.tex_file + previous_temp = tmp_name + -- get the command to execute + local cmd = commands[cmd_name] + -- fill the command template with make4ht arguments and execute + local command = cmd.command % arg + log:info(command) + mkutils.execute(command) + end + return temp_files +end + +local function get_preprocessing_pipeline(input_file) + -- detect the file extension + local extension = input_file:match("%.(.-)$") + if not extension then return nil, "Cannot get extension: " .. input_file end + -- the table with file actions is case insensitive + -- the extension is converted to lowercase in order + -- to support both .rnw and .Rnw + extension = string.lower(extension) + local matched = filetypes[extension] + if not matched then return nil, "Unsupported extension: " .. extension end + return matched +end + +-- join the make4ht params and command options tables +local function make_options(arg, command_options) + local options = {} + local command_options = command_options or {} + for k,v in pairs(arg) do options[k] = v end + for k,v in pairs(command_options) do options[k] = v end + return options +end + +M.modify_build = function(make) + + -- get access to the main argumens + local arg = make.params + -- get the execution sequence for the input format + local matched, msg = get_preprocessing_pipeline(arg.tex_file) + if not matched then + log:error("preprocess_input error: ".. msg) + return + end + -- prepare options + local options = make_options(arg, matched.options) + -- run the execution sequence + local temp_files = execute_sequence(matched.sequence or {}, options, make) + -- the last temporary file contains the actual TeX file + local last_temp_file = temp_files[#temp_files] + -- remove the intermediate temp files + if #temp_files > 2 then + for i = 1, #temp_files - 1 do + log:debug("Removing temporary file", temp_files[i]) + os.remove(temp_files[i]) + end + end + if last_temp_file then + -- update all commands in the .mk4 file with the temp file as tex_file + local update_params = function(cmd) + local params = cmd.params + params.tex_file = last_temp_file + params.is_tmp_file = true + end + for _, cmd in ipairs(make.build_seq) do + update_params(cmd) + end + -- also update the main params + update_params(make) + end + return make +end + +return M diff --git a/support/make4ht/extensions/staticsite.lua b/support/make4ht/extensions/staticsite.lua index fcb6c09e17..a4204bc173 100644 --- a/support/make4ht/extensions/staticsite.lua +++ b/support/make4ht/extensions/staticsite.lua @@ -1,6 +1,7 @@ local M = {} local filter = require "make4ht-filter" local mkutils = require "mkutils" +local log = logging.new "staticsite" -- get the published file name local function get_slug(settings) @@ -15,7 +16,7 @@ local function get_slug(settings) local f = io.open(published_name, "r") local readtime = f:read("*line") time = tonumber(readtime) - print("Already pubslished", slug) + log:info("Already pubslished", slug) f:close() else -- escape diff --git a/support/make4ht/extensions/tidy.lua b/support/make4ht/extensions/tidy.lua index cf8ea9785a..b381a05a8f 100644 --- a/support/make4ht/extensions/tidy.lua +++ b/support/make4ht/extensions/tidy.lua @@ -1,5 +1,6 @@ local M = {} +local log = logging.new "tidy" function M.test(format) if format == "odt" then return false end return true @@ -41,7 +42,7 @@ function M.modify_build(make) local settings = get_filter_settings "tidy" or {} par.options = par.options or settings.options or "-utf8 -w 512 -ashtml -q" local command = "tidy ${options} ${filename}" % par - print("execute: ".. command) + log:info("running tidy: ".. command) -- os.execute(command) local run = io.popen(command, "r") local result = run:read("*all") diff --git a/support/make4ht/filters/make4ht-domfilter.lua b/support/make4ht/filters/make4ht-domfilter.lua index 27d9ed871d..f6df769d60 100644 --- a/support/make4ht/filters/make4ht-domfilter.lua +++ b/support/make4ht/filters/make4ht-domfilter.lua @@ -1,6 +1,7 @@ local filter_lib = require "make4ht-filterlib" local dom = require "luaxml-domobject" local mkutils = require "mkutils" +local log = logging.new "domfilter" local function load_filter(filtername) return require("domfilters.make4ht-"..filtername) @@ -42,8 +43,8 @@ local function filter(filters, name) return dom.parse(input) end) if not status then - print("DOM parsing of " .. filename .. " failed:") - print(domobject) + log:warning("DOM parsing of " .. filename .. " failed:") + log:warning(domobject) return nil, "DOM parsing failed" end for _,f in pairs(sequence) do @@ -53,7 +54,7 @@ local function filter(filters, name) if output then filter_lib.save_input_file(filename, output) else - print("DOM filter failed on ".. filename) + log:warning("DOM filter failed on ".. filename) end -- mark the filename as processed processed_files[filename] = true diff --git a/support/make4ht/filters/make4ht-entities-to-unicode.lua b/support/make4ht/filters/make4ht-entities-to-unicode.lua index fb921a631a..4bf106a79f 100644 --- a/support/make4ht/filters/make4ht-entities-to-unicode.lua +++ b/support/make4ht/filters/make4ht-entities-to-unicode.lua @@ -6,10 +6,12 @@ local disabled = { ["&"] = "&", ["<"] = "<", [">"] = ">"} return function(content) local content = content:gsub("%&%#x([A-Fa-f0-9]+);", function(entity) -- convert hexadecimal entity to Unicode - local newchar = utfchar(tonumber(entity, 16)) + local char_number = tonumber(entity, 16) + -- fix for non-breaking spaces, LO cannot open file when they are present as Unicode + if char_number == 160 then return " " end + local newchar = utfchar(char_number) -- we don't want to break XML validity with forbidden characters return disabled[newchar] or newchar end) - -- the non-breaking space character cause issues in the ODT opening - return content:gsub(string.char(160), " ") + return content end diff --git a/support/make4ht/filters/make4ht-mathjaxnode.lua b/support/make4ht/filters/make4ht-mathjaxnode.lua index 2b453e4255..748ecfd483 100644 --- a/support/make4ht/filters/make4ht-mathjaxnode.lua +++ b/support/make4ht/filters/make4ht-mathjaxnode.lua @@ -1,4 +1,5 @@ local mkutils = require "mkutils" +local log = logging.new("mathjaxnode") -- other possible value is page2svg local mathnodepath = "mjpage" -- options for MathJax command @@ -13,13 +14,13 @@ local cssfilename = "mathjax-chtml.css" local function compile(text) local tmpfile = os.tmpname() - print("Compile using MathJax") + log:info("Compile using MathJax") local command = mathnodepath .. " ".. options .. " > " .. tmpfile - print(command) + log:info(command) local commandhandle = io.popen(command,"w") commandhandle:write(text) commandhandle:close() - print("Result written to: ".. tmpfile) + log:info("Result written to: ".. tmpfile) local f = io.open(tmpfile) local content = f:read("*all") f:close() @@ -53,7 +54,7 @@ local function use_fonts(css) if not face:match("url%(") then return face end -- print(face) local family, filename = face:match(family_pattern) - print(family, filename) + log:info("use font: ",family, filename) local newfile = string.format("%s/%s.%s", fontdir, filename, fontformat) Make:add_file(newfile) return family_build:format(family, fontdir, filename, fontformat, fontformat) @@ -89,6 +90,6 @@ return function(text, arguments) save_css(cssfile, css) Make:add_file(cssfile) -- print(css) - print(cssfile) + log:info("CSS file: " .. cssfile) return newtext end diff --git a/support/make4ht/filters/make4ht-staticsite.lua b/support/make4ht/filters/make4ht-staticsite.lua index 6378f0440a..969d47ecaa 100644 --- a/support/make4ht/filters/make4ht-staticsite.lua +++ b/support/make4ht/filters/make4ht-staticsite.lua @@ -1,4 +1,5 @@ local domobj = require "luaxml-domobject" +local log = logging.new("staticsite") -- save the header settings in YAML format local function make_yaml(tbl, level) local t = {} @@ -69,7 +70,6 @@ local function get_header(tbl) end return function(s,par) - print(os.getenv "blog_home") local dom = domobj.parse(s) local properties = {} local head = dom:query_selector("head")[1] @@ -84,7 +84,7 @@ return function(s,par) properties.styles = styles local metas = {} for _, meta in ipairs(head:query_selector("meta")) do - print(meta:serialize()) + log:debug("parsed meta: " .. meta:serialize()) table.insert(metas, {charset= meta:get_attribute("charset"), content = meta:get_attribute("content"), property = meta:get_attribute("property"), name = meta:get_attribute("name")}) end properties.meta = metas @@ -92,7 +92,7 @@ return function(s,par) local body = dom:query_selector("body")[1] - print(get_header(properties)) + log:debug(get_header(properties)) -- return s return get_header(properties) .. body:serialize():gsub("", ""):gsub("", "") end diff --git a/support/make4ht/filters/make4ht-svg-height.lua b/support/make4ht/filters/make4ht-svg-height.lua index 44e98748ea..bb9f8dbdc6 100644 --- a/support/make4ht/filters/make4ht-svg-height.lua +++ b/support/make4ht/filters/make4ht-svg-height.lua @@ -1,6 +1,8 @@ +local log = logging.new("svg-height") -- Make:image("svg$", "dvisvgm -n -a -p ${page} -b preview -c 1.4,1.4 -s ${source} > ${output}") + local max = function(a,b) return a > b and a or b end @@ -34,7 +36,7 @@ return function(svg) max_height = get_max_height(path, max_height) end -- update the height only if the max_height is larger than height set in the SVG file - print(max_height, height) + log:debug("max height and height", max_height, height) if max_height > height then svg = update_height(svg, max_height) end diff --git a/support/make4ht/formats/docbook.lua b/support/make4ht/formats/docbook.lua index 9e5ffe9488..3bd79eb83e 100644 --- a/support/make4ht/formats/docbook.lua +++ b/support/make4ht/formats/docbook.lua @@ -6,6 +6,7 @@ local kpse = require "kpse" local filter = require "make4ht-filter" local domfilter = require "make4ht-domfilter" local xtpipeslib = require "make4ht-xtpipes" +local log = logging.new "docbook" function M.prepare_parameters(settings, extensions) settings.tex4ht_sty_par = settings.tex4ht_sty_par ..",docbook" @@ -25,7 +26,7 @@ local function call_xtpipes(make) make:match("xml$", matchfunction) move_matches(make) else - print "Cannot locate xtpipes. Try to set TEXMFROOT variable to a root directory of your TeX distribution" + log:warning "Cannot locate xtpipes. Try to set TEXMFROOT variable to a root directory of your TeX distribution" end end diff --git a/support/make4ht/formats/odt.lua b/support/make4ht/formats/odt.lua index 64b8793e2a..c515a154a1 100644 --- a/support/make4ht/formats/odt.lua +++ b/support/make4ht/formats/odt.lua @@ -6,6 +6,7 @@ local kpse = require "kpse" local filter = require "make4ht-filter" local domfilter = require "make4ht-domfilter" local xtpipeslib = require "make4ht-xtpipes" +local log = logging.new "odt" function M.prepare_parameters(settings, extensions) @@ -23,9 +24,12 @@ Odtfile.__index = Odtfile Odtfile.new = function(archivename) local self = setmetatable({}, Odtfile) - -- create temporary directory + -- create a temporary file local tmpname = os.tmpname() - tmpname = tmpname:match("([a-zA-Z0-9_%-]+)$") + -- remove a temporary file, we are interested only in the unique file name + os.remove(tmpname) + -- get the unique dir name + tmpname = tmpname:match("([a-zA-Z0-9_%-%.]+)$") local status, msg = lfs.mkdir(tmpname) if not status then return nil, msg end -- make picture dir @@ -68,10 +72,10 @@ function Odtfile:pack() lfs.chdir(self.archivelocation) -- make temporary mime type file self:make_mimetype() - os.execute(zip_command .. " -q0X " .. self.name .. " " .. self.mimetypename) + mkutils.execute(zip_command .. " -q0X " .. self.name .. " " .. self.mimetypename) -- remove it, so the next command doesn't overwrite it self:remove_mimetype() - os.execute(zip_command .." -r " .. self.name .. " *") + mkutils.execute(zip_command .." -r " .. self.name .. " *") lfs.chdir(currentdir) mkutils.cp(self.archivelocation .. "/" .. self.name, self.name) mkutils.delete_dir(self.archivelocation) @@ -99,7 +103,7 @@ local function call_xtpipes(make) move_matches(make) move_matches(make) else - print "Cannot locate xtpipes. Try to set TEXMFROOT variable to a root directory of your TeX distribution" + log:warning "Cannot locate xtpipes. Try to set TEXMFROOT variable to a root directory of your TeX distribution" end end @@ -111,7 +115,7 @@ local function prepare_output_files(lgfiles) local group = groups[extension] or {} table.insert(group, basename) groups[extension] = group - print(basename, extension) + log:debug("prepare output file", basename, extension) end return groups end @@ -130,7 +134,7 @@ function M.modify_build(make) -- expanded in tex4ht.env in Miktex or Debian call_xtpipes(make) -- fix the image dimensions wrongly set by xtpipes - local domfilters = domfilter {"t4htlinks"} + local domfilters = domfilter {"t4htlinks", "odtpartable"} make:match("4oo$", domfilters) -- execute it before xtpipes, because we don't want xtpipes to mess with t4htlink elements move_matches(make) @@ -138,9 +142,6 @@ function M.modify_build(make) local fixentities = filter {"entities-to-unicode"} make:match("4oo", fixentities) make:match("4om", fixentities) - -- execute fixentities as first - move_matches(make) - move_matches(make) -- build the ODT file. This match must be executed as a last one -- this will be executed as a first match, just to find the last filename @@ -161,7 +162,7 @@ function M.modify_build(make) local odtname = basename .. ".odt" local odt,msg = Odtfile.new(odtname) if not odt then - print("Cannot create ODT file: " .. msg) + log:error("Cannot create ODT file: " .. msg) end -- helper function for simple file moving local function move_file(group, dest) diff --git a/support/make4ht/make4ht b/support/make4ht/make4ht index 564bc6e7a0..b1ff84f700 100755 --- a/support/make4ht/make4ht +++ b/support/make4ht/make4ht @@ -3,8 +3,10 @@ -- This package is subject of LPPL license, version 1.3 kpse.set_program_name("luatex") - - +-- logging should be globally available +logging = require "make4ht-logging" +if os.type == "windows" then logging.use_colors = false end +local log = logging.new("make4ht") local make4ht = require("make4ht-lib") local lapp = require("lapp-mk4") local mkutils = require("mkutils") @@ -13,7 +15,7 @@ local mk_config = require("make4ht-config") -- args string is here just as sample, we dont pass it it to -- mkparams.get_args() so default args string is used local args = [[ -make4ht - build system for tex4ht +make4ht - build system for TeX4ht Usage: make4ht [options] filename ["tex4ht.sty op." "tex4ht op." "t4ht op" "latex op"] -c,--config (default xhtml) Custom config file @@ -27,13 +29,16 @@ make4ht [options] filename ["tex4ht.sty op." "tex4ht op." "t4ht op" "latex op"] -- set version number. the template should be replaced by the -- actual version number by the build script -local version = "v0.2g" +local version = "v0.3" mkparams.version_number = version local args = mkparams.get_args() local parameters = mkparams.process_args(args) +log:status("Conversion started") +log:status("Input file: " .. parameters.tex_file) + local mode = parameters.mode local build_file = parameters.build_file @@ -48,7 +53,7 @@ if allowed_output_formats[ output_format ] then else -- load html5 as default output format if output_format then - print("Cannot load output format: ".. output_format) + log:warning("Cannot load output format: ".. output_format) end formatter = mkutils.load_output_format("html5") end @@ -56,7 +61,7 @@ end local configname = "make4ht" 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) mkutils.load_config(parameters, conffile) end local extensions = formatter.prepare_extensions(parameters.extensions) @@ -96,15 +101,26 @@ end -- allow output formats to modify the build process at the end make = formatter.modify_build(make) or make +make:match("tmp$", function(filename,params) + -- remove the temporary tex file created when the input comes from the standard input + if params.is_tmp_file then + log:info("removing temp file", params.tex_file) + os.remove(params.tex_file) + end + -- prevent copying of the temporary file to the outdir + return false,"tmp file" end +) -make:match("tmp$", function() return false,"tmp file" end) make:match(".*",function(filename,par) local outdir = '' --par["outdir"] and par["outdir"] .."/" or '' if par['outdir'] ~= "" then outdir = par['outdir'] .. '/' end - print("outdir: "..outdir) + log:info("outdir: "..outdir) local outfilename = outdir .. filename mkutils.copy(filename,outfilename) return true end) make:run() + +log:status("Conversion finished") + diff --git a/support/make4ht/make4ht-doc.pdf b/support/make4ht/make4ht-doc.pdf index 0b7dd091ea..506be1df42 100644 Binary files a/support/make4ht/make4ht-doc.pdf and b/support/make4ht/make4ht-doc.pdf differ diff --git a/support/make4ht/make4ht-doc.tex b/support/make4ht/make4ht-doc.tex index 6815e50606..24f94aa131 100644 --- a/support/make4ht/make4ht-doc.tex +++ b/support/make4ht/make4ht-doc.tex @@ -3,19 +3,24 @@ \usepackage[english]{babel} \usepackage{hyperref} +\newcommand\authormail[1]{\footnote{\textless\url{#1}\textgreater}} \ifdefined\HCode -\usepackage[T1]{fontenc} -\usepackage[utf8]{inputenc} -\else +\renewcommand\authormail[1]{\space\textless\Link[#1]{}{}#1\EndLink\textgreater} +\fi + \usepackage{fontspec} \setmainfont{TeX Gyre Schola} -\setmonofont[Scale=MatchLowercase]{Inconsolatazi4} -\fi +% \setmonofont[Scale=MatchLowercase]{Inconsolatazi4} +\IfFontExistsTF{Noto Sans Mono Regular}{% + \setmonofont[Scale=MatchLowercase]{Noto Sans Mono Regular} +}{\setmonofont{NotoMono-Regular.ttf}} +\usepackage{upquote} + \usepackage{microtype} \providecommand\tightlist{\relax} \title{The \texttt{make4ht} build system} -\author{Michal Hoftich\footnote{\url{michal.h21@gmail.com}}} +\author{Michal Hoftich\authormail{michal.h21@gmail.com}} \date{Version \version\\\gitdate} \begin{document} \maketitle diff --git a/support/make4ht/make4ht-errorlogparser.lua b/support/make4ht/make4ht-errorlogparser.lua new file mode 100644 index 0000000000..b48957c651 --- /dev/null +++ b/support/make4ht/make4ht-errorlogparser.lua @@ -0,0 +1,97 @@ +local m = {} + +local function get_filename(chunk) + local filename = chunk:match("([^\n^%(]+)") + local first = filename:match("^[%./\\]+") + if first then return filename end + return false +end + +local function get_chunks(text) + -- parse log for particular included files + local chunks = {} + -- each file is enclosed in matching () brackets + local newtext = text:gsub("(%b())", function(a) + local chunk = string.sub(a,2,-2) + -- if no filename had been found in the chunk, it is probably not file chunk + -- so just return the original text + local filename = get_filename(chunk) + if not filename then return a end + local children, text = get_chunks(chunk) + table.insert(chunks, {filename = filename, text = text, children = children}) + return "" + end) + return chunks, newtext +end + + +function print_chunks(chunks, level) + local level = level or 0 + local indent = string.rep(" ", level) + for k,v in ipairs(chunks) do + print(indent .. (v.filename or "?"), string.len(v.text)) + print_chunks(v.children, level + 1) + end +end + +local function parse_errors(text) + local lines = {} + local errors = {} + local find_line_no = false + local error_lines = {} + for line in text:gmatch("([^\n]+)") do + lines[#lines+1] = line + end + for i = 1, #lines do + local line = lines[i] + -- error lines start with ! + local err = line:match("^!(.+)") + -- error lines can be on following lines + -- the format is l.number + local lineno = line:match("^l%.([0-9]+)") + if err then + errors[#errors+1] = err + -- we didn't find error line number since previous error, insert + if find_line_no then + error_lines[#error_lines+1] = false + end + find_line_no = true + elseif lineno then + find_line_no = false + error_lines[#error_lines+1] = tonumber(lineno) + end + i = i + 1 + end + return errors, error_lines +end + + +local function get_errors(chunks, errors) + local errors = errors or {} + for _, v in ipairs(chunks) do + local current_errors, error_lines = parse_errors(v.text) + for i, err in ipairs(current_errors) do + table.insert(errors, {filename = v.filename, error = err, line = error_lines[i] }) + end + errors = get_errors(v.children, errors) + end + return errors +end + + +function m.parse(log) + local chunks, newtext = get_chunks(log) + -- save the unparsed text that contains system messages + table.insert(chunks, {text = newtext, children = {}}) + -- print_chunks(chunks) + local errors = get_errors(chunks) + -- for _,v in ipairs(errors) do + -- print("error", v.filename, v.line, v.error) + -- end + return errors, chunks +end + + +m.print_chunks = print_chunks + +return m diff --git a/support/make4ht/make4ht-htlatex.lua b/support/make4ht/make4ht-htlatex.lua new file mode 100644 index 0000000000..214972e8bb --- /dev/null +++ b/support/make4ht/make4ht-htlatex.lua @@ -0,0 +1,75 @@ +local log = logging.new "htlatex" + +local error_logparser = require("make4ht-errorlogparser") + +local Make = Make or {} +-- this function reads the LaTeX log file and tries to detect fatal errors in the compilation +local function testlogfile(par) + local logfile = par.input .. ".log" + local f = io.open(logfile,"r") + if not f then + log:warning("Make4ht: cannot open log file "..logfile) + return 1 + end + local content = f:read("*a") + -- test only the end of the log file, no need to run search functions on everything + local text = content:sub(-1256) + f:close() + -- parse log file for all errors in non-interactive modes + if par.interaction~="errorstopmode" then + -- the error log parsing can be slow, so detect errors first + if content:match("\n!") then + local errors, chunks = error_logparser.parse(content) + if #errors > 0 then + log:error("Compilation errors in the htlatex run") + log:error("Filename", "Line", "Message") + for _, err in ipairs(errors) do + log:error(err.filename or "?", err.line or "?", err.error) + end + end + end + end + -- test for fatal errors + if text:match("No pages of output") or text:match("TeX capacity exceeded, sorry") or text:match("That makes 100 errors") or text:match("Emergency stop") then return 1 end + return 0 +end + + +-- Make this function available in the build files +Make.testlogfile = testlogfile +--env.Make:add("htlatex", "${htlatex} ${latex_par} '\\\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,html]{tex4ht}\\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}") + +-- template for calling LaTeX with tex4ht loaded +Make.latex_command = "${htlatex} --interaction=${interaction} ${latex_par} '\\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,html]{tex4ht}${packages}}".. +"\\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 ${tex4ht_sty_par}.a.b.c.".. +"\\input \"\\detokenize{${tex_file}}\"'" + + +local m = {} + +function m.htlatex(par) + local command = Make.latex_command + local devnull = " > /dev/null 2>&1" + if os.type == "windows" then + command = command:gsub("'",'') + devnull = " > nul 2>&1" + end + par.interaction = par.interaction or "batchmode" + -- remove all terminal output from the batchmode + if par.interaction == "batchmode" then + command = command .. devnull + end + command = command % par + log:info("LaTeX call: "..command) + os.execute(command) + return Make.testlogfile(par) +end + +return m diff --git a/support/make4ht/make4ht-indexing.lua b/support/make4ht/make4ht-indexing.lua new file mode 100644 index 0000000000..5801df509b --- /dev/null +++ b/support/make4ht/make4ht-indexing.lua @@ -0,0 +1,190 @@ + +local M = {} +local log = logging.new "indexing" + +-- Handle accented characters in files created with \usepackage[utf]{inputenc} +-- this code was originally part of https://github.com/michal-h21/iec2utf/ +local enc = {} + +local licrs = {} +local codepoint2utf = unicode.utf8.char +local used_encodings = {} + +-- load inputenc encoding file +local function load_encfiles(f) + local file= io.open(f,"r") + local encodings = file:read("*all") + file:close() + for codepoint, licr in encodings:gmatch('DeclareUnicodeCharacter(%b{})(%b{})') do + local codepoint = codepoint2utf(tonumber(codepoint:sub(2,-2),16)) + local licr= licr:sub(2,-2):gsub('@tabacckludge','') + licrs[licr] = codepoint + end +end + +local function sanitize_licr(l) + return l:gsub(" (.)",function(s) if s:match("[%a]") then return " "..s else return s end end):sub(2,-2) +end + +local load_enc = function(enc) + -- use default encodings if used doesn't provide one + enc = enc or {"T1","T2A","T2B","T2C","T3","T5", "LGR"} + for _,e in pairs(enc) do + local filename = e:lower() .. "enc.dfu" + -- don't process an enc file multiple times + if not used_encodings[filename] then + local dfufile = kpse.find_file(filename) + if dfufile then + load_encfiles(dfufile) + end + end + used_encodings[filename] = true + end +end + + + +local cache = {} + +local get_utf8 = function(input) + local output = input:gsub('\\IeC[%s]*(%b{})',function(iec) + -- remove \protect commands + local iec = iec:gsub("\\protect%s*", "") + local code = cache[iec] or licrs[sanitize_licr(iec)] or '\\IeC '..iec + -- print(iec, code) + cache[iec] = code + return code + end) + return output +end + + +-- parse the idx file produced by tex4ht +-- it replaces the document page numbers by index entry number +-- each index entry can then link to place in the HTML file where the +-- \index command had been used + +local parse_idx = function(content) + -- index entry number + local current_entry = 0 + -- map between index entry number and corresponding HTML file and destination + local map = {} + local buffer = {} + + for line in content:gmatch("([^\n]+)") do + if line:match("^\\beforeentry") then + -- increment index entry number + current_entry = current_entry + 1 + local file, dest = line:match("\\beforeentry{(.-)}{(.-)}") + map[current_entry] = {file = file, dest = dest} + elseif line:match("^\\indexentry") then + -- replace the page number with the current + -- index entry number + local result = line:gsub("{[0-9]+}", "{"..current_entry .."}") + buffer[#buffer+1] = get_utf8(result) + else + buffer[#buffer+1] = line + end + end + -- return table with page to dest map and updated idx file + return {map = map, idx = table.concat(buffer, "\n")} +end + +-- replace page numbers in the ind file with hyperlinks +local fix_idx_pages = function(content, idxobj) + local buffer = {} + local entries = idxobj.map + for line in content:gmatch("([^\n]+)") do + local line = line:gsub("(%s*\\%a+.-%,)(.+)$", function(start,rest) + return start .. rest:gsub("(%d+)", function(page) + local entry = entries[tonumber(page)] + if entry then + -- construct link to the index entry + return "\\Link[" .. entry.file .."]{".. entry.dest .."}{}" .. page .."\\EndLink{}" + else + return page + end + end) + end) + buffer[#buffer+1] = line + end + return table.concat(buffer, "\n") +end + +-- prepare the .idx file produced by tex4ht +-- for use with Xindy or Makeindex +local prepare_idx = function(filename) + local f = io.open(filename, "r") + if not f then return nil, "Cannot open file :".. tostring(filename) end + local content = f:read("*all") + local idx = parse_idx(content) + local idxname = os.tmpname() + local f = io.open(idxname, "w") + f:write(idx.idx) + f:close() + -- return the object with mapping between dummy page numbers + -- and link destinations in the files, and the temporary .idx file + -- these can be used for the processing with the index processor + return idx, idxname +end + +-- add links to a index file +local process_index = function(indname, idx) + local f = io.open(indname, "r") + if not f then return nil, "Cannot open .ind file: " .. tostring(indname) end + local content = f:read("*all") + f:close() + + local newcontent = fix_idx_pages(content, idx) + local f = io.open(indname,"w") + f:write(newcontent) + f:close() + return true +end + +local prepare_tmp_idx = function(par) + par.idxfile = par.idxfile or par.input .. ".idx" + -- construct the .ind name, based on the .idx name + par.indfile = par.indfile or par.idxfile:gsub("idx$", "ind") + load_enc() + -- save hyperlinks and clean the .idx file + local idxdata, newidxfile = prepare_idx(par.idxfile) + if not idxdata then + -- if the prepare_idx function returns nil, the second reuturned value contains error msg + return nil, newidxfile + end + return newidxfile, idxdata +end + +local run_indexing_command = function(command, par) + -- detect command name from the command. It will be the first word + local cmd_name = command:match("^[%a]+") or "indexing" + local xindylog = logging.new(cmd_name) + local newidxfile, idxdata = prepare_tmp_idx(par) + if not newidxfile then + -- the idxdata will contain error message in the case of error + xindylog:warning(idxdata) + return false + end + par.newidxfile = newidxfile + xindylog:debug("Prepared temporary idx file: ", newidxfile) + -- prepare modules + local xindy_call = command % par + xindylog:info(xindy_call) + local status = mkutils.execute(xindy_call) + -- insert correct links to the index + local status, msg = process_index(par.indfile, idxdata) + if not status then xindylog:warning(msg) end + -- remove the temporary idx file + os.remove(newidxfile) +end + +M.get_utf8 = get_utf8 +M.load_enc = load_enc +M.parse_idx = parse_idx +M.fix_idx_pages = fix_idx_pages +M.prepare_idx = prepare_idx +M.process_index = process_index +M.prepare_tmp_idx = prepare_tmp_idx +M.run_indexing_command = run_indexing_command +return M diff --git a/support/make4ht/make4ht-lib.lua b/support/make4ht/make4ht-lib.lua index 2ada398f86..91418d18b5 100644 --- a/support/make4ht/make4ht-lib.lua +++ b/support/make4ht/make4ht-lib.lua @@ -2,6 +2,7 @@ --kpse.set_program_name("luatex") -- module(...,package.seeall) local m = {} +local log = logging.new "make4ht-lib" Make = {} --Make.params = {} @@ -17,13 +18,13 @@ Make.add = function(self,name,fn,par,rep) Make[name] = function(self,p,typ) local params = {} for k,v in pairs(self.params) do params[k] = v end - for k,v in pairs(par) do params[k] = v; print("setting param "..k) end + for k,v in pairs(par) do params[k] = v; log:info("setting param "..k) end local typ = typ or "make" local p = p or {} local fn = fn for k,v in pairs(p) do params[k]=v - print("Adding: ",k,v) + log:info("Adding: ",k,v) end -- print( fn % params) local command = { @@ -50,14 +51,14 @@ Make.run_command = function(self,filename,s) local command = s.command local params = s.params params["filename"] = filename - print("parse_lg process file: "..filename) + log:info("parse_lg process file: "..filename) --for k,v in pairs(params) do print(k..": "..v) end if type(command) == "function" then return command(filename,params) elseif type(command) == "string" then local run = command % params - print("Execute: " .. run) - return os.execute(run) + log:info("Execute: " .. run) + return mkutils.execute(run) end return false, "parse_lg: Command is not string or function" end @@ -95,8 +96,8 @@ Make.image_convert = function(self, images) command(p) elseif type(command) == "string" then local c = command % p - print("Make4ht convert: "..c) - os.execute(c) + log:info("Make4ht convert: "..c) + mkutils.execute(c) end break end @@ -125,7 +126,7 @@ Make.file_matches = function(self, files) msg = msg or "No message given" table.insert(statuses[file],status) if status == false then - print(msg) + log:info(msg) break end end @@ -173,14 +174,14 @@ Make.run = function(self) self.run_count[v.command] = run_count local repetition = v.repetition if repetition and run_count > repetition then - print ("Make4ht: ".. command .." can be executed only "..repetition .."x") + log:warning (command .." can be executed only "..repetition .."x") else - print("Make4ht: " .. command) - local status = os.execute(command) + log:info("executing: " .. command) + local status = mkutils.execute(command) table.insert(return_codes,{name=v.name,status=status}) end else - print("Unknown command type, must be string or function - " ..v.name..": "..type(v.command)) + log:warning("Unknown command type, must be string or function - " ..v.name..": "..type(v.command)) end local correct_exit = params.correct_exit or nil if correct_exit then @@ -188,7 +189,7 @@ Make.run = function(self) local current_status = last_return.status or 0 if current_status ~= correct_exit then local last_name = last_return.name or "unknown" - print("Make4ht: Fatal error. Command "..last_name .." returned exit code "..current_status) + log:fatal("Fatal error. Command "..last_name .." returned exit code "..current_status) os.exit(1) end end @@ -208,7 +209,7 @@ Make.run = function(self) end self:file_matches(files) else - print("No lg file. tex4ht run failed?") + log:warning("No lg file. tex4ht run failed?") end return return_codes end diff --git a/support/make4ht/make4ht-logging.lua b/support/make4ht/make4ht-logging.lua new file mode 100644 index 0000000000..c1d2b24a57 --- /dev/null +++ b/support/make4ht/make4ht-logging.lua @@ -0,0 +1,105 @@ +-- logging system for make4ht +-- inspired by https://github.com/rxi/log.lua +local logging = {} + +local levels = {} +-- level of bugs that should be shown +local show_level = 1 +local max_width = 0 + +logging.use_colors = true + +logging.modes = { + {name = "debug", color = 34}, + {name = "info", color = 32}, + {name = "status", color = 37}, + {name = "warning", color = 33}, + {name = "error", color = 31}, + {name = "fatal", color = 35} +} + +-- prepare table with mapping between mode names and corresponding levels + +function logging.prepare_levels(modes) + local modes = modes or logging.modes + logging.modes = modes + for level, mode in ipairs(modes) do + levels[mode.name] = level + mode.level = level + max_width = math.max(string.len(mode.name), max_width) + end +end + +-- the logging level is set once +function logging.set_level(name) + local level = levels[name] or 1 + show_level = level +end + +function logging.print_msg(header, message, color) + local color = color or 0 + -- use format for collors depending on the use_colors option + local header = "[" .. header .. "]" + local color_format = logging.use_colors and string.format("\27[%im%%s\27[0m%%s", color) or "%s%s" + -- the padding is maximal mode name width + brackets + space + local padded_header = string.format("%-".. max_width + 3 .. "s", header) + print(string.format(color_format, padded_header, message)) +end + +-- +function logging.new(module) + local obj = { + module = module, + output = function(self, output) + -- used for printing of output of commands + if show_level <= (levels[debug] or 1) then + print(output) + end + end + } + obj.__index = obj + -- make a function for each mode + for _, mode in ipairs(logging.modes) do + local name = mode.name + local color = mode.color + obj[name] = function(self, ...) + -- max width is saved in logging.prepare_levels + if mode.level >= show_level then + -- support variable number of parameters + local table_with_holes = table.pack(...) + local table_without_holes = {} + -- trick used to support the nil values in the varargs + -- https://stackoverflow.com/a/7186820/2467963 + for i= 1, table_with_holes.n do + table.insert(table_without_holes, tostring(table_with_holes[i]) or "") + end + local msg = table.concat(table_without_holes, "\t") + logging.print_msg(string.upper(name), string.format("%s: %s", self.module, msg), color) + end + end + end + return setmetatable({}, obj) + +end + + +-- prepare default levels +logging.prepare_levels() + +-- for _, mode in ipairs(logging.modes) do +-- logging.print_msg(mode.name,"xxxx", mode.color) +-- end + +-- local cls = logging.new("sample") +-- cls:warning("hello") +-- cls:error("world") +-- cls:info("set new level") +-- logging.set_level("error") +-- cls:info("level set") +-- cls:error("just print the error") + + +return logging + + + diff --git a/support/make4ht/make4ht-xtpipes.lua b/support/make4ht/make4ht-xtpipes.lua index 78014baff1..3f5b51e07b 100644 --- a/support/make4ht/make4ht-xtpipes.lua +++ b/support/make4ht/make4ht-xtpipes.lua @@ -1,5 +1,6 @@ local M = {} +local log = logging.new "xtpipes" -- find if tex4ht.jar exists in a path local function find_tex4ht_jar(path) local jar_file = path .. "/tex4ht/bin/tex4ht.jar" @@ -38,7 +39,7 @@ end function M.get_xtpipes(selfautoparent) -- make pattern using TeX distro path - local pattern = string.format("java -classpath %s/tex4ht/bin/tex4ht.jar xtpipes -i %s/tex4ht/xtpipes/ -o ${outputfile} ${filename}", selfautoparent, selfautoparent) + local pattern = string.format('java -classpath "%s/tex4ht/bin/tex4ht.jar" xtpipes -i "%s/tex4ht/xtpipes/" -o "${outputfile}" "${filename}"', selfautoparent, selfautoparent) -- call xtpipes on a temporary file local matchfunction = function(filename) -- move the matched file to a temporary file, xtpipes will write it back to the original file @@ -46,19 +47,19 @@ function M.get_xtpipes(selfautoparent) local tmpfile = basename ..".tmp" mkutils.mv(filename, tmpfile) local command = pattern % {filename = tmpfile, outputfile = filename} - print(command) - local status = os.execute(command) + log:info("execute: " ..command) + local status = mkutils.execute(command) if status > 0 then -- if xtpipes failed to process the file, it may mean that it was bad-formed xml -- we can try to make it well-formed using Tidy - local tidy_command = "tidy -utf8 -xml -asxml -q -o ${filename} ${tmpfile}" % {tmpfile = tmpfile, filename = filename} - print("xtpipes failed trying tidy") - print(tidy_command) + local tidy_command = 'tidy -utf8 -xml -asxml -q -o "${filename}" "${tmpfile}"' % {tmpfile = tmpfile, filename = filename} + log:warning("xtpipes failed trying tidy") + log:debug(tidy_command) local status = os.execute(tidy_command) if status > 0 then -- if tidy failed as well, just use the original file -- it will probably produce corrupted ODT file though - print("Tidy failed as well") + log:warning("Tidy failed as well") mkutils.mv(tmpfile, filename) end end diff --git a/support/make4ht/mkparams.lua b/support/make4ht/mkparams.lua index 4db967f270..eff8c99670 100644 --- a/support/make4ht/mkparams.lua +++ b/support/make4ht/mkparams.lua @@ -1,6 +1,7 @@ local lapp = require "lapp-mk4" local mkutils = require "mkutils" local m = {} -- use ugly module system for new lua versions support +local log = logging.new "mkparams" -- these two variables will be used in the version number -- progname will be set in get_args @@ -9,25 +10,28 @@ m.progname = "make4ht" m.version_number = "v0.1" m.optiontext = [[ -${progname} - build system for tex4ht +${progname} - build system for TeX4ht Usage: ${progname} [options] filename ["tex4ht.sty op."] ["tex4ht op."] ["t4ht op"] ["latex op"] Available options: + -a,--loglevel (default status) Set log level. + possible values: debug, info, status, warning, error, fatal -b,--backend (default tex4ht) Backend used for xml generation. possible values: tex4ht or lua4ht -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 html5) Output file format - -h,-- help Display this message + -h,--help Display this 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 - -n,--no-tex4ht Disable dvi file processing with tex4ht command + -n,--no-tex4ht Disable dvi file processing with the tex4ht command -s,--shell-escape Enables running external programs from LaTeX -u,--utf8 For output documents in utf8 encoding - -x,--xetex Use xelatex for document compilation -v,--version Display version number + -x,--xetex Use xelatex for document compilation ]] -- test if the current command line argument should be passed to tex4ht, t4ht or latex @@ -36,11 +40,12 @@ local function is_escapedargument(arg) local ignored_options = {["-h"]=true, ["--help"]=true, ["-v"] = true, ["--version"]=true} if ignored_options[arg] then return false end -- in other cases, match if the argument starts with "-" character - return arg:match("^%-") + return arg:match("^%-.+") end local function get_args(parameters, optiontext) local parameters = parameters or {} parameters.progname = parameters.progname or "make4ht" + parameters.issue_tracker = parameters.issue_tracker or "https://github.com/michal-h21/make4ht/issues" parameters.postparams = parameters.postparams or "" local optiontext = optiontext or m.optiontext parameters.postfile = parameters.postfile or "" @@ -54,7 +59,7 @@ Positional optional argumens: Documentation: https://tug.org/applications/tex4ht/mn.html Issue tracker for tex4ht bugs: https://puszcza.gnu.org.ua/bugs/?group=tex4ht -Issue tracker for make4ht bugs: https://github.com/michal-h21/make4ht/issues +Issue tracker for ${progname} bugs: ${issue_tracker} ]] .. parameters.postfile -- we can pass arguments for tex4ht and t4ht after filename, but it will confuse lapp, thinking that these -- options are for make4ht. this may result in execution error or wrong option parsing @@ -88,6 +93,61 @@ local function get_format_extensions(format_string) return format, extensions end +-- detect if user specified -jobname in arguments to the TeX engine +-- or used the --jobname option for make4ht +local function handle_jobname(input, args) + -- parameters to the TeX engine + local latex_params = {} + local latex_cli_params = args[4] or "" + -- use the jobname as input name if it is specified + local jobname = args.jobname ~="nil" and args.jobname or nil + if jobname or not latex_cli_params:match("%-jobname") then + -- prefer jobname over input + input = jobname or input + -- we must strip out directories from jobname when full path to document is given + input = input:match("([^%/^%\\]+)$") + -- input also cannot contain spaces, replace them with underscores + input = input:gsub("%s", "_") + table.insert(latex_params,"-jobname="..input) + else + -- when user specifies -jobname, we must change name of the input file, + -- in order to be able to process correct dvi file with tex4ht and t4ht + local newinput + -- first contains quotation character or first character of the name + local first, rest = latex_cli_params:match("%-jobname%s*=?%s*(.)(.*)") + if first=='"' then + newinput=rest:match('([^"]+)') + elseif first=="'" then + newinput=rest:match("([^']+)") + elseif type(first)== "string" then + -- if the jobname is unquoted, it cannot contain space + -- join the first character and rest + rest = first.. rest + newinput = rest:match("([^ ]+)") + end + if newinput then + input = newinput + end + end + -- + table.insert(latex_params, latex_cli_params) + return latex_params, input +end + +-- use standard input instead of file if the filename is just `-` +-- return the filename and status if it is a tmp name +local function handle_input_file(filename) + -- return the original file name if it isn't just dash + if filename ~= "-" then return filename, false end + -- generate the temporary name. the added extension is important + local tmp_name = os.tmpname() + local contents = io.read("*all") + local f = io.open(tmp_name, "w") + f:write(contents) + f:close() + return tmp_name, true +end + local function process_args(args) local function get_inserter(args,tb) return function(key, value) @@ -98,6 +158,14 @@ local function process_args(args) end end + -- set error log level + logging.set_level(args.loglevel) + -- the default LaTeX --interaction parameter + local interaction = "batchmode" + if args.loglevel == "debug" then + interaction = "errorstopmode" + end + if args.version ==true then print(string.format("%s version %s", m.progname, m.version_number)) os.exit() @@ -122,36 +190,12 @@ local function process_args(args) local compiler = args.lua and "dvilualatex" or args.xetex and "xelatex --no-pdf" or "latex" - local tex_file = args.filename - local input = mkutils.remove_extension(args.filename) - local latex_params = {} + local tex_file, is_tmp_file = handle_input_file(args.filename) + local input = mkutils.remove_extension(tex_file) + -- the output file name can be influneced using -jobname parameter passed to the TeX engine + local latex_params, input = handle_jobname(input, args) local insert_latex = get_inserter(args,latex_params) insert_latex("shell-escape","-shell-escape") - local latex_cli_params = args[4] or "" - if not latex_cli_params:match("%-jobname") then - -- we must strip out directories from jobname when full path to document is given - input = input:match("([^%/^%\\]+)$") - -- input also cannot contain spaces, replace them with underscores - input = input:gsub("%s", "_") - table.insert(latex_params,"-jobname="..input) - else - -- when user specifies -jobname, we must change name of the input file, - -- in order to be able to process correct dvi file with tex4ht and t4ht - local newinput - local first, rest = latex_cli_params:match("%-jobname=(.)(.*)") - if first=='"' then - newinput=rest:match('([^"]+)') - elseif first=="'" then - newinput=rest:match("([^']+)") - elseif type(first)== "string" then - rest = first.. rest - newinput = rest:match("([^ ]+)") - end - if newinput then - input = newinput - end - end - table.insert(latex_params, latex_cli_params) --table.insert(latex_params,args["shell-escape"] and "-shell-escape") @@ -181,9 +225,9 @@ local function process_args(args) tex4ht = args[2] else tex4ht = args.utf8 and " -cmozhtf -utf8" or "" - if args.xetex then tex4ht = tex4ht .. " -.xdv" end - -- tex4ht = tex4ht .. xdv end + -- set the correct extension for tex4ht if xetex is used + if args.xetex then tex4ht = tex4ht .. " -.xdv" end local t4ht = args[3] or "" @@ -215,19 +259,21 @@ local function process_args(args) ,build_file = build_file ,output_format = outformat ,extensions = extensions + ,is_tmp_file = is_tmp_file + ,interaction = interaction --,t4ht_dir_format=t4ht_dir_format } if outdir then parameters.outdir = outdir end - print("Output dir: ",outdir) - print("Compiler: ", compiler) - print("Latex options: ", table.concat(latex_params," ")) - print("tex4ht.sty :",t4sty) - print("tex4ht",tex4ht) - print("build_file", build_file) + log:info("Output dir: "..outdir) + log:info("Compiler: "..compiler) + log:info("Latex options: ".. table.concat(latex_params," ")) + log:info("tex4ht.sty: "..t4sty) + log:info("tex4ht: "..tex4ht) + log:info("build_file: ".. build_file) if outformat~="nil" then - print("Output format", outformat) + log:info("Output format: ".. outformat) for _, ex in ipairs(extensions) do - print("Extension", ex.type .. ex.name) + log:info("Extension: ".. ex.type .. ex.name) end end return parameters diff --git a/support/make4ht/mkutils.lua b/support/make4ht/mkutils.lua index fd32c91e08..ba1cd6cbb9 100644 --- a/support/make4ht/mkutils.lua +++ b/support/make4ht/mkutils.lua @@ -1,7 +1,10 @@ module(...,package.seeall) +local log = logging.new("mkutils") + local make4ht = require("make4ht-lib") local mkparams = require("mkparams") +local indexing = require("make4ht-indexing") --template engine function interp(s, tab) local tab = tab or {} @@ -61,11 +64,11 @@ end -- searching for converted images function parse_lg(filename) - print("Parse LG") + log:info("Parse LG") local outputimages,outputfiles,status={},{},nil local fonts, used_fonts = {},{} if not file_exists(filename) then - print("Cannot read log file: "..filename) + log:warning("Cannot read log file: "..filename) else local usedfiles={} for line in io.lines(filename) do @@ -114,7 +117,7 @@ local cp_func = os.type == "unix" and "cp" or "copy" function cp(src,dest) local command = string.format('%s "%s" "%s"', cp_func, src, dest) if cp_func == "copy" then command = command:gsub("/",'\\') end - print("Copy: "..command) + log:info("Copy: "..command) os.execute(command) end @@ -123,7 +126,7 @@ function mv(src, dest) local command = string.format('%s "%s" "%s"', mv_func, src, dest) -- fix windows paths if mv_func == "move" then command = command:gsub("/",'\\') end - print("Move: ".. command) + log:info("Move: ".. command) os.execute(command) end @@ -211,18 +214,32 @@ function copy(filename,outfilename) if not used_dir[path] then local to_create, msg = find_directories(parts) if not to_create then - print(msg) + log:warning(msg) return false end used_dir[path] = true local stat, msg = mkdirectories(to_create) - if not stat then print(msg) end + if not stat then log:warning(msg) end end lfs.chdir(currdir) cp(filename, path) return true end +function execute(command) + local f = io.popen(command, "r") + local output = f:read("*all") + -- rc will contain return codes of the executed command + local rc = {f:close()} + -- the status code is on the third position + -- https://stackoverflow.com/a/14031974/2467963 + local status = rc[3] + -- print the command line output only when requested through + -- log level + log:output(output) + return status, output +end + -- find the zip command function find_zip() if io.popen("zip -v","r"):close() then @@ -269,6 +286,7 @@ env.os = os env.io = io env.math = math env.unicode = unicode +env.logging = logging -- it is necessary to use the settings table @@ -317,57 +335,14 @@ env.Make = make4ht.Make env.Make.params = env.settings env.Make:add("test","test the variables: ${tex4ht_sty_par} ${htlatex} ${input} ${config}") --- this function reads the LaTeX log file and tries to detect fatal errors in the compilation -local function testlogfile(par) - local logfile = par.input .. ".log" - local f = io.open(logfile,"r") - if not f then - print("Make4ht: cannot open log file "..logfile) - return 1 - end - local len = f:seek("end") - -- test only the end of the log file, no need to run search functions on everything - local newlen = len - 1256 - -- but the value to seek must be greater than 0 - newlen = (newlen > 0) and newlen or 0 - f:seek("set", newlen) - local text = f:read("*a") - f:close() - if text:match("No pages of output") or text:match("TeX capacity exceeded, sorry") then return 1 end - return 0 -end - - --- Make this function available in the build files -Make.testlogfile = testlogfile ---env.Make:add("htlatex", "${htlatex} ${latex_par} '\\\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,html]{tex4ht}\\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}") - --- template for calling LaTeX with tex4ht loaded -Make.latex_command = "${htlatex} ${latex_par} '\\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,html]{tex4ht}${packages}}".. -"\\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 ${tex4ht_sty_par}.a.b.c.".. -"\\input \"\\detokenize{${tex_file}}\"'" - -env.Make:add("htlatex",function(par) - local command = Make.latex_command - if os.type == "windows" then - command = command:gsub("'",'') - end - command = command % par - print("LaTeX call: "..command) - os.execute(command) - return testlogfile(par) -end +local htlatex = require "make4ht-htlatex" +env.Make:add("htlatex", htlatex.htlatex ,{correct_exit=0}) env.Make:add("latexmk", function(par) + local settings = get_filter_settings "htlatex" or {} + par.interaction = par.interaction or settings.interaction or "batchmode" local command = Make.latex_command par.expanded = command % par -- quotes in latex_command must be escaped, they cause Latexmk error @@ -407,13 +382,13 @@ function load_config(settings, config_name) end local f = io.open(config_name,"r") if not f then - print("Cannot open config file", config_name) + log:info("Cannot open config file", config_name) return env end - print("Using build file", config_name) + log:info("Using build file", config_name) local code = f:read("*all") local fn, msg = run(code,env) - if not fn then print(msg) end + if not fn then log:warning(msg) end assert(fn) -- reload extensions from command line arguments for the "format" parameter for _,v in ipairs(saved_extensions) do @@ -423,25 +398,49 @@ function load_config(settings, config_name) end env.Make:add("xindy", function(par) - -- par.encoding = par.encoding or "utf8" - -- par.language = par.language or "english" - par.idxfile = par.idxfile or par.input .. ".idx" - local modules = par.modules or {par.input} + local xindylog = logging.new "xindy" + local settings = get_filter_settings "xindy" or {} + par.encoding = settings.encoding or par.encoding or "utf8" + par.language = settings.language or par.language or "english" + local modules = settings.modules or par.modules or {} local t = {} for k,v in ipairs(modules) do + xindylog:debug("Loading module: " ..v) t[#t+1] = "-M ".. v end par.moduleopt = table.concat(t, " ") - local xindy_call = "xindy -L ${language} -C ${encoding} ${moduleopt} ${idxfile}" % par - print(xindy_call) - return os.execute(xindy_call) -end, { language = "english", encoding = "utf8"}) + return indexing.run_indexing_command("texindy -L ${language} -C ${encoding} ${moduleopt} -o ${indfile} ${newidxfile}", par) +end, {}) + +env.Make:add("makeindex", function(par) + local makeindxcall = "makeindex ${options} -t ${ilgfile} -o ${indfile} ${newidxfile}" + local settings = get_filter_settings "makeindex" or {} + par.options = settings.options or par.options or "" + par.ilgfile = par.input .. ".ilg" + local status = indexing.run_indexing_command(makeindxcall, par) + return status +end, {}) + +env.Make:add("xindex", function(par) + local xindex_call = "xindex -l ${language} ${options} -o ${indfile} ${newidxfile}" + local settings = get_filter_settings "xindex" or {} + par.options = settings.options or par.options or "" + par.language = settings.language or par.language or "en" + local status = indexing.run_indexing_command(xindex_call, par) + return status +end, {}) + + local function find_lua_file(name) local extension_path = name:gsub("%.", "/") .. ".lua" return kpse.find_file(extension_path, "lua") end +-- for the BibLaTeX support +env.Make:add("biber", "biber ${input}") +env.Make:add("bibtex", "bibtex ${input}") + --- load the output format plugins function load_output_format(format_name) local format_library = "make4ht.formats."..format_name @@ -532,10 +531,10 @@ function load_extensions(extensions, format) if module_names[name] == true then local extension = load_extension(name,format) if extension then - print("Load extension", name) + log:info("Load extension", name) table.insert(extension_table, extension) else - print("Cannot load extension: ".. name) + log:warning("Cannot load extension: ".. name) end end end diff --git a/support/make4ht/readme.tex b/support/make4ht/readme.tex index c9fc0a2f3e..ff1d942d18 100644 --- a/support/make4ht/readme.tex +++ b/support/make4ht/readme.tex @@ -1,125 +1,267 @@ \hypertarget{introduction}{% \section{Introduction}\label{introduction}} -\texttt{make4ht} is a simple build system for \texttt{tex4ht}, \TeX~to -XML converter. It provides a command line tool that drive the conversion -process. It also provides a library which can be used to create -customized conversion tools. An example of such conversion tool is -\href{https://github.com/michal-h21/tex4ebook}{tex4ebook} for conversion -of \TeX~to ePub and other e-book formats. - -\hypertarget{how-it-works}{% -\subsection{How it works}\label{how-it-works}} - -\hypertarget{the-issues-with-default-tex4ht-conversion-commands}{% -\subsubsection{\texorpdfstring{The issues with default \texttt{tex4ht} -conversion -commands}{The issues with default tex4ht conversion commands}}\label{the-issues-with-default-tex4ht-conversion-commands}} - -\texttt{tex4ht} system supports several output formats, most notably -\texttt{XHTML}, \texttt{HTML\ 5} and \texttt{ODT}. The conversion can be -invoked using several commands. These commands invoke LaTeX~or Plain TeX -with special instructions to load \texttt{tex4ht.sty} package. The -\TeX~run produces special \texttt{DVI} file which contains the code for -desired output format. Produced \texttt{DVI} file is then processed and -desired output files are created. - -The basic command provided by \texttt{tex4ht} is named \texttt{htlatex}. -It compiles \LaTeX~ files to \texttt{HTML} with this command sequence: +\texttt{make4ht} is a build system for \TeX4ht, \TeX~to XML converter. +It provides a command line tool that drives the conversion process. It +also provides a library that can be used to create customized conversion +tools. An example of such a tool is +\href{https://github.com/michal-h21/tex4ebook}{tex4ebook}, a tool for +conversion from \TeX~to ePub and other e-book formats. + +The basic conversion from \LaTeX~to \texttt{HTML} using \texttt{make4ht} +can be executed using the following command: + +\begin{verbatim} +$ make4ht filename.tex +\end{verbatim} + +It will produce a file named \texttt{filename.html} if the compilation +goes without fatal errors. + +\hypertarget{clioptions}{% +\section{Command line options}\label{clioptions}} + +\label{sec:clioptions} + +\begin{verbatim} +make4ht - build system for TeX4ht +Usage: +make4ht [options] 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 +-b,--backend (default tex4ht) Backend used for xml generation. + possible values: tex4ht or lua4ht +-c,--config (default xhtml) Custom config file +-d,--output-dir (default "") Output directory +-e,--build-file (default nil) If build filename is different + than `filename`.mk4 +-f,--format (default nil) Output file format +-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 +-n,--no-tex4ht Disable DVI file processing with tex4ht command +-s,--shell-escape Enables running external programs from LaTeX +-u,--utf8 For output documents in utf8 encoding +-x,--xetex Use xelatex for document compilation +-v,--version Print version number + (string) Input filename +\end{verbatim} + +It is still possible to invoke \texttt{make4ht} in the same way as is +invoked \texttt{htlatex}: + +\begin{verbatim} +$ make4ht filename "customcfg, charset=utf-8" "-cunihtf -utf8" "-dfoo" +\end{verbatim} + +Note that this will not use \texttt{make4ht} routines for the output +directory handling. See section \ref{sec:output-dir} for more +information about this issue. To use these routines, change the previous +listing to: + +\begin{verbatim} +$ make4ht -d foo filename "customcfg, charset=utf-8" "-cunihtf -utf8" +\end{verbatim} + +This call has the same effect as the following: + +\begin{verbatim} +$ make4ht -u -c customcfg -d foo filename +\end{verbatim} + +Output directory doesn't have to exist, it will be created +automatically. Specified path can be relative to the current directory, +or absolute: + +\begin{verbatim} +$ make4ht -d use/current/dir/ filename +$ make4ht -d ../gotoparrentdir filename +$ make4ht -d ~/gotohomedir filename +$ make4ht -d c:\documents\windowspathsareworkingtoo filename +\end{verbatim} + +The short options that don't take parameters can be collapsed: + +\begin{verbatim} +$ make4ht -ulc customcfg -d foo filename +\end{verbatim} + +To pass output from the other commands to \texttt{make4ht} use the +\texttt{-} character as a filename. It is best to use this feature +together with the \texttt{-\/-jobname} or \texttt{-j} option. \begin{verbatim} -latex $latex_options 'code for loading tex4ht.sty \input{filename}' -latex $latex_options 'code for loading tex4ht.sty \input{filename}' -latex $latex_options 'code for loading tex4ht.sty \input{filename}' -tex4ht $tex4ht_options filename -t4ht $t4ht_options filename +$ cat hello.tex | make4ht -j world - +\end{verbatim} + +By default, \texttt{make4ht} tries to be quiet, so it hides most of the +command line messages and the output from the executed commands. It will +display only status messages, warnings and errors. The logging level can +be selected using the \texttt{-\/-loglevel} or \texttt{-a} options. If +the compilation fails, it may be useful to display more information +using the \texttt{info} or \texttt{debug} levels. + +\begin{verbatim} +$ make4ht -a debug faulty.tex +\end{verbatim} + +\hypertarget{why-make4ht-htlatex-issues}{% +\section{\texorpdfstring{Why \texttt{make4ht}? -- \texttt{htlatex} +issues}{Why make4ht? -- htlatex issues}}\label{why-make4ht-htlatex-issues}} + +\TeX4ht~system supports several output formats, most notably +\texttt{XHTML}, \texttt{HTML\ 5} and \texttt{ODT}, but it also supports +\texttt{TEI} or \texttt{Docbook}. + +The conversion can be invoked using several scripts, which are +distributed with \TeX4ht. They differ in parameters passed to the +underlying commands. + +These scripts invoke \LaTeX~or Plain \TeX~with special instructions to +load the \texttt{tex4ht.sty} package. The \TeX~run produces a special +\texttt{DVI} file that contains the code for the desired output format. +The produced \texttt{DVI} file is then processed using the +\texttt{tex4ht} command, which in conjunction with the \texttt{t4ht} +command produces the desired output files. + +\hypertarget{passing-command-line-arguments}{% +\subsection{Passing command line +arguments}\label{passing-command-line-arguments}} + +The basic conversion script provided by \TeX4ht~system is named +\texttt{htlatex}. It compiles \LaTeX~ files to \texttt{HTML} with this +command sequence: + +\begin{verbatim} +$ latex $latex_options 'code for loading tex4ht.sty \input{filename}' +$ latex $latex_options 'code for loading tex4ht.sty \input{filename}' +$ latex $latex_options 'code for loading tex4ht.sty \input{filename}' +$ tex4ht $tex4ht_options filename +$ t4ht $t4ht_options filename \end{verbatim} The options for various parts of the system can be passed on the command line: \begin{verbatim} -htlatex filename "tex4ht.sty options" "tex4ht_options" "t4ht_options" "latex_options" +$ htlatex filename "tex4ht.sty options" "tex4ht_options" "t4ht_options" "latex_options" \end{verbatim} For basic \texttt{HTML} conversion it is possible to use the most basic invocation: \begin{verbatim} -htlatex filename.tex +$ htlatex filename.tex \end{verbatim} -It can be much more involved for \texttt{HTML\ 5} output in +It can be much more involved for the \texttt{HTML\ 5} output in \texttt{UTF-8} encoding: \begin{verbatim} -htlatex filename.tex "xhtml,html5,charset=utf-8" "-cmozhtf -utf8" +$ htlatex filename.tex "xhtml,html5,charset=utf-8" " -cmozhtf -utf8" \end{verbatim} \texttt{make4ht} can simplify it: \begin{verbatim} -make4ht -uf html5 filename.tex +$ make4ht -u filename.tex \end{verbatim} -Another issue is the fixed compilation order and hard-coded number of -LaTeX invocations. +The \texttt{-u} option requires the \texttt{UTF-8} encoding. +\texttt{HTML\ 5} is used as the default output format by +\texttt{make4ht}. + +More information about the command line arguments can be found in +section \ref{sec:clioptions}. + +\hypertarget{compilation-sequence}{% +\subsection{Compilation sequence}\label{compilation-sequence}} + +\texttt{htlatex} has a fixed compilation order and a hard-coded number +of \LaTeX~invocations. -When you need to run a program which interact with LaTeX, such as -\texttt{Makeindex} or \texttt{Bibtex}, you need to create a new script -based on \texttt{htlatex}, or run \texttt{htlatex} twice, which means -that LaTeX will be invoked six times. This can lead to significantly -long compilation times. \texttt{make4ht} provides build files and -extensions, which can be used for interaction with external tools. +It is not possible to execute additional commands during the +compilation. When we want to run a program that interacts with \LaTeX, +such as \texttt{Makeindex} or \texttt{Bibtex}, we have two options. The +first option is to create a new script based on \texttt{htlatex} and add +the wanted commands to the modified script. The second option is to +execute \texttt{htlatex}, then the additional and then \texttt{htlatex} +again. The second option means that \LaTeX~will be invoked six times, as +each call to \texttt{htlatex} executes three calls to \LaTeX. This can +lead to significantly long compilation times. -It is also possible to have several compilation modes. When you just add -new text to a document, which doesn't contain cross-references, don't -add new stuff to the table of contents, etc., it is possible to use the -\texttt{draft} mode which will invoke LaTeX only once. It can save quite -a lot of the compilation time: +\texttt{make4ht} provides a solution for this issue using a build file, +or extensions. These can be used for interaction with external tools. + +\texttt{make4ht} also provides compilation modes, which enables to +select commands that should be executed using a command line option. + +There is a built-in \texttt{draft} mode, which invokes \LaTeX~only once, +instead of the default three invocations. It is useful for the +compilations of the document before its final stage, when it is not +important that all cross-references work. It can save quite a lot of the +compilation time: \begin{verbatim} -make4ht -um draft -f html5 filename.tex +$ make4ht -um draft filename.tex \end{verbatim} -There are also issues with a behaviour of the \texttt{t4ht} application. -It reads file \texttt{filename.lg}, generated by \texttt{tex4ht}, where -are instructions about generated files, \texttt{CSS} instructions, calls -to external applications, instructions for image conversions etc. It can -be instructed to copy generated files to some output directory, but it -doesn't preserve directory structure, so when you have images in a -subdirectory, they will be copied to the output directory. Links will be -pointing to a non-existing subdirectory. The following command should -copy all output files to the correct destinations. +More information about the build files can be found in section +\ref{sec:buildfiles}. + +\hypertarget{handling-of-the-generated-files}{% +\subsection{Handling of the generated +files}\label{handling-of-the-generated-files}} + +\label{sec:output-dir} + +There are also issues with the behavior of the \texttt{t4ht} +application. It reads the \texttt{.lg} file generated by the +\texttt{tex4ht} command. This file contains information about the +generated files, \texttt{CSS} instructions, calls to the external +applications, instructions for image conversions, etc. + +\texttt{t4ht} can be instructed to copy the generated files to an output +directory, but it doesn't preserve the directory structure. When the +images are placed in a\\ +subdirectory, they will be copied to the output directory, losing the +directory structure. Links will be pointing to a non-existing +subdirectory. The following command should copy all output files to the +correct destinations. \begin{verbatim} -make4ht -d outputdir filename.tex +$ make4ht -d outputdir filename.tex \end{verbatim} -The image conversion is configured in the -\href{http://www.tug.org/applications/tex4ht/mn35.html\#index35-73001}{env -file}, which has really strange syntax based and the rules are -\href{http://www.tug.org/applications/tex4ht/mn-unix.html\#index27-69005}{os -dependent}. \texttt{make4ht} provides simpler means for the image -conversion in the build files. +\hypertarget{image-conversion-and-post-processing-of-the-generated-files}{% +\subsection{Image conversion and post-processing of the generated +files}\label{image-conversion-and-post-processing-of-the-generated-files}} -With \texttt{make4ht} build files, we have simple mean to fix these -issues. We can change image conversion parameters without the need to -modify the \texttt{env\ file}, or execute actions on the output files. -These actions can be either external programs such as \texttt{xslt} -processors or \texttt{HTML\ tidy} or \texttt{Lua} functions. +\TeX4ht~can convert parts of the document to images. This is useful for +diagrams or complicated math, for example. -The idea is to make system controlled by a build file. Because -\texttt{Lua} interpreter is included in modern TeX distributions and -\texttt{Lua} is ideal language for such task, it was chosen as language -in which the build scripts are written. +By default, the image conversion is configured in a +\href{http://www.tug.org/applications/tex4ht/mn35.html\#index35-73001}{\texttt{.env} +file}. It has a bit of strange syntax, with +\href{http://www.tug.org/applications/tex4ht/mn-unix.html\#index27-69005}{operating +system dependent} rules. \texttt{make4ht} provides simpler means for the +image conversion in the build files. It is possible to change the image +conversion parameters without a need to modify the \texttt{.env} file. +The process is described in section \ref{sec:imageconversion}. + +It is also possible to post-process the generated output files. The +post-processing can be done either using external programs such as +\texttt{XSLT} processors and \texttt{HTML\ Tidy} or using \texttt{Lua} +functions. More information can be found in section +\ref{sec:postprocessing}. \hypertarget{output-file-formats-and-extensions}{% \section{Output file formats and extensions}\label{output-file-formats-and-extensions}} -The default output format used by \texttt{make4ht} is \texttt{html5}. -Different format can be requested using the \texttt{-\/-format} option. +The default output format used by \texttt{make4ht} is \texttt{html5}. A +different format can be requested using the \texttt{-\/-format} option. Supported formats are: \begin{itemize} @@ -149,50 +291,63 @@ The extensions can be enabled or disabled by appending \texttt{+EXTENSION} or \texttt{-EXTENSION} after the output format name: \begin{verbatim} - make4ht -uf html5+tidy filename.tex + $ make4ht -uf html5+tidy filename.tex \end{verbatim} Available extensions: \begin{description} -\item[latexmk\_build] -use \texttt{Latexmk} for \LaTeX~compilation. -\item[tidy] -clean the \texttt{HTML} files using the \texttt{tidy} command. -\item[dvisvgm\_hashes] -efficient generation of SVG pictures using Dvisvgm. It can utilize -multiple processor cores and generates only changed images. \item[common\_filters] clean the output HTML files using filters. \item[common\_domfilters] clean the HTML file using DOM filters. It is more powerful than \texttt{common\_filters}. Used DOM filters are \texttt{fixinlines}, -\texttt{idcolons} and \texttt{joincharacters}. +\texttt{idcolons}, \texttt{joincharacters}, and \texttt{tablerows}. +\item[dvisvgm\_hashes] +efficient generation of SVG pictures using Dvisvgm. It can utilize +multiple processor cores and generates only changed images. \item[join\_colors] load the \texttt{joincolors} domfilter for all HTML files. +\item[latexmk\_build] +use \href{https://ctan.org/pkg/latexmk?lang=en}{Latexmk} for the +\LaTeX~compilation. \item[mathjaxnode] use \href{https://github.com/pkra/mathjax-node-page/}{mathjax-node-page} to convert from MathML code to HTML + CSS or SVG. See \protect\hyperlink{mathjaxsettings}{the available settings}. \item[odttemplate] -automatically load the \texttt{odttemplate} filter. +it automatically loads the \texttt{odttemplate} filter (page +\pageref{sec:odttemplate}). +\item[preprocess\_input] +compilation of the formats supported by +\href{https://yihui.name/knitr/}{Knitr} (\texttt{.Rnw}, \texttt{.Rtex}, +\texttt{.Rmd}, \texttt{.Rrst}) and also Markdown and reStructuredText +formats. It requires \href{https://www.r-project.org/}{R} + +\href{https://yihui.name/knitr/}{Knitr} installation, it requires also +\href{https://pandoc.org/}{Pandoc} for formats based on Markdown or +reStructuredText. \item[staticsite] -build the document in form suitable for static site generators like +build the document in a form suitable for static site generators like \href{https://jekyllrb.com/}{Jekyll}. +\item[tidy] +clean the \texttt{HTML} files using the \texttt{tidy} command. \end{description} \hypertarget{build-files}{% \section{Build files}\label{build-files}} +\label{sec:buildfiles} + \texttt{make4ht} supports build files. These are \texttt{Lua} scripts -which can adjust the build process. You can request external -applications like \texttt{bibtex} or \texttt{makeindex}, pass options to +that can adjust the build process. They can request external +applications like \texttt{BibTeX} or \texttt{Makeindex}, pass options to the commands, modify the image conversion process, or post-process the generated files. \texttt{make4ht} tries to load default build file named as -\texttt{filename\ +\ .mk4\ extension}. You can choose different build -file with \texttt{-e} or \texttt{-\/-build-file} command line option. +\texttt{filename\ +\ .mk4\ extension}. It is possible to select a +different build file with \texttt{-e} or \texttt{-\/-build-file} command +line option. Sample build file: @@ -201,184 +356,228 @@ Make:htlatex() Make:match("html$", "tidy -m -xml -utf8 -q -i ${filename}") \end{verbatim} -\texttt{Make:htlatex()} is preconfigured command for calling LaTeX with -\texttt{tex4ht} loaded on the input file. In this case, it will be -called one time. After compilation, the \texttt{tidy} command is -executed on the output \texttt{HTML} file. +\texttt{Make:htlatex()} is preconfigured command for calling \LaTeX~with +the \texttt{tex4ht.sty} package loaded. In this example, it will be +executed only once. After the compilation, the \texttt{tidy} command is +executed on the output \texttt{HTML} files. -Note that you don't have to call \texttt{tex4ht} and \texttt{t4ht} +Note that it is not necessary to call \texttt{tex4ht} and \texttt{t4ht} commands explicitly in the build file, they are called automatically. \hypertarget{user-commands}{% \subsection{User commands}\label{user-commands}} -You can add more commands like \texttt{Make:htlatex} using +It is possible to add more commands like \texttt{Make:htlatex} using the \texttt{Make:add} command: \begin{verbatim} -Make:add("name", "command", {parameters}, repetition) +Make:add("name", "command", {settings table}, repetition) \end{verbatim} -The \texttt{name} and \texttt{command} parameters are required, rest of -the parameters are optional. +This defines the \texttt{name} command, which can be then executed using +\texttt{Make:name()} command in the build file. -This defines \texttt{name} command, which can be then executed as -\texttt{Make:name()} command. +The \texttt{name} and \texttt{command} parameters are required, the rest +of the parameters are optional. -\hypertarget{provided-commands}{% -\subsubsection{Provided commands}\label{provided-commands}} +The defined command receives a table with settings as a parameter at the +call time. The default settings are provided by \texttt{make4ht}. +Additional settings can be declared in the \texttt{Make:add} commands, +user can also override the default settings when the command is executed +in the build file: -\begin{description} -\item[\texttt{Make:htlatex}] -One call to TeX engine with special configuration for \texttt{tex4ht} -loading. -\item[\texttt{Make:latexmk}] -Use \texttt{Latexmk} for the document compilation. \texttt{tex4ht} will -be loaded automatically. -\item[\texttt{Make:tex4ht}] -Process the \texttt{DVI} file and creates the output files. -\item[\texttt{Make:t4ht}] -Creates the CSS file. -\end{description} +\begin{verbatim} +Make:name({hello="world"}) +\end{verbatim} + +More information about settings, including the default settings provided +by \texttt{make4ht}, can be found in section \ref{sec:settings} on page +\pageref{sec:settings}. -\hypertarget{command-function}{% -\subsubsection{Command function}\label{command-function}} +\hypertarget{the-command-function}{% +\subsubsection{\texorpdfstring{The \texttt{command} +function}{The command function}}\label{the-command-function}} -The \texttt{command} parameter can be either string template or +\label{sec:commandfunction} + +The \texttt{command} parameter can be either a string template or function: \begin{verbatim} Make:add("text", "echo hello, input file: ${input}") -Make:add("function", function(params) - for k, v in pairs(params) do - print(k..": "..v) - end, {custom="Hello world"} -) \end{verbatim} -The template can get variable value from the parameters table using a -\texttt{\$\{var\_name\}} placeholder. Templates are executed using -operating system, so they should invoke existing OS commands. Function -commands may execute system commands using \texttt{os.execute} function. - -\hypertarget{parameters-table}{% -\subsubsection{Parameters table}\label{parameters-table}} +The template can get a variable value from the parameters table using a +\texttt{\$\{var\_name\}} placeholder. Templates are executed using the +operating system, so they should invoke existing OS commands. -\texttt{parameters} parameter is optional, it can be table or -\texttt{nil} value, which should be used if you want to use the -\texttt{repetition} parameter, but don't want to modify the parameters -table. +\hypertarget{the-settings-table-table}{% +\subsubsection{\texorpdfstring{The \texttt{settings\ table} +table}{The settings table table}}\label{the-settings-table-table}} -The table with default parameters is passed to all commands, they can be -accessed from command functions or templates. When you specify your own -parameters in the command definition, these additional parameters are -added to the default parameters table for this particular command. You -can override the default parameters in the parameters table. +The \texttt{settings\ table} parameter is optional. If it is present, it +should be a table with new settings available in the command. It can +also override the default \texttt{make4ht} settings for the defined +command. -The default parameters are following: - -\begin{description} -\item[\texttt{htlatex}] -used compiler -\item[\texttt{input}] -it is output file name in fact -\item[\texttt{tex\_file}] -input TeX file -\item[\texttt{latex\_par}] -parameters to \texttt{latex} -\item[\texttt{packages}] -insert additional LaTeX code which is inserted before -\texttt{\textbackslash{}documentclass}. Useful for passing options to -packages or additional packages loading -\item[\texttt{tex4ht\_sty\_par}] -parameters to \texttt{tex4ht.sty} -\item[\texttt{tex4ht\_par}] -parameters to the \texttt{tex4ht} application -\item[\texttt{t4ht\_par}] -parameters to the \texttt{t4ht} application -\item[\texttt{outdir}] -the output directory -\item[\texttt{repetition}] -limit number of command execution. -\item[\texttt{correct\_exit}] -expected \texttt{exit\ code} from the command. The compilation will be -terminated if the command \texttt{exit\ code} is different. -\end{description} +\begin{verbatim} +Make:add("sample_function", function(params) + for k, v in pairs(params) do + print(k..": "..v) + end, {custom="Hello world"} +) +\end{verbatim} \hypertarget{repetition}{% \subsubsection{Repetition}\label{repetition}} -Repetition is number which specifies a maximal number of executions of -the particular command. This is used for instance for \texttt{tex4ht} -and \texttt{t4ht} commands, as they should be executed only once in the -compilation. They would be executed multiple times if you include them -in the build file because they are called by \texttt{make4ht} by -default. Because these commands allow only one \texttt{repetition}, the -second execution will be blocked. +The \texttt{repetition} parameter specifies the maximum number of +executions of the particular command. This is used for instance for +\texttt{tex4ht} and \texttt{t4ht} commands, as they should be executed +only once in the compilation. They would be executed multiple times when +they are included in the build file, as they are called by +\texttt{make4ht} by default. Because these commands allow only one +\texttt{repetition}, the second execution is blocked. \hypertarget{expected-exit-code}{% \subsubsection{Expected exit code}\label{expected-exit-code}} You can set the expected exit code from a command with a -\texttt{correct\_exit} key in the parameters table. The compilation will -be stopped when the command returns a different exit code. - -This mechanism isn't used for LaTeX (for all TeX engines and formats, in -fact), because it doesn't differentiate between fatal and non-fatal -errors, and it returns the same exit code in all cases. Log parsing is -used because of that, error code \texttt{1} is returned in the case of -fatal error, \texttt{0} is used otherwise. The \texttt{Make.testlogfile} +\texttt{correct\_exit} key in the settings table. The compilation will +be terminated when the command returns a different exit code. + +\begin{verbatim} +Make:add("biber", "biber ${input}", {correct_exit=0}) +\end{verbatim} + +Commands that execute lua functions can return the numerical values +using the \texttt{return} statement. + +This mechanism isn't used for \TeX, because it doesn't differentiate +between fatal and non-fatal errors. It returns the same exit code in all +cases. Because of this, log parsing is used for a fatal error detection +instead. Error code value \texttt{1} is returned in the case of a fatal +error, \texttt{0} is used otherwise. The \texttt{Make.testlogfile} function can be used in the build file to detect compilation errors in the TeX log file. +\hypertarget{provided-commands}{% +\subsection{Provided commands}\label{provided-commands}} + +\begin{description} +\item[\texttt{Make:htlatex}] +One call to the TeX engine with special configuration for loading of the +\texttt{tex4ht.sty} package. +\item[\texttt{Make:latexmk}] +Use \texttt{Latexmk} for the document compilation. \texttt{tex4ht.sty} +will be loaded automatically. +\item[\texttt{Make:tex4ht}] +Process the \texttt{DVI} file and create output files. +\item[\texttt{Make:t4ht}] +Create the CSS file and generate images. +\item[\texttt{Make:biber}] +Process bibliography using the \texttt{biber} command. +\item[\texttt{Make:bibtex}] +Process bibliography using the \texttt{bibtex} command. +\item[\texttt{Make:xindy}] +Generate index using Xindy index processor. +\item[\texttt{Make:makeindex}] +Generate index using the Makeindex command. +\item[\texttt{Make:xindex}] +Generate index using the Xindex command. +\end{description} + \hypertarget{file-matches}{% \subsection{File matches}\label{file-matches}} -Another type of action which can be specified in the build file is -\texttt{match}. It can be called on the generated files: +\label{sec:postprocessing} + +Another type of action that can be specified in the build file is +\texttt{Make:match}. It can be used to post-process the generated files: \begin{verbatim} Make:match("html$", "tidy -m -xml -utf8 -q -i ${filename}") \end{verbatim} -It tests output file names with \texttt{Lua} pattern matching and on -matched items will execute a command or a function, specified in the -second argument. Commands may be specified as strings, the templates -will be expanded, \texttt{\$\{var\_name\}} placeholders will be replaced -with corresponding variables from the \texttt{parameters} table, -described in the previous subsection. One additional variable is -available: \texttt{filename}. It contains the name of the current output -file. - The above example will clean all output \texttt{HTML} files using the \texttt{tidy} command. -If function is used instead, it will get two parameters. The first one -is a current filename, the second one table with parameters. +The \texttt{Make:match} action tests output filenames using a +\texttt{Lua} pattern matching function.\\ +It executes a command or a function, specified in the second argument, +on files whose filenames match the pattern. + +The commands to be executed can be specified as strings. They can +contain \texttt{\$\{var\_name\}} placeholders, which are replaced with +corresponding variables from the \texttt{settings} table. The templating +system was described in subsection \ref{sec:commandfunction}. There is +an additional variable available in this table, called +\texttt{filename}. It contains the name of the current output file. + +If a function is used instead, it will get two parameters. The first one +is the current filename, the second one is the \texttt{settings} table. + +\begin{verbatim} +Make:match("html$", function(filename, settings) + print("Post-processing file: ".. filename) + print("Available settings") + for k,v in pairs(settings) + print(k,v) + end + return true +\end{verbatim} + +end) + +Multiple post-processing actions can be executed on each filename. The +Lua action functions can return an exit code. If the exit code is false, +the execution of the post-processing chain for the current file will be +terminated. \hypertarget{filters}{% \subsubsection{Filters}\label{filters}} -Some default \texttt{match} actions which can be used are available from -the \texttt{make4ht-filter} module. It contains some functions which are -useful for fixing some \texttt{tex4ht} bugs or shortcomings. +\label{sec:filters} -Example: +To make it easier to post-process the generated files using the +\texttt{match} actions, \texttt{make4ht} provides a filtering mechanism +thanks to the \texttt{make4ht-filter} module. + +The \texttt{make4ht-filter} module returns a function that can be used +for the filter chain building. Multiple filters can be chained into a +pipeline. Each filter can modify the string that is passed to it from +the previous filters. The changes are then saved to the processed file. + +Several built-in filters are available, it is also possible to create +new ones. + +Example that use only the built-in filters: \begin{verbatim} local filter = require "make4ht-filter" local process = filter{"cleanspan", "fixligatures", "hruletohr"} Make:htlatex() +Make:match("html$",process) +\end{verbatim} + +Function \texttt{filter} accepts also function arguments, in this case +this function takes file contents as a parameter and modified contents +are returned. + +Example with custom filter: + +\begin{verbatim} +local filter = require "make4ht-filter" +local changea = function(s) return s:gsub("a","z") end +local process = filter{"cleanspan", "fixligatures", changea} Make:htlatex() Make:match("html$",process) \end{verbatim} -The \texttt{make4ht-filter} module return a function which can be used -for the filter chain building. Multiple filters can be chained, each of -them can modify the string which was modified by the previous filters. -The changes are then saved to the processed file. +In this example, spurious span elements are joined, ligatures are +decomposed, and then all letters ``a'' are replaced with ``z'' letters. -Built-in filters are: +Built-in filters are the following: \begin{description} \item[cleanspan] @@ -389,16 +588,15 @@ alternative clean span filter, provided by Nat Kuhn decompose ligatures to base characters \item[hruletohr] \texttt{\textbackslash{}hrule} commands are translated to series of -underscore characters by \texttt{tex4ht}, this filter translate these +underscore characters by \TeX4ht, this filter translates these underscores to \texttt{\textless{}hr\textgreater{}} elements \item[entites] -convert prohibited named entities to numeric entities (currently, only -\texttt{\ }, as it causes validation errors, and \texttt{tex4ht} is -producing it sometimes) +convert prohibited named entities to numeric entities (only +\texttt{\ } currently). \item[fix-links] replace colons in local links and \texttt{id} attributes with underscores. Some cross-reference commands may produce colons in -internal links, which results in validation error. +internal links, which results in a validation error. \item[mathjaxnode] use \href{https://github.com/pkra/mathjax-node-page/}{mathjax-node-page} to convert from MathML code to HTML + CSS or SVG. See @@ -407,39 +605,22 @@ to convert from MathML code to HTML + CSS or SVG. See use styles from another \texttt{ODT} file serving as a template in the current document. It works for the \texttt{styles.xml} file in the \texttt{ODT} file. During the compilation, this file is named as -\texttt{\textbackslash{}jobname.4oy}. +\texttt{\textbackslash{}jobname.4oy}. \label{sec:odttemplate} \item[staticsite] -create HTML files in format suitable for static site generators such as -\href{https://jekyllrb.com/}{Jekyll} +create HTML files in a format suitable for static site generators such +as \href{https://jekyllrb.com/}{Jekyll} \item[svg-height] some SVG images produced by \texttt{dvisvgm} seem to have wrong dimensions. This filter tries to set the correct image size. \end{description} -Function \texttt{filter} accepts also function arguments, in this case -this function takes file contents as a parameter and modified contents -are returned. - -Example: - -\begin{verbatim} -local filter = require "make4ht-filter" -local changea = function(s) return s:gsub("a","z") end -local process = filter{"cleanspan", "fixligatures", changea} -Make:htlatex() -Make:htlatex() -Make:match("html$",process) -\end{verbatim} - -In this example, spurious span elements are joined, ligatures are -decomposed, and then all letters `a' are replaced with `z' letters. - \hypertarget{dom-filters}{% \subsubsection{DOM filters}\label{dom-filters}} -DOM filters use the \href{https://ctan.org/pkg/luaxml}{\texttt{LuaXML}} -library to modify directly the XML object. This enables more powerful -operations than the regex based filters from the previous section. +DOM filters are variants of filters that use the +\href{https://ctan.org/pkg/luaxml}{\texttt{LuaXML}} library to modify +directly the XML object. This enables more powerful operations than the +regex-based filters from the previous section. Example: @@ -455,7 +636,10 @@ Available DOM filters: \item[aeneas] \href{https://www.readbeyond.it/aeneas/}{Aeneas} is a tool for automagical synchronization of text and audio. This filter modifies the -HTML code to support the synchronization. +HTML code to support synchronization. +\item[collapsetoc] +collapse table of contents to contain only top-level sectioning level +and sections on the current page. \item[fixinlines] put all inline elements which are direct children of the \texttt{\textless{}body\textgreater{}} elements to a paragraph. @@ -464,236 +648,105 @@ replace the colon (\texttt{:}) character in internal links and \texttt{id} attributes. They cause validation issues. \item[joincharacters] join consecutive \texttt{\textless{}span\textgreater{}} or -\texttt{\textless{}mn\textgreater{}} elements. +\texttt{\textless{}mn\textgreater{}} elements. This DOM filter +supersedes the \texttt{cleanspan} filter. \item[joincolors] many \texttt{\textless{}span\textgreater{}} elements with unique -\texttt{id} attribute are created when \LaTeX~colors are being used in -the document. CSS rule is added for each of these elements, which may -result in substantial grow of the CSS file. This filter replace these +\texttt{id} attributes are created when \LaTeX~colors are being used in +the document. A CSS rule is added for each of these elements, which may +result in substantial growth of the CSS file. This filter replaces these rules with a common one for elements with the same color value. \item[odtimagesize] set correct dimensions for images in the ODT format. It is loaded by default for the ODT output. +\item[odtpartable] +resolve tables nested inside paragraphs, which is invalid in the ODT +format. +\item[tablerows] +remove spurious rows from HTML tables. \item[t4htlinks] fix hyperlinks in the ODT format. \end{description} -\hypertarget{make4ht-aeneas-config-package}{% -\subsubsection{make4ht-aeneas-config -package}\label{make4ht-aeneas-config-package}} +\hypertarget{image-conversion}{% +\subsection{Image conversion}\label{image-conversion}} -Companion for the \texttt{aeneas} DOM filter is the -\texttt{make4ht-aeneas-config} plugin. It can be used to write Aeneas -configuration file or execute Aeneas on the generated HTML files. +\label{sec:imageconversion} -Available functions: +It is possible to convert parts of the \LaTeX~input as pictures. It can +be used for preserving the appearance of math or diagrams, for example. -\begin{description} -\item[write\_job(parameters)] -write Aenas job configuration to \texttt{config.xml} file. See the -\href{https://www.readbeyond.it/aeneas/docs/clitutorial.html\#processing-jobs}{Aeneas -documentation} for more information about jobs. -\item[execute(parameters)] -execute Aeneas. -\item[process\_files(parameters)] -process the audio and generated subtitle files. -\end{description} +These pictures are stored in a special \texttt{DVI} file, which can be +processed by a \texttt{DVI} to image commands, such as \texttt{dvipng} +or \texttt{dvisvgm}. -By default, the \texttt{smil} file is created. It is assumed that there -is audio file in \texttt{mp3} format named as the TeX file. It is -possible to use different formats and file names using mapping. +This conversion is normally configured in the \texttt{tex4ht.env} file. +This file is system dependent and it has quite an unintuitive syntax. +The configuration is processed by the \texttt{t4ht} application and the +conversion command is called for all pictures. -The configuration options can be passed directly to the functions or set -using \texttt{filter\_settings\ "aeneas-config"\ \{parameters\}} -function. +It is possible to disable \texttt{t4ht} image processing and configure +image conversion in the build file using the \texttt{image} action: -Available parameters: +\begin{verbatim} +Make:image("png$", +"dvipng -bg Transparent -T tight -o ${output} -pp ${page} ${source}") +\end{verbatim} -\begin{description} -\item[lang] -document language. It is interfered from the HTML file, so it is not -necessary to set it. -\item[map] -mapping between HTML, audio and subtitle files. More info bellow. -\item[text\_type] -type of the input. The \texttt{aeneas} DOM filter produces -\texttt{unparsed} text type. -\item[id\_sort] -sorting of id attributes. Default value is \texttt{numeric}. -\item[id\_regex] -regular expression to parse the id attributes. -\item[sub\_format] -generated subtitle format. Default \texttt{smil}. -\end{description} +\texttt{Make:image} takes two parameters, a \texttt{Lua} pattern to +match the image name, and the action. + +Action can be either a string template with the conversion command or a +function that takes a table with parameters as an argument. -Additional parameters for the job configuration file: +There are three parameters: \begin{itemize} \tightlist \item - description -\item - prefix + \texttt{output} - output image filename \item - config\_name + \texttt{source} - \texttt{DVI} file with the pictures \item - keep\_config + \texttt{page} - page number of the converted image \end{itemize} -It is possible to generate multiple HTML files from the LaTeX source. -For example, \texttt{tex4ebook} generates separate file for each chapter -or section. It is possible to set options for each HTML file, in -particular names of the corresponding audio files. This mapping is done -using \texttt{map} parameter. +\hypertarget{the-mode-variable}{% +\subsection{\texorpdfstring{The \texttt{mode} +variable}{The mode variable}}\label{the-mode-variable}} -Example: +The \texttt{mode} variable available in the build process contains +contents of the \texttt{-\/-mode} command line option. It can be used to +run some commands conditionally. For example: \begin{verbatim} -filter_settings "aeneas-config" { - map = { - ["sampleli1.html"] = {audio_file="sample.mp3"}, - ["sample.html"] = false - } -} + if mode == "draft" then + Make:htlatex{} + else + Make:htlatex{} + Make:htlatex{} + Make:htlatex{} + end \end{verbatim} -Table keys are the configured file names. It is necessary to insert them -as \texttt{{[}"filename.html"{]}}, because of Lua syntax rules. +In this example (which is the default configuration used by +\texttt{make4ht}), \LaTeX~is called only once when \texttt{make4ht} is +called with the \texttt{draft} mode: -This example maps audio file \texttt{sample.mp3} to a section subpage. -The main HTML file, which may contain title and table of contents -doesn't have a corresponding audio file. +\begin{verbatim} +make4ht -m draft filename +\end{verbatim} -Filenames of the sub files corresponds to the chapter numbers, so they -are not stable when a new chapter is added. It is possible to request -file names interfered from the chapter titles using the -\texttt{sec-filename} option or \texttt{tex4ht}. +\hypertarget{the-settings-table}{% +\subsection{\texorpdfstring{The \texttt{settings} +table}{The settings table}}\label{the-settings-table}} -Available \texttt{map} options: +\label{sec:settings} -\begin{description} -\item[audio\_file] -the corresponding audio file -\item[sub\_file] -name of the generated subtitle file -\end{description} - -The following options are same as their counter-parts from the main -parameters table and generally don't need to be set: - -\begin{itemize} -\tightlist -\item - prefix -\item - file\_desc -\item - file\_id -\item - text\_type -\item - id\_sort -\item - id\_prefix -\item - sub\_format -\end{itemize} - -Full example: - -\begin{verbatim} -local domfilter = require "make4ht-domfilter" -local aeneas_config = require "make4ht-aeneas-config" - -filter_settings "aeneas-config" { - map = { - ["krecekli1.xhtml"] = {audio_file="krecek.mp3"}, - ["krecek.xhtml"] = false - } -} - -local process = domfilter {"aeneas"} -Make:match("html$", process) - -if mode == "draft" then - aeneas_config.process_files {} -else - aeneas_config.execute {} -end -\end{verbatim} - -\hypertarget{image-conversion}{% -\subsection{Image conversion}\label{image-conversion}} - -It is possible to convert parts of LaTeX input to pictures, it is used -for example for math or diagrams in \texttt{tex4ht}. - -These pictures are stored in a special \texttt{dvi} file, which can be -processed by the \texttt{dvi\ to\ image} commands. - -This conversion is normally configured in the \texttt{env\ file}, which -is system dependent and which has a bit unintuitive syntax. This -configuration is processed by the \texttt{t4ht} application and -conversion commands are called for all pictures. - -It is possible to disable \texttt{t4ht} image processing and configure -image conversion in the build file: - -\begin{verbatim} -Make:image("png$", -"dvipng -bg Transparent -T tight -o ${output} -pp ${page} ${source}") -\end{verbatim} - -\texttt{Make:image} takes two parameters, pattern to match image name -and action. Action can be either string template with conversion -command, or function which takes a table with parameters as an argument. - -There are three parameters: - -\begin{itemize} -\tightlist -\item - \texttt{output} - output image file name -\item - \texttt{source} - \texttt{dvi} file with the pictures -\item - \texttt{page} - page number of the converted image -\end{itemize} - -\hypertarget{the-mode-variable}{% -\subsection{\texorpdfstring{The \texttt{mode} -variable}{The mode variable}}\label{the-mode-variable}} - -There is global \texttt{mode} variable available in the build file. It -contains contents of the \texttt{-\/-mode} command line option. It can -be used to run some commands conditionally. For example: - -\begin{verbatim} - if mode == "draft" then - Make:htlatex{} - else - Make:htlatex{} - Make:htlatex{} - Make:htlatex{} - end -\end{verbatim} - -In this example (which is the default configuration used by -\texttt{make4ht}), LaTeX is called only once when \texttt{make4ht} is -called with \texttt{draft} mode: - -\begin{verbatim} -make4ht -m draft filename -\end{verbatim} - -\hypertarget{the-settings-table}{% -\subsection{\texorpdfstring{The \texttt{settings} -table}{The settings table}}\label{the-settings-table}} - -You may want to access to the parameters also outside commands, file -matches and image conversion functions. For example, if you want to -convert your file to the \texttt{OpenDocument\ Format\ (ODT)}, you can -use the following settings, based on the \texttt{oolatex} command: +It is possible to access the parameters outside commands, file matches +and image conversion functions. For example, to convert the document to +the \texttt{OpenDocument\ Format\ (ODT)}, the following settings can be +used. They are based on the \texttt{oolatex} command: \begin{verbatim} settings.tex4ht_sty_par = settings.tex4ht_sty_par ..",ooffice" @@ -701,7 +754,11 @@ settings.tex4ht_par = settings.tex4ht_par .. " ooffice/! -cmozhtf" settings.t4ht_par = settings.t4ht_par .. " -cooxtpipes -coo " \end{verbatim} -There are some functions to ease access to the settings: +(Note that it is possible to use the \texttt{-\/-format\ odt} option +which is superior to the previous code. This example is intended just as +an illustration) + +There are some functions to simplify access to the settings: \begin{description} \item[\texttt{set\_settings\{parameters\}}] @@ -714,8 +771,8 @@ set settings for a filter get settings for a filter \end{description} -Using these functions, it is possible to simplify the settings for the -\texttt{ODT} format: +For example, it is possible to simplify the sample from the previous +code listings: \begin{verbatim} settings_add { @@ -734,8 +791,8 @@ filter_settings "test" { } \end{verbatim} -These settings can be read in the extensions and filters using -\texttt{get\_filter\_settings}: +These settings can be retrieved in the extensions and filters using the +\texttt{get\_filter\_settings} function: \begin{verbatim} function test(input) @@ -746,14 +803,153 @@ end \end{verbatim} +\hypertarget{default-settings}{% +\subsubsection{Default settings}\label{default-settings}} + +The default parameters are the following: + +\begin{description} +\item[\texttt{htlatex}] +used \TeX~engine +\item[\texttt{input}] +content of \texttt{\textbackslash{}jobname}, see also the +\texttt{tex\_file} parameter. +\item[\texttt{interaction}] +interaction mode for the \TeX~engine. The default value is +\texttt{batchmode} to suppress user input on compilation errors. It also +suppresses most of the \TeX~ compilation log output. Use the +\texttt{errorstopmode} for the default behavior. +\item[\texttt{tex\_file}] +input \TeX~filename +\item[\texttt{latex\_par}] +command line parameters to the \TeX~engine +\item[\texttt{packages}] +additional \LaTeX~code inserted before +\texttt{\textbackslash{}documentclass}. Useful for passing options to +packages used in the document or to load additional packages. +\item[\texttt{tex4ht\_sty\_par}] +options for \texttt{tex4ht.sty} +\item[\texttt{tex4ht\_par}] +command line options for the \texttt{tex4ht} command +\item[\texttt{t4ht\_par}] +command line options for the \texttt{t4ht} command +\item[\texttt{outdir}] +the output directory +\item[\texttt{correct\_exit}] +expected \texttt{exit\ code} from the command. The compilation will be +terminated if the exit code of the executed command has a different +value. +\end{description} + +\hypertarget{configfile}{% +\section{Configuration file}\label{configfile}} + +It is possible to globally modify the build settings using the +configuration file. It is a special version of a build file where the +global settings can be set. + +Common tasks for the configuration file can be a declaration of the new +commands, loading of the default filters or specification of a default +build sequence. + +One additional functionality not available in the build files are +commands for enabling and disabling of extensions. + +\hypertarget{location}{% +\subsection{Location}\label{location}} + +The configuration file can be saved either in the +\texttt{\$HOME/.config/make4ht/config.lua} file, or in the +\texttt{.make4ht} file placed in the current directory or it's parent +directories (up to the \texttt{\$HOME} directory). + +\hypertarget{additional-commands}{% +\subsection{Additional commands}\label{additional-commands}} + +There are two additional commands: + +\begin{description} +\item[\texttt{Make:enable\_extension(name)}] +require extension +\item[\texttt{Make:disable\_extension(name)}] +disable extension +\end{description} + +\hypertarget{example}{% +\subsection{Example}\label{example}} + +The following example of the configuration file adds support for the +\texttt{biber} command, requires \texttt{common\_domfilters} extension +and requires MathML output for math. + +\begin{verbatim} +Make:add("biber", "biber ${input}") +Make:enable_extension "common_domfilters" +settings_add { + tex4ht_sty_par =",mathml" +} +\end{verbatim} + \hypertarget{list-of-available-settings-for-filters-and-extensions.}{% -\subsection{List of available settings for filters and +\section{List of available settings for filters and extensions.}\label{list-of-available-settings-for-filters-and-extensions.}} -These settings may be set using \texttt{filter\_settings} function. +These settings may be set using \texttt{filter\_settings} function in a +build file or in the \texttt{make4ht} configuration file. + +\hypertarget{indexing-commands}{% +\subsection{Indexing commands}\label{indexing-commands}} + +The indexing commands (like \texttt{xindy} or \texttt{makeindex}) use +some common settings. + +\begin{description} +\item[idxfile] +name of the \texttt{.idx} file. Default value is +\texttt{\textbackslash{}jobname.idx}. +\item[indfile] +name of the \texttt{.ind} file. Default value is the same as +\texttt{idxfile} with the file extension changed to \texttt{.ind}. +\end{description} + +Each indexing command can have some additional settings. + +\hypertarget{the-xindy-command}{% +\subsubsection{\texorpdfstring{The \texttt{xindy} +command}{The xindy command}}\label{the-xindy-command}} + +\begin{description} +\item[encoding] +text encoding of the \texttt{.idx} file. Default value is \texttt{utf8}. +\item[language] +index language. Default language is English. +\item[modules] +table with names of additional \texttt{Xindy} modules to be used. +\end{description} + +\hypertarget{the-makeindex-command}{% +\subsubsection{\texorpdfstring{The \texttt{makeindex} +command}{The makeindex command}}\label{the-makeindex-command}} + +options + +: additional command line options for the Makeindex command. + +\hypertarget{the-xindex-command}{% +\subsubsection{\texorpdfstring{The \texttt{xindex} +command}{The xindex command}}\label{the-xindex-command}} + +options + +: additional command line options for the Xindex command. + +\begin{description} +\item[language] +document language +\end{description} \hypertarget{the-tidy-extension}{% -\subsubsection{\texorpdfstring{The \texttt{tidy} +\subsection{\texorpdfstring{The \texttt{tidy} extension}{The tidy extension}}\label{the-tidy-extension}} \begin{description} @@ -762,13 +958,37 @@ command line options for the \texttt{tidy} command. Default value is \texttt{-m\ -utf8\ -w\ 512\ -q}. \end{description} +\hypertarget{the-collapsetoc-dom-filter}{% +\subsection{\texorpdfstring{The \texttt{collapsetoc} dom +filter}{The collapsetoc dom filter}}\label{the-collapsetoc-dom-filter}} + +\begin{description} +\item[\texttt{toc\_query}] +CSS selector for selecting the table of contents container. +\item[\texttt{title\_query}] +CSS selector for selecting all elements that contain the section ID +attribute. +\item[\texttt{toc\_levels}] +table containing a hierarchy of classes used in TOC +\end{description} + +Default values: + +\begin{verbatim} +filter_settings "collapsetoc" { + toc_query = ".tableofcontents", + title_query = ".partHead a, .chapterHead a, .sectionHead a, .subsectionHead a", + toc_levels = {"partToc", "chapterToc", "sectionToc", "subsectionToc", "subsubsectionToc"} +} +\end{verbatim} + \hypertarget{the-fixinlines-dom-filter}{% -\subsubsection{\texorpdfstring{The \texttt{fixinlines} dom +\subsection{\texorpdfstring{The \texttt{fixinlines} dom filter}{The fixinlines dom filter}}\label{the-fixinlines-dom-filter}} \begin{description} \item[inline\_elements] -table of inline elements which shouldn't be direct descendants of the +table of inline elements that shouldn't be direct descendants of the \texttt{body} element. The element names should be table keys, the values should be true. \end{description} @@ -780,23 +1000,24 @@ filter_settings "fixinlines" {inline_elements = {a = true, b = true}} \end{verbatim} \hypertarget{the-joincharacters-dom-filter}{% -\subsubsection{\texorpdfstring{The \texttt{joincharacters} dom +\subsection{\texorpdfstring{The \texttt{joincharacters} dom filter}{The joincharacters dom filter}}\label{the-joincharacters-dom-filter}} \begin{description} -\item[charelements] -table of elements which should be joined if several instances with the -same value of \texttt{class} attribute are side by side. +\item[charclasses] +table of elements that should be concatenated when two or more of such +elements with the same value of the \texttt{class} attribute are placed +one after another. \end{description} Example \begin{verbatim} -filter_settings "joincharacters" { charclases = { span=true, mn = true}} +filter_settings "joincharacters" { charclasses = { span=true, mn = true}} \end{verbatim} \hypertarget{mathjaxsettings}{% -\subsubsection{\texorpdfstring{The \texttt{mathjaxnode} +\subsection{\texorpdfstring{The \texttt{mathjaxnode} filter}{The mathjaxnode filter}}\label{mathjaxsettings}} \begin{description} @@ -815,12 +1036,12 @@ filter_settings "mathjaxnode" { \begin{description} \item[cssfilename] -\texttt{mjpage} puts some CSS code into the HTML pages. +the \texttt{mjpage} command puts some CSS code into the HTML pages. \texttt{mathjaxnode} extracts this information and saves it to a standalone CSS file. Default CSS filename is \texttt{mathjax-chtml.css} \item[fontdir] -directory with MathJax font files. This option enables use of local -fonts, which is usefull in Epub conversion, for example. The font +directory with MathJax font files. This option enables the use of local +fonts, which is useful in the conversion to ePub, for example. The font directory should be sub-directory of the current directory. Only TeX font is supported at the moment. \end{description} @@ -833,37 +1054,22 @@ filter_settings "mathjaxnode" { } \end{verbatim} -\hypertarget{the-aeneas-filter}{% -\subsubsection{\texorpdfstring{The \texttt{aeneas} -filter}{The aeneas filter}}\label{the-aeneas-filter}} - -\begin{description} -\item[skip\_elements] -List of CSS selectors that match elements which shouldn't be processed. -Default value: \texttt{\{\ "math",\ "svg"\}}. -\item[id\_prefix] -prefix used in the ID attribute forming. -\item[sentence\_match] -Lua pattern used to match a sentence. Default value: -\texttt{"({[}\^{}\%.\^{}\%?\^{}!{]}*)({[}\%.\%?!{]}?)"}. -\end{description} - \hypertarget{the-staticsite-filter-and-extension}{% -\subsubsection{\texorpdfstring{The \texttt{staticsite} filter and +\subsection{\texorpdfstring{The \texttt{staticsite} filter and extension}{The staticsite filter and extension}}\label{the-staticsite-filter-and-extension}} \begin{description} \item[site\_root] directory where generated files should be copied. \item[map] -table where keys are patterns that match filenames, value contains -destination directoryfor matched files, relative to the -\texttt{site\_root} (it is possible to use \texttt{..} to swich to -parent directory). +a hash table where keys contain patterns that match filenames and values +contain destination directory for the matched files. The destination +directories are relative to the \texttt{site\_root} (it is possible to +use \texttt{..} to switch to a parent directory). \item[file\_pattern] -pattern used for filename generation. It is possible to use string -templates and format strings for \texttt{os.date} function. Default -value of \texttt{\%Y-\%m-\%d-\$\{input\}} creates names in the form of +a pattern used for filename generation. It is possible to use string +templates and format strings for \texttt{os.date} function. The default +pattern \texttt{\%Y-\%m-\%d-\$\{input\}} creates names in the form of \texttt{YYYY-MM-DD-file\_name}. \item[header] table with variables to be set in the YAML header in HTML files. If the @@ -891,7 +1097,7 @@ filter_settings "staticsite" { \end{verbatim} \hypertarget{the-dvisvgm_hashes-extension}{% -\subsubsection{\texorpdfstring{The \texttt{dvisvgm\_hashes} +\subsection{\texorpdfstring{The \texttt{dvisvgm\_hashes} extension}{The dvisvgm\_hashes extension}}\label{the-dvisvgm_hashes-extension}} \begin{description} @@ -899,18 +1105,18 @@ extension}{The dvisvgm\_hashes extension}}\label{the-dvisvgm_hashes-extension}} command line options for Dvisvgm. The default value is \texttt{-n\ -\/-exact\ -c\ 1.15,1.15}. \item[cpu\_cnt] -number of processor cores used for conversion. The extension tries to -detect the available cores automatically by default. +the number of processor cores used for the conversion. The extension +tries to detect the available cores automatically by default. \item[parallel\_size] -number of pages used in each Dvisvgm call. The extension detects changed -pages in the DVI file and construct multiple calls to Dvisvgm with only -changed pages. +the number of pages used in each Dvisvgm call. The extension detects +changed pages in the DVI file and constructs multiple calls to Dvisvgm +with only changed pages. \item[scale] SVG scaling. \end{description} \hypertarget{the-odttemplate-filter-and-extension}{% -\subsubsection{\texorpdfstring{The \texttt{odttemplate} filter and +\subsection{\texorpdfstring{The \texttt{odttemplate} filter and extension}{The odttemplate filter and extension}}\label{the-odttemplate-filter-and-extension}} \begin{description} @@ -920,108 +1126,175 @@ filename of the template \texttt{ODT} file \texttt{odttemplate} can also get the template filename from the \texttt{odttemplate} option from \texttt{tex4ht\_sty\_par} parameter. It -can be set useing following command line call, for example: +can be set using the following command line call: \begin{verbatim} make4ht -f odt+odttemplate filename.tex "odttemplate=template.odt" \end{verbatim} -\hypertarget{configfile}{% -\section{Configuration file}\label{configfile}} +\hypertarget{the-aeneas-filter}{% +\subsection{\texorpdfstring{The \texttt{aeneas} +filter}{The aeneas filter}}\label{the-aeneas-filter}} -It is possible to globally modify the build settings using the -configuration file. New compilation commands can be added, extensions -can be loaded or disabled and settings can be set. +\begin{description} +\item[skip\_elements] +List of CSS selectors that match elements that shouldn't be processed. +Default value: \texttt{\{\ "math",\ "svg"\}}. +\item[id\_prefix] +prefix used in the ID attribute forming. +\item[sentence\_match] +Lua pattern used to match a sentence. Default value: +\texttt{"({[}\^{}\%.\^{}\%?\^{}!{]}*)({[}\%.\%?!{]}?)"}. +\end{description} -\hypertarget{location}{% -\subsection{Location}\label{location}} +\hypertarget{the-make4ht-aeneas-config-package}{% +\subsection{\texorpdfstring{The \texttt{make4ht-aeneas-config} +package}{The make4ht-aeneas-config package}}\label{the-make4ht-aeneas-config-package}} -The configuration file can be saved either in -\texttt{\$HOME/.config/make4ht/config.lua} or in \texttt{.make4ht} in -the current directory or it's parents (up to \texttt{\$HOME}). +Companion for the \texttt{aeneas} DOM filter is the +\texttt{make4ht-aeneas-config} plugin. It can be used to write the +Aeneas configuration file or execute Aeneas on the generated HTML files. -\hypertarget{additional-commands}{% -\subsection{Additional commands}\label{additional-commands}} +Available functions: -There are two additional commands: +\begin{description} +\item[write\_job(parameters)] +write Aenas job configuration to \texttt{config.xml} file. See the +\href{https://www.readbeyond.it/aeneas/docs/clitutorial.html\#processing-jobs}{Aeneas +documentation} for more information about jobs. +\item[execute(parameters)] +execute Aeneas. +\item[process\_files(parameters)] +process the audio and generated subtitle files. +\end{description} + +By default, a \texttt{SMIL} file is created. It is assumed that there is +an audio file in the \texttt{mp3} format, named as the \TeX~file. It is +possible to use different formats and filenames using mapping. + +The configuration options can be passed directly to the functions or set +using \texttt{filter\_settings\ "aeneas-config"\ \{parameters\}} +function. + +\hypertarget{available-parameters}{% +\subsubsection{Available parameters}\label{available-parameters}} \begin{description} -\item[\texttt{Make:enable\_extension(name)}] -require extension -\item[\texttt{Make:disable\_extension(name)}] -disable extension +\item[lang] +document language. It is interfered from the HTML file, so it is not +necessary to set it. +\item[map] +mapping between HTML, audio and subtitle files. More info below. +\item[text\_type] +type of input. The \texttt{aeneas} DOM filter produces an +\texttt{unparsed} text type. +\item[id\_sort] +sorting of id attributes. The default value is \texttt{numeric}. +\item[id\_regex] +regular expression to parse the id attributes. +\item[sub\_format] +generated subtitle format. The default value is \texttt{smil}. \end{description} -\hypertarget{example}{% -\subsection{Example}\label{example}} +\hypertarget{additional-parameters-for-the-job-configuration-file}{% +\subsubsection{Additional parameters for the job configuration +file}\label{additional-parameters-for-the-job-configuration-file}} + +\begin{itemize} +\tightlist +\item + description +\item + prefix +\item + config\_name +\item + keep\_config +\end{itemize} + +It is possible to generate multiple HTML files from the \LaTeX~source. +For example, \texttt{tex4ebook} generates a separate file for each +chapter or section. It is possible to set options for each HTML file, in +particular names of the corresponding audio files. This mapping is done +using the \texttt{map} parameter. -The following configuration add support for the \texttt{biber} command, -requires \texttt{common\_domfilters} extension and requires MathML -output for math. +Example: \begin{verbatim} -Make:add("biber", "biber ${input}") -Make:enable_extension "common_domfilters" -settings_add { - tex4ht_sty_par =",mathml" +filter_settings "aeneas-config" { + map = { + ["sampleli1.html"] = {audio_file="sample.mp3"}, + ["sample.html"] = false + } } \end{verbatim} -\hypertarget{command-line-options}{% -\section{Command line options}\label{command-line-options}} +Table keys are the configured filenames. It is necessary to insert them +as \texttt{{[}"filename.html"{]}}, because of Lua syntax rules. -\begin{verbatim} -make4ht - build system for tex4ht -Usage: -make4ht [options] filename ["tex4ht.sty op." "tex4ht op." - "t4ht op" "latex op"] --b,--backend (default tex4ht) Backend used for xml generation. - possible values: tex4ht or lua4ht --c,--config (default xhtml) Custom config file --d,--output-dir (default "") Output directory --e,--build-file (default nil) If build file name is different - than `filename`.mk4 --f,--format (default nil) Output file format --l,--lua Use lualatex for document compilation --m,--mode (default default) Switch which can be used in the makefile --n,--no-tex4ht Disable dvi file processing with tex4ht command --s,--shell-escape Enables running external programs from LaTeX --u,--utf8 For output documents in utf8 encoding --v,--version Print version number --x,--xetex Use xelatex for document compilation - (string) Input file name -\end{verbatim} +This example maps audio file \texttt{sample.mp3} to a section subpage. +The main HTML file, which may contain title and table of contents +doesn't have a corresponding audio file. -You can still invoke \texttt{make4ht} in the same way as -\texttt{htlatex}: +Filenames of the subfiles correspond to the chapter numbers, so they are +not stable when a new chapter is added. It is possible to request +filenames derived from the chapter titles using the +\texttt{sec-filename} option for \texttt{tex4ht.sty}. -\begin{verbatim} -make4ht filename "customcfg, charset=utf-8" "-cunihtf -utf8" "-dfoo" -\end{verbatim} +\hypertarget{available-map-options}{% +\subsubsection{\texorpdfstring{Available \texttt{map} +options}{Available map options}}\label{available-map-options}} -Note that this will not use \texttt{make4ht} routines for output -directory making and copying. If you want to use them, change the line -above to: +\begin{description} +\item[audio\_file] +the corresponding audio file +\item[sub\_file] +name of the generated subtitle file +\end{description} -\begin{verbatim} -make4ht -d foo filename "customcfg, charset=utf-8" "-cunihtf -utf8" -\end{verbatim} +The following options are the same as their counterparts from the main +parameters table and generally, don't need to be set: -This call has the same effect as the following: +\begin{itemize} +\tightlist +\item + prefix +\item + file\_desc +\item + file\_id +\item + text\_type +\item + id\_sort +\item + id\_prefix +\item + sub\_format +\end{itemize} + +\hypertarget{full-example}{% +\subsubsection{Full example}\label{full-example}} \begin{verbatim} -make4ht -u -c customcfg -d foo filename -\end{verbatim} +local domfilter = require "make4ht-domfilter" +local aeneas_config = require "make4ht-aeneas-config" -Output directory doesn't have to exist, it will be created -automatically. Specified path can be relative to current directory, or -absolute: +filter_settings "aeneas-config" { + map = { + ["krecekli1.xhtml"] = {audio_file="krecek.mp3"}, + ["krecek.xhtml"] = false + } +} -\begin{verbatim} -make4ht -d use/current/dir/ filename -make4ht -d ../gotoparrentdir filename -make4ht -d ~/gotohomedir filename -make4ht -d c:\documents\windowspathsareworkingtoo filename +local process = domfilter {"aeneas"} +Make:match("html$", process) + +if mode == "draft" then + aeneas_config.process_files {} +else + aeneas_config.execute {} +end \end{verbatim} \hypertarget{troubleshooting}{% @@ -1041,23 +1314,23 @@ make4ht:unrecognized parameter: i It may be caused by a following \texttt{make4ht} invocation: \begin{verbatim} -make4ht hello.tex "customcfg,charset=utf-8" "-cunihtf -utf8" -d foo +$ make4ht hello.tex "customcfg,charset=utf-8" "-cunihtf -utf8" -d foo \end{verbatim} The command line option parser is confused by mixing options for -\texttt{make4ht} and \texttt{tex4ht} in this case and tries to interpret -the \texttt{-cunihtf\ -utf8}, which are options for \texttt{tex4ht} -command as \texttt{make4ht} options. To fix that, you can either move -the \texttt{-d\ foo} directly after \texttt{make4ht} command: +\texttt{make4ht} and \TeX4ht~in this case. It tries to interpret the +\texttt{-cunihtf\ -utf8}, which are options for the \texttt{tex4ht} +command, as \texttt{make4ht} options. To fix that, try to move the +\texttt{-d\ foo} directly after the \texttt{make4ht} command: \begin{verbatim} -make4ht -d foo hello.tex "customcfg,charset=utf-8" "-cunihtf -utf8" +$ make4ht -d foo hello.tex "customcfg,charset=utf-8" "-cunihtf -utf8" \end{verbatim} -Another option is to add a space before \texttt{tex4ht} options: +Another option is to add a space before the \texttt{tex4ht} options: \begin{verbatim} -make4ht hello.tex "customcfg,charset=utf-8" " -cunihtf -utf8" -d foo +$ make4ht hello.tex "customcfg,charset=utf-8" " -cunihtf -utf8" -d foo \end{verbatim} The former way is preferable, though. @@ -1066,10 +1339,10 @@ The former way is preferable, though. \subsection{Filenames containing spaces}\label{filenames-containing-spaces}} -\texttt{tex4ht} cannot handle filenames containing spaces. -\texttt{make4ht} thus replaces spaces in input file names with -underscores, so generated XML files use underscores instead of spaces as -well. +\texttt{tex4ht} command cannot handle filenames containing spaces. to +fix this issue, \texttt{make4ht} replaces spaces in the input filenames +with underscores. The generated XML filenames use underscores instead of +spaces as well. \hypertarget{filenames-containing-non-ascii-characters}{% \subsection{Filenames containing non-ASCII diff --git a/support/tex4ebook/README b/support/tex4ebook/README index 951dad5a57..796445674a 100644 --- a/support/tex4ebook/README +++ b/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 ` 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/support/tex4ebook/exec_epub3.lua b/support/tex4ebook/exec_epub3.lua index 53173be9e9..edf6df01c2 100644 --- a/support/tex4ebook/exec_epub3.lua +++ b/support/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/support/tex4ebook/exec_mobi.lua b/support/tex4ebook/exec_mobi.lua index 11871ade76..ce10b6da76 100644 --- a/support/tex4ebook/exec_mobi.lua +++ b/support/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/support/tex4ebook/readme.tex b/support/tex4ebook/readme.tex index 2a6110e03f..13b064bf31 100644 --- a/support/tex4ebook/readme.tex +++ b/support/tex4ebook/readme.tex @@ -1,29 +1,23 @@ \hypertarget{introduction}{% \section{Introduction}\label{introduction}} -\texttt{TeX4ebook} is bundle of Lua scripts and \texttt{LaTeX} packages -for conversion of LaTeX files to ebook formats, for example -\texttt{epub}, \texttt{mobi} and \texttt{epub3}. \texttt{tex4ht} is used -as conversion engine. - -Note that while \texttt{mobi} is supported by Amazon Kindle, most -widespread ebook reader, it doesn't support \texttt{mathml} and this -means that math must re represented as images. The same is true for -\texttt{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 \texttt{epub3}, as it supports -\texttt{mathml}. The issue with \texttt{epub3} is, that majority of -\texttt{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 -\texttt{epub3} supports most features from web standards, such as -\texttt{CSS}. - -As with \texttt{tex4ht}, the emphasis is on conversion of document's -logical structure 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 \texttt{CSS} or -fonts in configuration files. +\texttt{TeX4ebook} is a tool for conversion from \LaTeX~to ebook +formats, such as EPUB, MOBI and EPUB 3. It is based on +\texttt{TeX4ht}\footnote{\url{https://tug.org/tex4ht/}}, which provides +instructions for the actual \LaTeX~to HTML conversion, and on +\texttt{make4ht}\footnote{\url{https://ctan.org/pkg/make4ht?lang=en}}. + +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 \texttt{CSS} or fonts in +a configuration file. + +\texttt{TeX4ebook} supports the same features as \texttt{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 \texttt{make4ht} documentation to see the supported +features. \hypertarget{license}{% \subsection{License}\label{license}} @@ -67,8 +61,11 @@ But it is optional. You shouldn't need to modify your \TeX~files \section{Command line options}\label{command-line-options}} \begin{description} +\item[\texttt{-a,-\/-loglevel}] +Set message log level. Possible values: debug, info, status, warning, +error, fatal. Default: status. \item[\texttt{-c,-\/-config}] -specify custom config file for \texttt{tex4ht} +specify custom config file for \texttt{TeX4ht} \end{description} \textbf{example config file}: File \texttt{sample.cfg} @@ -95,7 +92,10 @@ Specify make4ht build file\footnote{\url{https://github.com/michal-h21/make4ht\# Defaulf build file filename is \texttt{filename.mk4}, use this option if you use different filename. \item[\texttt{-f,-\/-format\ (default\ epub)}] -Output format. Epub, Epub3 and Mobi formats are supported. +Output format. Possible values are \texttt{epub}, \texttt{epub3} and +\texttt{mobi}. +\item[\texttt{-j,-\/-jobname}] +Specify the output file name, without file extension. \item[\texttt{-l,-\/-lua}] Use LuaLaTeX as TeX engine. \item[\texttt{-m,-\/-mode\ (default\ default)}] @@ -107,8 +107,8 @@ times. Resolution of generated images, for example math. It should meet resolution of target devices, which is usually about 167 ppi. \item[\texttt{-s,-\/-shell-escape}] -Enable shell escape in htlatex run. This may be needed if you run -external commands from your source files. +Enable shell escape in the \texttt{htlatex} run. This is necessary for +the execution of the external commands from your source files. \item[\texttt{-t,-\/-tidy}] process output html files with \texttt{HTML\ tidy} command\footnote{It needs to be installed separately}. @@ -121,8 +121,8 @@ Print the version number. \hypertarget{configuration}{% \section{Configuration}\label{configuration}} -\texttt{tex4ebook} uses \texttt{tex4ht}\footnote{\url{http://www.tug.org/tex4ht/}} -for conversion from LaTeX to html. \texttt{tex4ht} is highly +\texttt{TeX4ebook} uses \texttt{TeX4ht}\footnote{\url{http://www.tug.org/tex4ht/}} +for conversion from LaTeX to html. \texttt{TeX4ht} is highly configurable using config files. Basic config file structure is \begin{verbatim} @@ -134,19 +134,19 @@ configurable using config files. Basic config file structure is \end{verbatim} Basic info about command configurations can be found in a -work-in-progres \emph{tex4ht tutorial}\footnote{\url{https://github.com/michal-h21/helpers4ht/wiki/tex4ht-tutorial}}, -\emph{tex4ht documentation}\footnote{\url{http://www.tug.org/applications/tex4ht/mn11.html}}, +work-in-progres \emph{TeX4ht tutorial}\footnote{\url{https://github.com/michal-h21/helpers4ht/wiki/tex4ht-tutorial}}, +\emph{TeX4ht documentation}\footnote{\url{http://www.tug.org/applications/tex4ht/mn11.html}}, and in series of blogposts on CV Radhakrishnan's blog: \emph{Configure -part 1}\footnote{\url{http://www.cvr.cc/?p=323}}, \emph{Configure part -2}\footnote{\url{http://www.cvr.cc/?p=362}}, \emph{Low level -commands}\footnote{\url{http://www.cvr.cc/?p=482}}. Available options -for \texttt{\textbackslash{}Preamble} command are listed in the article -\emph{TeX4ht: options}\footnote{\url{http://www.cvr.cc/?p=504}}. +part 1}\footnote{\url{https://web.archive.org/web/20180908234227/http://www.cvr.cc/?p=323}}, +\emph{Configure part 2}\footnote{\url{https://web.archive.org/web/20180908201057/http://www.cvr.cc/?p=362}}, +\emph{Low level commands}\footnote{\url{https://web.archive.org/web/20180909101325/http://cvr.cc/?p=482}}. +Available options for \texttt{\textbackslash{}Preamble} command are +listed in the article \emph{TeX4ht: options}\footnote{\url{https://web.archive.org/web/20180813043722/http://cvr.cc/?p=504}}. \emph{Comparison of tex4ebook and Pandoc output}\footnote{\url{https://github.com/richelbilderbeek/travis_tex_to_epub_example_1}} -Great source of tips for \texttt{tex4ht} configuring is \emph{tex4ht tag -on TeX.sx}\footnote{\url{http://tex.stackexchange.com/questions/tagged/tex4ht}}, -there is also a \emph{tag for tex4ebook}\footnote{\url{http://tex.stackexchange.com/questions/tagged/tex4ebook}}. +A great source of tips for \texttt{TeX4ht} configuration is \emph{tex4ht +tag on TeX.sx}\footnote{\url{http://tex.stackexchange.com/questions/tagged/tex4ht}}. +There is also a \emph{tag for tex4ebook}\footnote{\url{http://tex.stackexchange.com/questions/tagged/tex4ebook}}. Examples of interesting questions are \emph{including images and fonts in ebooks}\footnote{\url{http://tex.stackexchange.com/a/213165/2891}} or @@ -161,7 +161,7 @@ in ebooks}\footnote{\url{http://tex.stackexchange.com/a/213165/2891}} or \Configure{UniqueIdentifier}{identifier} \end{verbatim} -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 \texttt{http://example.com/\textbackslash{}jobname}. @@ -179,8 +179,8 @@ to strip away any information which don't belongs here, such as \Configure{OpfScheme}{URI} \end{verbatim} -Type of unique identifier, default type is URI. It is used only in -\texttt{epub}, it is deprecated for \texttt{epub3} +Type of unique identifier, default type is URI. It is used only in the +EPUB format, it is deprecated for EPUB 3. \begin{verbatim} \Configure{resettoclevels}{list of section types in descending order} @@ -194,7 +194,7 @@ file. Default value is the whole document hierarchy, from \Configure{DocumentLanguage}{language code} \end{verbatim} -Each ePub file must declare the document language. It is inferred from +Each EPUB file must declare the document language. It is inferred from \texttt{babel} main language by default, but you can configure it when it doesn't work correctly. The \texttt{language\ code} should be in \href{https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes}{ISO 639-1} @@ -243,11 +243,12 @@ Add item to \texttt{\textless{}manifest\textgreater{}} section in the \Configure{xmlns}{prefix}{uri} \end{verbatim} -Add xml name space to \texttt{xhtml} files. Useful in \texttt{EPUB\ 3} +Add \texttt{XML} name space to the generated \texttt{XHTML} files. +Useful in \texttt{EPUB\ 3}. -\hypertarget{commands-available-in-config-files}{% -\subsection{Commands available in config -files}\label{commands-available-in-config-files}} +\hypertarget{commands-available-in-the-config-file}{% +\subsection{Commands available in the config +file}\label{commands-available-in-the-config-file}} \begin{description} \item[\texttt{\textbackslash{}OpfRegisterFile{[}filename{]}}] @@ -293,8 +294,44 @@ information. \hypertarget{troubleshooting}{% \section{Troubleshooting}\label{troubleshooting}} +\hypertarget{fixed-layout-epub}{% +\subsection{Fixed layout EPUB}\label{fixed-layout-epub}} + +The basic support for the Fixed layout EPUB 3 can be enabled using the +following configurations: + +\begin{verbatim} +\Configure{OpfMetadata}{\HCode{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/support/tex4ebook/tex4ebook b/support/tex4ebook/tex4ebook index a95bf44c23..731607fc5b 100755 --- a/support/tex4ebook/tex4ebook +++ b/support/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/support/tex4ebook/tex4ebook-doc.pdf b/support/tex4ebook/tex4ebook-doc.pdf index 6c7fd83af0..8372406fd9 100644 Binary files a/support/tex4ebook/tex4ebook-doc.pdf and b/support/tex4ebook/tex4ebook-doc.pdf differ diff --git a/support/tex4ebook/tex4ebook.4ht b/support/tex4ebook/tex4ebook.4ht index e4233fc55a..e623ac8bc2 100644 --- a/support/tex4ebook/tex4ebook.4ht +++ b/support/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/support/tex4ebook/tex4ebook.sty b/support/tex4ebook/tex4ebook.sty index c3726ee167..27a46b756e 100644 --- a/support/tex4ebook/tex4ebook.sty +++ b/support/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