diff options
author | Karl Berry <karl@freefriends.org> | 2015-07-03 23:24:59 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2015-07-03 23:24:59 +0000 |
commit | f1073f0dfaaeabd2edda00c472a3d814fad132fa (patch) | |
tree | f72e3d8d9e989b6fa399a13600be3d88168eebca | |
parent | 0562fc367d1605303c6b2674d0d6b73b37d1d888 (diff) |
make4ht (29jul15)
git-svn-id: svn://tug.org/texlive/trunk@37750 c570f23f-e606-0410-a88d-b1316a301751
43 files changed, 1878 insertions, 1 deletions
diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.am b/Build/source/texk/texlive/linked_scripts/Makefile.am index c2f28b67c1a..be0cbe8cbc1 100644 --- a/Build/source/texk/texlive/linked_scripts/Makefile.am +++ b/Build/source/texk/texlive/linked_scripts/Makefile.am @@ -133,6 +133,7 @@ texmf_other_scripts = \ ltximg/ltximg.pl \ luaotfload/luaotfload-tool.lua \ m-tx/m-tx.lua \ + make4ht/make4ht \ match_parens/match_parens \ mathspic/mathspic.pl \ mf2pt1/mf2pt1.pl \ diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.in b/Build/source/texk/texlive/linked_scripts/Makefile.in index ee7a773b90f..e5b3b051163 100644 --- a/Build/source/texk/texlive/linked_scripts/Makefile.in +++ b/Build/source/texk/texlive/linked_scripts/Makefile.in @@ -349,6 +349,7 @@ texmf_other_scripts = \ ltximg/ltximg.pl \ luaotfload/luaotfload-tool.lua \ m-tx/m-tx.lua \ + make4ht/make4ht \ match_parens/match_parens \ mathspic/mathspic.pl \ mf2pt1/mf2pt1.pl \ diff --git a/Build/source/texk/texlive/linked_scripts/make4ht/make4ht b/Build/source/texk/texlive/linked_scripts/make4ht/make4ht new file mode 100755 index 00000000000..22b2e7cf2fb --- /dev/null +++ b/Build/source/texk/texlive/linked_scripts/make4ht/make4ht @@ -0,0 +1,62 @@ +#!/usr/bin/env texlua +kpse.set_program_name("luatex") + + +local make4ht = require("make4ht-lib") +local lapp = require("lapp-mk4") +local mkutils = require("mkutils") +local mkparams = require("mkparams") +-- 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 +Usage: +make4ht [options] filename ["tex4ht.sty op." "tex4ht op." "t4ht op" "latex op"] +-c,--config (default xhtml) Custom config file +-d,--output-dir (default nil) Output directory +-l,--lua Use lualatex for document compilation +-s,--shell-escape Enables running external programs from LaTeX +-u,--utf8 For output documents in utf8 encoding +-x,--xetex Use xelatex for document compilation +<filename> (string) Input file name +]] + +local args = mkparams.get_args() + +local parameters = mkparams.process_args(args) + +local mode = parameters.mode +local build_file = parameters.build_file + +local make = mkutils.load_config(parameters, build_file)["Make"] +make.params = parameters +if make:length() < 1 then + if mode == "draft" then + make:htlatex() + else + make:htlatex() + make:htlatex() + make:htlatex() + end +end + + +if not args["no-tex4ht"] then + make:tex4ht() +end + +local ext = args.xetex and "xdv" or "dvi" +if #make.image_patterns > 0 then + make.params.t4ht_par = make.params.t4ht_par .. " -p" +end +make:t4ht {ext = ext} +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) + local outfilename = outdir .. filename + mkutils.copy(filename,outfilename) + return true +end) +make:run() diff --git a/Build/source/texk/texlive/linked_scripts/scripts.lst b/Build/source/texk/texlive/linked_scripts/scripts.lst index 1e65a7e61be..b8682c41f9a 100644 --- a/Build/source/texk/texlive/linked_scripts/scripts.lst +++ b/Build/source/texk/texlive/linked_scripts/scripts.lst @@ -79,6 +79,7 @@ lilyglyphs/lily-rebuild-pdfs.py ltximg/ltximg.pl luaotfload/luaotfload-tool.lua m-tx/m-tx.lua +make4ht/make4ht match_parens/match_parens mathspic/mathspic.pl mf2pt1/mf2pt1.pl diff --git a/Master/bin/alpha-linux/make4ht b/Master/bin/alpha-linux/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/alpha-linux/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/amd64-freebsd/make4ht b/Master/bin/amd64-freebsd/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/amd64-freebsd/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/amd64-netbsd/make4ht b/Master/bin/amd64-netbsd/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/amd64-netbsd/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/armel-linux/make4ht b/Master/bin/armel-linux/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/armel-linux/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/armhf-linux/make4ht b/Master/bin/armhf-linux/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/armhf-linux/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/i386-cygwin/make4ht b/Master/bin/i386-cygwin/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/i386-cygwin/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/i386-freebsd/make4ht b/Master/bin/i386-freebsd/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/i386-freebsd/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/i386-linux/make4ht b/Master/bin/i386-linux/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/i386-linux/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/i386-netbsd/make4ht b/Master/bin/i386-netbsd/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/i386-netbsd/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/i386-solaris/make4ht b/Master/bin/i386-solaris/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/i386-solaris/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/mipsel-linux/make4ht b/Master/bin/mipsel-linux/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/mipsel-linux/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/powerpc-linux/make4ht b/Master/bin/powerpc-linux/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/powerpc-linux/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/sparc-solaris/make4ht b/Master/bin/sparc-solaris/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/sparc-solaris/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/universal-darwin/make4ht b/Master/bin/universal-darwin/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/universal-darwin/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/win32/make4ht.exe b/Master/bin/win32/make4ht.exe Binary files differnew file mode 100755 index 00000000000..5777d90a17a --- /dev/null +++ b/Master/bin/win32/make4ht.exe diff --git a/Master/bin/x86_64-cygwin/make4ht b/Master/bin/x86_64-cygwin/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/x86_64-cygwin/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/x86_64-darwin/make4ht b/Master/bin/x86_64-darwin/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/x86_64-darwin/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/x86_64-linux/make4ht b/Master/bin/x86_64-linux/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/x86_64-linux/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/bin/x86_64-solaris/make4ht b/Master/bin/x86_64-solaris/make4ht new file mode 120000 index 00000000000..ff701ecdc0a --- /dev/null +++ b/Master/bin/x86_64-solaris/make4ht @@ -0,0 +1 @@ +../../texmf-dist/scripts/make4ht/make4ht
\ No newline at end of file diff --git a/Master/texmf-dist/doc/support/make4ht/README.md b/Master/texmf-dist/doc/support/make4ht/README.md new file mode 100644 index 00000000000..13840cd2e58 --- /dev/null +++ b/Master/texmf-dist/doc/support/make4ht/README.md @@ -0,0 +1,297 @@ +# Introduction + +`make4ht` is a simple build system for tex4ht. It is both executable which +simplifies `tex4ht` execution and a library which can be used to create +customized conversion programs. An example of such conversion program is [tex4ebook](https://github.com/michal-h21/tex4ebook) + +## License + +Permission is granted to copy, distribute and/or modify this software +under the terms of the LaTeX Project Public License, version 1.3. + +How it works +------------ + +Default compilation script for `tex4ht`, named `htlatex` 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 options filename + t4ht options filename + +The problem is that this is inefficient when you need to run program which +interact with LaTeX, such as `Makeindex` or `Bibtex`. In that case, you need to +create new script based on the default one, or run `htlatex` twice, which means +six LaTeX runs. + +Another problem is with `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 some subdirectory, they will be copied to the output directory, +but links will be pointing to non existing subdirectory. + +Image conversion is directed with the +[env file](http://www.tug.org/applications/tex4ht/mn35.html#index35-73001), +with really strange syntax based on whitespace and +[os dependent](http://www.tug.org/applications/tex4ht/mn-unix.html#index27-69005). +With `make4ht` build files, we have simple mean to fix these issues. We can +change image conversion parameters without need to modify the `env file`, +and call 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 build script are written. + + +# Build files + +## Commands + +By default, build file is saved in file named `filename + .mk4 extension`. +You can choose different build file with `-e` or `--build-file` command line +option. + +Sample: + + 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, `tidy` command is executed on the output `html` file. + +Note that you don't have to call `tex4ht` and `t4ht` commands explicitly in the build file, they are called automatically. + +You can add more commands like `Make:htlatex` with + + Make:add("name", "command", {parameters}, repetition) + + +it can be called with + + Make:name() + +`command` can be text template, or function: + + Make:add("text", "hello, input file: ${input}") + Make:add("function", function(params) for k, v in pairs(params) do print(k..": "..v) end) + +`parameters` is a table or `nil` value. + +Default parameters are: + +htlatex + +: used compiler + +input + +: input file + +latex_par + +: parameters to `latex` + +tex4ht_sty_par + +: parameters to `tex4ht.sty` + +tex4ht_par + +: parameters to `tex4ht` application + +t4ht_par + +: parameters to `t4ht` application + +outdir + +: output directory + +repetition + +: limit number of command execution. + +correct_exit + +: expected `exit code` from the command. + + +You may add your own parameters, they will be accessible in templates and +functions. + +With `repetition`, you can limit number of command executions. Its value +should be number or `nil`. This is used in the case of `tex4ht` and `t4ht` +commands, as they should be executed only once and they would be executed +multiple times if you include them in the build file, because they would be +called also by `make4ht`. With `repetition`, second execution is blocked. + +You can set expected exit code from a command with `correct_exit`. Compilation +is stopped when command returns different exit code. Situation is little bit +difficult with LaTeX (all engines and formats in fact), because it doesn't +differentiate between fatal and non fatal errors and returns 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. + +## File matches + +Other type of action which can be specified in the build file are +matches which may be called on the generated files: + + Make:match("html$", "tidy -m -xml -utf8 -q -i ${filename}") + +It tests filenames with `Lua` pattern matching and on matched items will +execute command or function, specified in the second argument. Parameters are +the same as in previous section, except `filename`, which contains generated +output name. + +### Filters + +Some default match actions which you can use are in the `filter` module. It +contains some functions which ares useful for fixing some `tex4ht` bugs or +shortcomings. + +Example: + + local filter = require "make4ht-filter" + local process = filter{"cleanspan", "fixligatures", "hruletohr"} + Make:htlatex() + Make:htlatex() + Make:match("html$",process) + + +Filter module is located in `make4ht-filter`. Function is returned, +which is used for building filter chains then. + +Built-in filters are: + +cleanspan + +: clean spurious span elements when accented characters are used + +cleanspan-nat + +: alternative clean span filter, provided by Nat Kuhn + +fixligatures + +: decompose ligatures to base characters + +hruletohr + +: `\hrule` commands are translated to series of underscore characters + by `tex4ht`, this filter translate these underscores to `<hr>` elements +entites + +: convert prohibited named entities to numeric entities (currently, only ` `, as it causes validation errors, and `tex4ht` is producing it sometimes + + +Function `filter` accepts also function arguments, in this case this function +takes file contents as 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 case, spurious span elements are joined, ligatures are decomposed, +and then all letters 'a' are replaced with 'z' letters. + +## Image conversion + +It is possible to convert parts of LaTeX input to pictures, it is used +for example for math or diagrams in `tex4ht`. + +These pictures are stored in special `dvi` file, on which `dvi to image` +commands are called. + +This conversion is normally configured in the `env file`, +which is system dependent and which has a little bit unintuitive syntax. +This configuration is processed by `t4ht` application and conversion +commands are called for all pictures. + +It is possible to disable `t4ht` image processing and configure image +conversion in the make file: + + Make:image("png$", + "dvipng -bg Transparent -T tight -o ${output} -pp ${page} ${source}") + + +`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 table with parameters as argument. + +There are three parameters: + + - output - output image file name + - source - `dvi` file with the pictures + - page - page number of the converted image + +## `mode` variable + +Variable `mode` contains contents of `-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 + +In this example (which is default configuration used by `make4ht`), +LaTeX is called only once when `make4ht` is called with + + make4ht -m draft filename + + +# Command line options + + 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 + -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 + <filename> (string) Input file name + + +You can still use `make4ht` in same way as `htlatex` + + make4ht filename "customcfg, charset=utf-8" " -cunihtf -utf8" " -dfoo" + +Note that this will not use `make4ht` routines for output dir making and +copying If you want to use them, change the line above to: + + make4ht filename "customcfg, charset=utf-8" " -cunihtf -utf8" -d foo + +This call has the same effect as 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 current directory, or absolute: + + make4ht -d use/current/dir/ filename + make4ht -d ../gotoparrentdir filename + make4ht -d ~/gotohomedir filename + make4ht -d c:\documents\windowspathsareworkingtoo filename + + diff --git a/Master/texmf-dist/doc/support/make4ht/changelog.tex b/Master/texmf-dist/doc/support/make4ht/changelog.tex new file mode 100644 index 00000000000..8687ad10ec9 --- /dev/null +++ b/Master/texmf-dist/doc/support/make4ht/changelog.tex @@ -0,0 +1,11 @@ +\section{Changelog}\label{changelog} + +\subsection{2015/06/26}\label{section} + +\begin{itemize} +\itemsep1pt\parskip0pt\parsep0pt +\item + added Makefile +\item + moved INSTALL instructions from README to INSTALL +\end{itemize} diff --git a/Master/texmf-dist/doc/support/make4ht/make4ht-doc.pdf b/Master/texmf-dist/doc/support/make4ht/make4ht-doc.pdf Binary files differnew file mode 100644 index 00000000000..cca309660e2 --- /dev/null +++ b/Master/texmf-dist/doc/support/make4ht/make4ht-doc.pdf diff --git a/Master/texmf-dist/doc/support/make4ht/make4ht-doc.tex b/Master/texmf-dist/doc/support/make4ht/make4ht-doc.tex new file mode 100644 index 00000000000..e89626c7a96 --- /dev/null +++ b/Master/texmf-dist/doc/support/make4ht/make4ht-doc.tex @@ -0,0 +1,24 @@ +\documentclass{ltxdoc} + + +\usepackage[english]{babel} +\usepackage{hyperref} +\ifdefined\HCode +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\else +\usepackage{fontspec} +\setmainfont{TeX Gyre Schola} +\fi +\usepackage{microtype} + +\title{The \texttt{make4ht} build system} +\author{Michal Hoftich\footnote{\url{michal.h21@gmail.com}}} +\date{Version 0.1\\29/06/2015} +\begin{document} +\maketitle +\tableofcontents +\input{readme} + +\input{changelog} +\end{document} diff --git a/Master/texmf-dist/doc/support/make4ht/readme.tex b/Master/texmf-dist/doc/support/make4ht/readme.tex new file mode 100644 index 00000000000..fc2f72727f3 --- /dev/null +++ b/Master/texmf-dist/doc/support/make4ht/readme.tex @@ -0,0 +1,322 @@ +\section{Introduction}\label{introduction} + +\texttt{make4ht} is a simple build system for tex4ht. It is both +executable which simplifies \texttt{tex4ht} execution and a library +which can be used to create customized conversion programs. An example +of such conversion program is +\href{https://github.com/michal-h21/tex4ebook}{tex4ebook} + +\subsection{License}\label{license} + +Permission is granted to copy, distribute and/or modify this software +under the terms of the LaTeX Project Public License, version 1.3. + +\subsection{How it works}\label{how-it-works} + +Default compilation script for \texttt{tex4ht}, named \texttt{htlatex} +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 options filename +t4ht options filename +\end{verbatim} + +The problem is that this is inefficient when you need to run program +which interact with LaTeX, such as \texttt{Makeindex} or +\texttt{Bibtex}. In that case, you need to create new script based on +the default one, or run \texttt{htlatex} twice, which means six LaTeX +runs. + +Another problem is with \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 some +subdirectory, they will be copied to the output directory, but links +will be pointing to non existing subdirectory. + +Image conversion is directed with the +\href{http://www.tug.org/applications/tex4ht/mn35.html\#index35-73001}{env +file}, with really strange syntax based on whitespace and +\href{http://www.tug.org/applications/tex4ht/mn-unix.html\#index27-69005}{os +dependent}. With \texttt{make4ht} build files, we have simple mean to +fix these issues. We can change image conversion parameters without need +to modify the \texttt{env file}, and call 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. + +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 build script are written. + +\section{Build files}\label{build-files} + +\subsection{Commands}\label{commands} + +By default, build file is saved in file named +\texttt{filename + .mk4 extension}. You can choose different build file +with \texttt{-e} or \texttt{-{}-build-file} command line option. + +Sample: + +\begin{verbatim} +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, \texttt{tidy} command is executed on the +output \texttt{html} file. + +Note that you don't have to call \texttt{tex4ht} and \texttt{t4ht} +commands explicitly in the build file, they are called automatically. + +You can add more commands like \texttt{Make:htlatex} with + +\begin{verbatim} +Make:add("name", "command", {parameters}, repetition) +\end{verbatim} + +it can be called with + +\begin{verbatim} +Make:name() +\end{verbatim} + +\texttt{command} can be text template, or function: + +\begin{verbatim} +Make:add("text", "hello, input file: ${input}") +Make:add("function", function(params) for k, v in pairs(params) do print(k..": "..v) end) +\end{verbatim} + +\texttt{parameters} is a table or \texttt{nil} value. + +Default parameters are: + +\begin{description} +\item[htlatex] +used compiler +\item[input] +input file +\item[latex\_par] +parameters to \texttt{latex} +\item[tex4ht\_sty\_par] +parameters to \texttt{tex4ht.sty} +\item[tex4ht\_par] +parameters to \texttt{tex4ht} application +\item[t4ht\_par] +parameters to \texttt{t4ht} application +\item[outdir] +output directory +\item[repetition] +limit number of command execution. +\item[correct\_exit] +expected \texttt{exit code} from the command. +\end{description} + +You may add your own parameters, they will be accessible in templates +and functions. + +With \texttt{repetition}, you can limit number of command executions. +Its value should be number or \texttt{nil}. This is used in the case of +\texttt{tex4ht} and \texttt{t4ht} commands, as they should be executed +only once and they would be executed multiple times if you include them +in the build file, because they would be called also by +\texttt{make4ht}. With \texttt{repetition}, second execution is blocked. + +You can set expected exit code from a command with +\texttt{correct\_exit}. Compilation is stopped when command returns +different exit code. Situation is little bit difficult with LaTeX (all +engines and formats in fact), because it doesn't differentiate between +fatal and non fatal errors and returns 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. + +\subsection{File matches}\label{file-matches} + +Other type of action which can be specified in the build file are +matches which may be called on the generated files: + +\begin{verbatim} +Make:match("html$", "tidy -m -xml -utf8 -q -i ${filename}") +\end{verbatim} + +It tests filenames with \texttt{Lua} pattern matching and on matched +items will execute command or function, specified in the second +argument. Parameters are the same as in previous section, except +\texttt{filename}, which contains generated output name. + +\subsubsection{Filters}\label{filters} + +Some default match actions which you can use are in the \texttt{filter} +module. It contains some functions which ares useful for fixing some +\texttt{tex4ht} bugs or shortcomings. + +Example: + +\begin{verbatim} +local filter = require "make4ht-filter" +local process = filter{"cleanspan", "fixligatures", "hruletohr"} +Make:htlatex() +Make:htlatex() +Make:match("html$",process) +\end{verbatim} + +Filter module is located in \texttt{make4ht-filter}. Function is +returned, which is used for building filter chains then. + +Built-in filters are: + +\begin{description} +\item[cleanspan] +clean spurious span elements when accented characters are used +\item[cleanspan-nat] +alternative clean span filter, provided by Nat Kuhn +\item[fixligatures] +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 +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 +\end{description} + +Function \texttt{filter} accepts also function arguments, in this case +this function takes file contents as 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 case, spurious span elements are joined, ligatures are +decomposed, and then all letters `a' are replaced with `z' letters. + +\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 special \texttt{dvi} file, on which +\texttt{dvi to image} commands are called. + +This conversion is normally configured in the \texttt{env file}, which +is system dependent and which has a little bit unintuitive syntax. This +configuration is processed by \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 make 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 table with parameters as argument. + +There are three parameters: + +\begin{itemize} +\itemsep1pt\parskip0pt\parsep0pt +\item + output - output image file name +\item + source - \texttt{dvi} file with the pictures +\item + page - page number of the converted image +\end{itemize} + +\subsection{\texttt{mode} variable}\label{mode-variable} + +Variable \texttt{mode} contains contents of \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 default configuration used by +\texttt{make4ht}), LaTeX is called only once when \texttt{make4ht} is +called with + +\begin{verbatim} +make4ht -m draft filename +\end{verbatim} + +\section{Command line options}\label{command-line-options} + +\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 +-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 +<filename> (string) Input file name +\end{verbatim} + +You can still use \texttt{make4ht} in same way as \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 output dir +making and copying If you want to use them, change the line above to: + +\begin{verbatim} +make4ht filename "customcfg, charset=utf-8" " -cunihtf -utf8" -d foo +\end{verbatim} + +This call has the same effect as 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 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} diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan-nat.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan-nat.lua new file mode 100644 index 00000000000..896132160c1 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan-nat.lua @@ -0,0 +1,12 @@ +-- cleanspan function submitted by Nat Kuhn +-- http://www.natkuhn.com/ + +local function filter(s) + local pattern = "(<span%s+([^>]+)>[^<]*)</span>(%s*)<span%s+%2>" + repeat + s, n = s:gsub(pattern, "%1%3") + until n == 0 + return s +end + +return filter diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan.lua new file mode 100644 index 00000000000..ede5c6c74e6 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan.lua @@ -0,0 +1,33 @@ +-- make4ht-cleanspan4ht.lua +-- fixes spurious <span> elements in tex4ht output + + +function filter(input) + local parse_args = function(s) + local at = {} + s:gsub("(%w+)%s*=%s*\"([^\"]-)\"", function(k,w) + at[k]=w + end) + return at + end + -- local pattern = "(<?/?[%w]*>?)<span[%s]*class=\"([^\"]+)\"[%s]*>" + local pattern = "(<?/?[%w]*>?)([%s]*)<span[%s]*([^>]-)>" + local last_class = "" + local depth = 0 + return input:gsub(pattern, function(tag,space, args) + local attr = parse_args(args) or {} + local class = attr["class"] or "" + if tag == "</span>" then + if class == last_class and class~= "" then + last_class = class + return space .. "" + end + elseif tag == "" then + class="" + end + last_class = class + return tag ..space .. '<span '..args ..'>' + end) +end + +return filter diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-entities.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-entities.lua new file mode 100644 index 00000000000..f9f6c883d1f --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-entities.lua @@ -0,0 +1,14 @@ +-- Fix bad entities +-- Sometimes, tex4ht produce named xml entities, which are prohobited in epub +-- , for example +function filter(s) + local replaces = { + nbsp = "#160" + } + return s:gsub("&(%w+);",function(x) + local m = replaces[x] or x + return "&"..m..";" + end) +end + +return filter diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-filter.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-filter.lua new file mode 100644 index 00000000000..4c7ce409a07 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-filter.lua @@ -0,0 +1,41 @@ +local filters = {} + +local function load_filter(filtername) + return require("filters.make4ht-"..filtername) +end + +function filter(filters) + local sequence = {} + if type(filters) == "string" then + table.insert(sequence,load_filter(filters)) + elseif type(filters) == "table" then + for _,n in pairs(filters) do + if type(n) == "string" then + table.insert(sequence,load_filter(n)) + elseif type(n) == "function" then + table.insert(sequence, n) + end + end + elseif type(filters) == "function" then + table.insert(sequence, filters) + else + return false, "Argument to filter must be either\ntable with filter names, or single filter name" + end + return function(filename) + if not filename then return false, "filters: no filename" end + local input = nil + + if filename then + local file = io.open(filename,"r") + input = file:read("*all") + file:close() + end + for _,f in pairs(sequence) do + input = f(input) + end + local file = io.open(filename,"w") + file:write(input) + file:close() + end +end +return filter diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-fixligatures.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-fixligatures.lua new file mode 100644 index 00000000000..01b31ed3067 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-fixligatures.lua @@ -0,0 +1,16 @@ +-- fix ligatures +-- replace ligatures produced by tex4ht with their components +-- this prevents problems with some readers +local gsub = unicode.utf8.gsub +function filter(s) + local replaces = { + ["fi"] = "fi", + ["ffi"] = "ffi", + ["fl"] = "fl", + ["ffl"] = "ffl", + ["ff"] = "ff" + } + return gsub(s, "([fiffiflfflff])",function (x) return replaces[x] or x end) +end + +return filter diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-hruletohr.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-hruletohr.lua new file mode 100644 index 00000000000..a9c0c5e9df2 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-hruletohr.lua @@ -0,0 +1,11 @@ +-- hruletohr +-- \hrule primitive is impossible to redefine catching all possible arguments +-- with tex4ht, it is converted as series of underscores +-- it seems that these underscores are always part of previous paragraph +-- this assumption may be wrong, needs more real world testing + +local hruletohr = function(s) + return s:gsub("___+(.-)</p>","%1</p>\n<hr class=\"hrule\" />") +end + +return hruletohr diff --git a/Master/texmf-dist/scripts/make4ht/lapp-mk4.lua b/Master/texmf-dist/scripts/make4ht/lapp-mk4.lua new file mode 100755 index 00000000000..71ed3704077 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/lapp-mk4.lua @@ -0,0 +1,317 @@ +-- lapp.lua +-- Simple command-line parsing using human-readable specification +----------------------------- +--~ -- args.lua +--~ local args = require ('lapp') [[ +--~ Testing parameter handling +--~ -p Plain flag (defaults to false) +--~ -q,--quiet Plain flag with GNU-style optional long name +--~ -o (string) Required string option +--~ -n (number) Required number option +--~ -s (default 1.0) Option that takes a number, but will default +--~ <start> (number) Required number argument +--~ <input> (default stdin) A parameter which is an input file +--~ <output> (default stdout) One that is an output file +--~ ]] +--~ for k,v in pairs(args) do +--~ print(k,v) +--~ end +------------------------------- +--~ > args -pq -o help -n 2 2.3 +--~ input file (781C1B78) +--~ p true +--~ s 1 +--~ output file (781C1B98) +--~ quiet true +--~ start 2.3 +--~ o help +--~ n 2 +-------------------------------- + +lapp = {} + +local append = table.insert +local usage +local open_files = {} +local parms = {} +local aliases = {} +local parmlist = {} + +local filetypes = { + stdin = {io.stdin,'file-in'}, stdout = {io.stdout,'file-out'}, + stderr = {io.stderr,'file-out'} +} + +local function quit(msg,no_usage) + if msg then + io.stderr:write(msg..'\n\n') + end + if not no_usage then + io.stderr:write(usage) + end + os.exit(1); +end + +local function error(msg,no_usage) + quit(arg[0]:gsub('.+[\\/]','')..':'..msg,no_usage) +end + +local function ltrim(line) + return line:gsub('^%s*','') +end + +local function rtrim(line) + return line:gsub('%s*$','') +end + +local function trim(s) + return ltrim(rtrim(s)) +end + +local function open (file,opt) + local val,err = io.open(file,opt) + if not val then error(err,true) end + append(open_files,val) + return val +end + +local function xassert(condn,msg) + if not condn then + error(msg) + end +end + +local function range_check(x,min,max,parm) + xassert(min <= x and max >= x,parm..' out of range') +end + +local function xtonumber(s) + local val = tonumber(s) + if not val then error("unable to convert to number: "..s) end + return val +end + +local function is_filetype(type) + return type == 'file-in' or type == 'file-out' +end + +local types = {} + +local function convert_parameter(ps,val) + if ps.converter then + val = ps.converter(val) + end + if ps.type == 'number' then + val = xtonumber(val) + elseif is_filetype(ps.type) then + val = open(val,(ps.type == 'file-in' and 'r') or 'w' ) + elseif ps.type == 'boolean' then + val = true + end + if ps.constraint then + ps.constraint(val) + end + return val +end + +function lapp.add_type (name,converter,constraint) + types[name] = {converter=converter,constraint=constraint} +end + +local function force_short(short) + xassert(#short==1,short..": short parameters should be one character") +end + +function process_options_string(str) + local res = {} + local varargs + + local function check_varargs(s) + local res,cnt = s:gsub('%.%.%.$','') + varargs = cnt > 0 + return res + end + + local function set_result(ps,parm,val) + if not ps.varargs then + res[parm] = val + else + if not res[parm] then + res[parm] = { val } + else + append(res[parm],val) + end + end + end + + usage = str + + for line in str:gmatch('([^\n]*)\n') do + local optspec,optparm,i1,i2,defval,vtype,constraint + line = ltrim(line) + -- flags: either -<short> or -<short>,<long> + i1,i2,optspec = line:find('^%-(%S+)') + if i1 then + optspec = check_varargs(optspec) + local short,long = optspec:match('([^,]+),(.+)') + if short then + optparm = long:sub(3) + aliases[short] = optparm + force_short(short) + else + optparm = optspec + force_short(optparm) + end + else -- is it <parameter_name>? + i1,i2,optparm = line:find('(%b<>)') + if i1 then + -- so <input file...> becomes input_file ... + optparm = check_varargs(optparm:sub(2,-2)):gsub('%A','_') + append(parmlist,optparm) + end + end + if i1 then -- this is not a pure doc line + local last_i2 = i2 + local sval + line = ltrim(line:sub(i2+1)) + -- do we have (default <val>) or (<type>)? + i1,i2,typespec = line:find('^%s*(%b())') + if i1 then + typespec = trim(typespec:sub(2,-2)) -- trim the parens and any space + sval = typespec:match('default%s+(.+)') + if sval then + local val = tonumber(sval) + if val then -- we have a number! + defval = val + vtype = 'number' + elseif filetypes[sval] then + local ft = filetypes[sval] + defval = ft[1] + vtype = ft[2] + else + defval = sval + vtype = 'string' + end + else + local min,max = typespec:match '([^%.]+)%.%.(.+)' + if min then -- it's (min..max) + vtype = 'number' + min = xtonumber(min) + max = xtonumber(max) + constraint = function(x) + range_check(x,min,max,optparm) + end + else -- () just contains type of required parameter + vtype = typespec + end + end + else -- must be a plain flag, no extra parameter required + defval = false + vtype = 'boolean' + end + local ps = { + type = vtype, + defval = defval, + required = defval == nil, + comment = line:sub((i2 or last_i2)+1) or optparm, + constraint = constraint, + varargs = varargs + } + if types[vtype] then + local converter = types[vtype].converter + if type(converter) == 'string' then + ps.type = converter + else + ps.converter = converter + end + ps.constraint = types[vtype].constraint + end + parms[optparm] = ps + end + end + -- cool, we have our parms, let's parse the command line args + local iparm = 1 + local iextra = 1 + local i = 1 + local parm,ps,val + while i <= #arg do + -- look for a flag, -<short flags> or --<long flag> + local i1,i2,dash,parmstr = arg[i]:find('^(%-+)(%a.*)') + if i1 then -- we have a flag + if #dash == 2 then -- long option + parm = parmstr + else -- short option + if #parmstr == 1 then + parm = parmstr + else -- multiple flags after a '-',? + parm = parmstr:sub(1,1) + if parmstr:find('^%a%d+') then + -- a short option followed by a digit? (exception for AW ;)) + -- push ahead into the arg array + table.insert(arg,i+1,parmstr:sub(2)) + else + -- push multiple flags into the arg array! + for k = 2,#parmstr do + table.insert(arg,i+k-1,'-'..parmstr:sub(k,k)) + end + end + end + end + if parm == 'h' or parm == 'help' then + quit() + end + if aliases[parm] then parm = aliases[parm] end + ps = parms[parm] + if not ps then error("unrecognized parameter: "..parm) end + if ps.type ~= 'boolean' then -- we need a value! This should follow + val = arg[i+1] + i = i + 1 + xassert(val,parm.." was expecting a value") + end + else -- a parameter + parm = parmlist[iparm] + if not parm then + -- extra unnamed parameters are indexed starting at 1 + parm = iextra + iextra = iextra + 1 + ps = { type = 'string' } + else + ps = parms[parm] + end + if not ps.varargs then + iparm = iparm + 1 + end + val = arg[i] + end + ps.used = true + val = convert_parameter(ps,val) + set_result(ps,parm,val) + if is_filetype(ps.type) then + set_result(ps,parm..'_name',arg[i]) + end + if lapp.callback then + lapp.callback(parm,arg[i],res) + end + i = i + 1 + end + -- check unused parms, set defaults and check if any required parameters were missed + for parm,ps in pairs(parms) do + if not ps.used then + if ps.required then error("missing required parameter: "..parm) end + set_result(ps,parm,ps.defval) + end + end + return res +end + +setmetatable(lapp, { + __call = function(tbl,str) return process_options_string(str) end, + __index = { + open = open, + quit = quit, + error = error, + assert = xassert, + } +}) + +return lapp diff --git a/Master/texmf-dist/scripts/make4ht/make4ht b/Master/texmf-dist/scripts/make4ht/make4ht new file mode 100755 index 00000000000..22b2e7cf2fb --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/make4ht @@ -0,0 +1,62 @@ +#!/usr/bin/env texlua +kpse.set_program_name("luatex") + + +local make4ht = require("make4ht-lib") +local lapp = require("lapp-mk4") +local mkutils = require("mkutils") +local mkparams = require("mkparams") +-- 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 +Usage: +make4ht [options] filename ["tex4ht.sty op." "tex4ht op." "t4ht op" "latex op"] +-c,--config (default xhtml) Custom config file +-d,--output-dir (default nil) Output directory +-l,--lua Use lualatex for document compilation +-s,--shell-escape Enables running external programs from LaTeX +-u,--utf8 For output documents in utf8 encoding +-x,--xetex Use xelatex for document compilation +<filename> (string) Input file name +]] + +local args = mkparams.get_args() + +local parameters = mkparams.process_args(args) + +local mode = parameters.mode +local build_file = parameters.build_file + +local make = mkutils.load_config(parameters, build_file)["Make"] +make.params = parameters +if make:length() < 1 then + if mode == "draft" then + make:htlatex() + else + make:htlatex() + make:htlatex() + make:htlatex() + end +end + + +if not args["no-tex4ht"] then + make:tex4ht() +end + +local ext = args.xetex and "xdv" or "dvi" +if #make.image_patterns > 0 then + make.params.t4ht_par = make.params.t4ht_par .. " -p" +end +make:t4ht {ext = ext} +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) + local outfilename = outdir .. filename + mkutils.copy(filename,outfilename) + return true +end) +make:run() diff --git a/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua b/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua new file mode 100755 index 00000000000..3b550b9995c --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua @@ -0,0 +1,204 @@ +-- Simple make system for tex4ht +--kpse.set_program_name("luatex") +-- module(...,package.seeall) +local m = {} + +Make = {} +--Make.params = {} +Make.build_seq = {} +-- Patterns for matching output filenames +Make.matches = {} +Make.image_patterns = {} +Make.run_count = {} + +Make.add = function(self,name,fn,par,rep) + local par = par or {} + self.params = self.params or {} + 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 + 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) + end + -- print( fn % params) + local command = { + name=name, + type=typ, + command = fn, + params = params, + repetition = rep + } + table.insert(self.build_seq,command) + end +end + +Make.length = function(self) + return #self.build_seq +end + +Make.match = function(self, pattern, command, params) + local params = params or {} + table.insert(self.matches,{pattern = pattern, command = command, params = params}) +end + +Make.run_command = function(self,filename,s) + local command = s.command + local params = s.params + params["filename"] = filename + print("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) + end + return false, "parse_lg: Command is not string or function" +end + +Make.image = function(self, pattern, command, params) + local tab = { + pattern = pattern, + command = command, + params = params + } + table.insert(self.image_patterns, tab) +end + +Make.image_convert = function(self, images) + local image_patterns = self.image_patterns or {} + for i, r in pairs(image_patterns) do + local p = self.params or {} + local v = r.params or {} + for k,v in pairs(v) do + p[k]= v + end + image_patterns[i].params = p + end + for _,i in pairs(images) do + local output = i.output + for _, x in pairs(image_patterns) do + local pattern = x.pattern + if output:match(pattern) then + local command = x.command + local p = x.params or {} + p.output = output + p.page= i.page + p.source = i.source + if type(command) == "function" then + command(p) + elseif type(command) == "string" then + local c = command % p + print("Make4ht convert: "..c) + os.execute(c) + end + break + end + end + end +end + +Make.file_matches = function(self, files) + local statuses = {} + -- First make params for all matchers + for k,v in pairs(self.matches) do + local v = self.matches[k].params or {} + local p = self.params or {} + for i,j in pairs(p) do + v[i] = j + end + self.matches[k].params = v + end + -- Loop over files, run command on matched + for _, file in pairs(files)do + statuses[file] = {} + for _, s in pairs(self.matches) do + local pattern= s.pattern + if file:match(pattern) then + local status, msg = self:run_command(file,s) + msg = msg or "No message given" + table.insert(statuses[file],status) + if status == false then + print(msg) + break + end + end + end + end + return statuses +end + +Make.run = function(self) + local return_codes = {} + local params = self.params or {} + for _,v in ipairs(self.build_seq) do + --print("sekvence: "..v.name) + for p,n in pairs(v.params) do params[p] = n end + --for c,_ in pairs(params) do print("build param: "..c) end + if type(v.command)=="function" then + table.insert(return_codes,{name=v.name,status = v.command(params)}) + elseif type(v.command) =="string" then + local command = v.command % params + -- Some commands should be executed only limited times, typicaly once + -- tex4ht or t4ht for example + local run_count = self.run_count[v.command] or 0 + run_count = run_count + 1 + 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") + else + print("Make4ht: " .. command) + local status = os.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)) + end + local correct_exit = params.correct_exit or nil + if correct_exit then + local last_return = return_codes[#return_codes] or {} + 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) + os.exit(1) + end + end + end + local lgfile = params.input and params.input .. ".lg" or nil + if lgfile then + local lg = mkutils.parse_lg(lgfile) + -- First convert images from lg files + self:image_convert(lg["images"]) + -- Then run file matchers on lg files and converted images + local files = lg["files"] + for _,v in pairs(lg["images"]) do + local v = v.output + -- print(v) + table.insert(files,v) + end + self:file_matches(files) + else + print("No lg file. tex4ht run failed?") + end + return return_codes +end + +m.Make = Make + +return m + +--[[Make:add("hello", "hello ${world}", {world = "world"}) +Make:add("ajaj", "ajaj") +Make:hello() +Make:hello{world="světe"} +Make:hello() +Make:run() +--]] diff --git a/Master/texmf-dist/scripts/make4ht/mkparams.lua b/Master/texmf-dist/scripts/make4ht/mkparams.lua new file mode 100755 index 00000000000..5b614192029 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/mkparams.lua @@ -0,0 +1,135 @@ +local lapp = require "lapp-mk4" +local mkutils = require "mkutils" +local m = {} -- use ugly module system for new lua versions support + +m.optiontext = [[ +${progname} - build system for tex4ht +Usage: +${progname} [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 nil) Output directory +-e,--build-file (default nil) If build file is different than `filename`.mk4 +-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 +]] +local function get_args(parameters, optiontext) + local parameters = parameters or {} + parameters.progname = parameters.progname or "make4ht" + parameters.postparams = parameters.postparams or "" + local optiontext = optiontext or m.optiontext + parameters.postfile = parameters.postfile or "" + optiontext = optiontext .. parameters.postparams .."<filename> (string) Input file name\n" .. parameters.postfile + --print("--------------\n" .. optiontext .."--------------\n") + return lapp(optiontext % parameters) +end + + +local function process_args(args) + local function get_inserter(args,tb) + return function(key, value) + --local v = args[key] and value or "" + local v = "" + if args[key] then v = value end + table.insert(tb,v) + end + end + + local outdir = "" + local packages = "" + + if args["output-dir"] ~= "nil" then + outdir = args["output-dir"] + outdir = outdir:gsub('\\','/') + outdir = outdir:gsub('/$','') + end + + if args.backend == "lua4ht" then + args.lua = true + args.xetex = nil + args.utf8 = true + args["no-tex4ht"] = true + packages = packages .."\\RequirePackage{lua4ht}" + end + + + local compiler = args.lua and "dvilualatex" or args.xetex and "xelatex --no-pdf" or "latex" + local input = mkutils.remove_extension(args.filename) + + local latex_params = {} + local insert_latex = get_inserter(args,latex_params) + insert_latex("shell-escape","-shell-escape") + table.insert(latex_params,"-jobname="..input) + table.insert(latex_params,args[4] or "") + --table.insert(latex_params,args["shell-escape"] and "-shell-escape") + + + local t4sty = args[1] or "" + -- test if first option is custom config file + local cfg_tmp = t4sty:match("([^,^ ]+)") + if cfg_tmp and cfg_tmp ~= args.config then + local fn = cfg_tmp..".cfg" + local f = io.open(fn,"r") + if f then + args.config = cfg_tmp + f:close() + end + end + --[[if args[1] and args[1] ~= "" then + t4sty = args[1] + else + --]] + -- Different behaviour from htlatex + local utf = args.utf8 and ",charset=utf-8" or "" + t4sty = args.config .. "," .. t4sty .. utf + --end + + local tex4ht = "" + if args[2] and args[2] ~="" then + tex4ht = args[2] + else + tex4ht = args.utf8 and " -cunihtf -utf8" or "" + local xdv = args.xetex and " -.xdv" or "" + tex4ht = tex4ht .. xdv + end + + local t4ht = args[3] or "" + + local mode = args.mode or "default" + + local build_file = input.. ".mk4" + + if args["build-file"] and args["build-file"] ~= "nil" then + build_file = args["build-file"] + end + + local parameters = { + htlatex = compiler + ,input=input + ,packages=packages + ,latex_par=table.concat(latex_params," ") + --,config=ebookutils.remove_extension(args.config) + ,tex4ht_sty_par=t4sty + ,tex4ht_par=tex4ht + ,t4ht_par=t4ht + ,mode = mode + ,build_file = build_file + --,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) + return parameters +end +m.get_args = get_args +m.process_args = process_args +return m diff --git a/Master/texmf-dist/scripts/make4ht/mkutils.lua b/Master/texmf-dist/scripts/make4ht/mkutils.lua new file mode 100755 index 00000000000..ca9e2ff208a --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/mkutils.lua @@ -0,0 +1,290 @@ +module(...,package.seeall) + +local make4ht = require("make4ht-lib") +--template engine +function interp(s, tab) + local tab = tab or {} + return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end)) +end +--print( interp("${name} is ${value}", {name = "foo", value = "bar"}) ) + +function addProperty(s,prop) + if prop ~=nil then + return s .." "..prop + else + return s + end +end +getmetatable("").__mod = interp +getmetatable("").__add = addProperty + +--print( "${name} is ${value}" % {name = "foo", value = "bar"} ) +-- Outputs "foo is bar" + +function string:split(sep) + local sep, fields = sep or ":", {} + local pattern = string.format("([^%s]+)", sep) + self:gsub(pattern, function(c) fields[#fields+1] = c end) + return fields +end + +function remove_extension(path) + local found, len, remainder = string.find(path, "^(.*)%.[^%.]*$") + if found then + return remainder + else + return path + end +end + +-- + +function file_exists(file) + local f = io.open(file, "rb") + if f then f:close() end + return f ~= nil +end + +-- searching for converted images +function parse_lg(filename) + print("Parse LG") + local outputimages,outputfiles,status={},{},nil + local fonts, used_fonts = {},{} + if not file_exists(filename) then + print("Cannot read log file: "..filename) + else + local usedfiles={} + for line in io.lines(filename) do + --- needs --- pokus.idv[1] ==> pokus0x.png --- + -- line:gsub("needs --- (.+?)[([0-9]+) ==> ([%a%d%p%.%-%_]*)",function(name,page,k) table.insert(outputimages,k)end) + line:gsub("needs %-%-%- (.+)%[([0-9]+)%] ==> ([%a%d%p%.%-%_]*)", + function(file,page,output) + local rec = { + source = file, + page=page, + output = output + } + table.insert(outputimages,rec) + end + ) + line:gsub("File: (.*)", function(k) + if not usedfiles[k] then + table.insert(outputfiles,k) + usedfiles[k] = true + end + end) + line:gsub("htfcss: ([^%s]+)(.*)",function(k,r) + local fields = {} + r:gsub("[%s]*([^%:]+):[%s]*([^;]+);",function(c,v) + fields[c] = v + end) + fonts[k] = fields + end) + + line:gsub('Font("([^"]+)","([%d]+)","([%d]+)","([%d]+)"',function(n,s1,s2,s3) + table.insert(used_fonts,{n,s1,s2,s3}) + end) + + end + status=true + end + return {files = outputfiles, images = outputimages},status +end + + +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) + os.execute(command) +end + +local used_dir = {} + +function prepare_path(path) + --local dirs = path:split("/") + local dirs = {} + if path:match("^/") then dirs = {""} + elseif path:match("^~") then + local home = os.getenv "HOME" + dirs = home:split "/" + path = path:gsub("^~/","") + table.insert(dirs,1,"") + end + if path:match("/$")then path = path .. " " end + for _,d in pairs(path:split "/") do + table.insert(dirs,d) + end + table.remove(dirs,#dirs) + return dirs,table.concat(dirs,"/") +end + +-- Find which part of path already exists +-- and which directories have to be created +function find_directories(dirs, pos) + local pos = pos or #dirs + -- we tried whole path and no dir exist + if pos < 1 then return dirs end + local path = "" + -- in the case of unix absolute path, empty string is inserted in dirs + if pos == 1 and dirs[pos] == "" then + path = "/" + else + path = table.concat(dirs,"/", 1,pos) .. "/" + end + if not lfs.chdir(path) then -- recursion until we succesfully changed dir + -- or there are no elements in the dir table + return find_directories(dirs,pos - 1) +elseif pos ~= #dirs then -- if we succesfully changed dir + -- and we have dirs to create + local p = {} + for i = pos+1, #dirs do + table.insert(p, dirs[i]) + end + return p +else -- whole path exists + return {} +end +end + +function mkdirectories(dirs) + if type(dirs) ~="table" then + return false, "mkdirectories: dirs is not table" + end + for _,d in pairs(dirs) do + local stat,msg = lfs.mkdir(d) + if not stat then return false, "makedirectories error: "..msg end + lfs.chdir(d) + end + return true +end + +function copy_filter(src,dest, filter) + local src_f=io.open(src,"rb") + local dst_f=io.open(dest,"w") + local contents = src_f:read("*all") + local filter = filter or function(s) return s end + src_f:close() + dst_f:write(filter(contents)) + dst_f:close() +end + + + +function copy(filename,outfilename) + local currdir = lfs.currentdir() + if filename == outfilename then return true end + local parts, path = prepare_path(outfilename) + if not used_dir[path] then + local to_create, msg = find_directories(parts) + if not to_create then + print(msg) + return false + end + used_dir[path] = true + local stat, msg = mkdirectories(to_create) + if not stat then print(msg) end + end + lfs.chdir(currdir) + cp(filename, path) + return true +end + +-- Config loading +local function run(untrusted_code, env) + if untrusted_code:byte(1) == 27 then return nil, "binary bytecode prohibited" end + local untrusted_function = nil + if not loadstring then + untrusted_function, message = load(untrusted_code, nil, "t",env) + else + untrusted_function, message = loadstring(untrusted_code) + end + if not untrusted_function then return nil, message end + if not setfenv then setfenv = function(a,b) return true end end + setfenv(untrusted_function, env) + return pcall(untrusted_function) +end + +local main_settings = {} +main_settings.fonts = {} +local env = {} + +-- We make sandbox for make script, all functions must be explicitely declared +-- Function declarations: +env.pairs = pairs +env.ipairs = ipairs +env.print = print +env.split = split +env.string = string +env.table = table +env.copy = copy +env.mkdirectories = mkdirectories +env.require = require +env.texio = texio +env.type = type +env.lfs = lfs +env.os = os +env.io = io +env.unicode = unicode +env.Font = function(s) + local font_name = s["name"] + if not font_name then return nil, "Cannot find font name" end + env.settings.fonts[font_name] = s +end + +env.Make = make4ht.Make +env.Make.params = env.settings +env.Make:add("test","no takže ${tex4ht_sty_par} ${htlatex} ${input} ${config}") +--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}") +env.Make:add("htlatex",function(par) + local 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 ${input}'" + if os.type == "windows" then + command = command:gsub("'",'') + end + command = command % par + print("LaTeX call: "..command) + os.execute(command) + 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") + + f:seek("set", len - 256) + local text = f:read("*a") + f:close() + if text:match("No pages of output") then return 1 end + return 0 +end +,{correct_exit=0}) +env.Make:add("tex4ht","tex4ht ${tex4ht_par} ${input}", nil, 1) +env.Make:add("t4ht","t4ht ${t4ht_par} ${input}.${ext}",{ext="dvi"},1) + +function load_config(settings, config_name) + local settings = settings or main_settings + env.settings = settings + env.mode = settings.mode + local config_name = config_name or "config.lua" + local f = io.open(config_name,"r") + if not f then return env, "Cannot open config file" end + local code = f:read("*all") + local fn, msg = run(code,env) + if not fn then print(msg) end + assert(fn) + return env +end + + diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check index e9872152619..01db7d7c76c 100755 --- a/Master/tlpkg/bin/tlpkg-ctan-check +++ b/Master/tlpkg/bin/tlpkg-ctan-check @@ -353,7 +353,8 @@ my @TLP_working = qw( luasseq luatexbase luatexja luatexko luatextra luatodonotes luaxml lxfonts ly1 m-tx macros2e macroswap mafr magaz mailing mailmerge - makebarcode makebox makecell makecirc makecmds makedtx makeglos makeplot + make4ht makebarcode makebox makecell makecirc makecmds + makedtx makeglos makeplot makeshape mandi manfnt manfnt-font manuscript margbib marginfix marginnote marvosym matc3 matc3mem match_parens diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds index eea4c8c345e..79129c18ef4 100755 --- a/Master/tlpkg/libexec/ctan2tds +++ b/Master/tlpkg/libexec/ctan2tds @@ -2579,6 +2579,7 @@ $standardttf = '\.ttf|\.TTC'; 'luaindex' => '\.lua$', 'lualatex-math' => '\.lua$', 'luasseq' => '\.lua$', + 'make4ht' => 'filters|\.lua$', 'mycv' => 'mycv_split_contents\.pl', 'pgfmolbio' => '\.lua$', 'placeat' => '\.lua$', @@ -2631,6 +2632,7 @@ $standardttf = '\.ttf|\.TTC'; 'ltximg' => 'ltximg\.pl$', 'lua2dox' => 'lua2dox_filter?$', 'luaotfload' => 'luaotfload-tool', + 'make4ht' => 'make4ht$', 'match_parens' => '^match_parens$', 'mathspic' => '^mathspic\.pl$', 'multibibliography' => '\.pl$', diff --git a/Master/tlpkg/tlpsrc/collection-binextra.tlpsrc b/Master/tlpkg/tlpsrc/collection-binextra.tlpsrc index f9cf3050f79..e17312fc9ab 100644 --- a/Master/tlpkg/tlpsrc/collection-binextra.tlpsrc +++ b/Master/tlpkg/tlpsrc/collection-binextra.tlpsrc @@ -47,6 +47,7 @@ depend latexindent depend ltxfileinfo depend ltximg depend listings-ext +depend make4ht depend match_parens depend mkjobtexmf depend patgen diff --git a/Master/tlpkg/tlpsrc/make4ht.tlpsrc b/Master/tlpkg/tlpsrc/make4ht.tlpsrc new file mode 100644 index 00000000000..dca338c719c --- /dev/null +++ b/Master/tlpkg/tlpsrc/make4ht.tlpsrc @@ -0,0 +1 @@ +binpattern f bin/${ARCH}/${PKGNAME} |