summaryrefslogtreecommitdiff
path: root/support
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2020-01-24 03:00:54 +0000
committerNorbert Preining <norbert@preining.info>2020-01-24 03:00:54 +0000
commite1192611f0655a1ccaff0dff2f53c7c65fa5db07 (patch)
tree232f5be17a2221a57cc6515bfe8692772b3a3e89 /support
parentf7e5d81fe9880913485f7f8c541eeca7adf4c195 (diff)
CTAN sync 202001240300
Diffstat (limited to 'support')
-rw-r--r--support/make4ht/README18
-rw-r--r--support/make4ht/changelog.tex64
-rw-r--r--support/make4ht/domfilters/make4ht-booktabs.lua82
-rw-r--r--support/make4ht/domfilters/make4ht-joincharacters.lua113
-rw-r--r--support/make4ht/domfilters/make4ht-tablerows.lua14
-rw-r--r--support/make4ht/extensions/make4ht-ext-common_domfilters.lua2
-rw-r--r--support/make4ht/extensions/make4ht-ext-detect_engine.lua91
-rwxr-xr-xsupport/make4ht/make4ht2
-rw-r--r--support/make4ht/make4ht-doc.pdfbin128196 -> 130605 bytes
-rw-r--r--support/make4ht/make4ht-htlatex.lua21
-rw-r--r--support/make4ht/make4ht-indexing.lua3
-rw-r--r--support/make4ht/make4ht-odtfilter.lua33
-rw-r--r--support/make4ht/mkparams.lua9
-rw-r--r--support/make4ht/mkutils.lua5
-rw-r--r--support/make4ht/readme.tex15
-rw-r--r--support/pdfbook2/README12
-rwxr-xr-xsupport/pdfbook2/pdfbook2380
-rw-r--r--support/pdfbook2/pdfbook2.16
18 files changed, 648 insertions, 222 deletions
diff --git a/support/make4ht/README b/support/make4ht/README
index 3b2e6b0085..78ec73056c 100644
--- a/support/make4ht/README
+++ b/support/make4ht/README
@@ -238,6 +238,16 @@ common\_domfilters
`common_filters`. Used DOM filters are `fixinlines`, `idcolons`,
`joincharacters`, and `tablerows`.
+detect\_engine
+
+: detect engine and format necessary for the document compilation from the
+ magic comments supported by \LaTeX\ editors such as TeXShop or TeXWorks.
+ Add something like the following line at the beginning of the main \TeX\ file:
+
+ `%!TEX TS-program = xelatex`
+
+ It supports also Plain \TeX, use for example `tex` or `luatex` as the program name.
+
dvisvgm\_hashes
: efficient generation of SVG pictures using Dvisvgm. It can utilize
@@ -387,6 +397,10 @@ detect compilation errors in the TeX log file.
: One call to the TeX engine with special configuration for loading of the `tex4ht.sty` package.
+`Make:httex`
+
+: Variant of `Make:htlatex` suitable for Plain \TeX.
+
`Make:latexmk`
: Use `Latexmk` for the document compilation. `tex4ht.sty` will be loaded automatically.
@@ -571,6 +585,10 @@ 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 synchronization.
+booktabs
+
+: fix lines produced by the `\cmidrule` command provided by the Booktabs package.
+
collapsetoc
: collapse table of contents to contain only top-level sectioning level and sections on the current page.
diff --git a/support/make4ht/changelog.tex b/support/make4ht/changelog.tex
index 8fdd699e83..d67418081f 100644
--- a/support/make4ht/changelog.tex
+++ b/support/make4ht/changelog.tex
@@ -3,6 +3,70 @@
\begin{itemize}
\item
+ 2020/01/22
+
+ \begin{itemize}
+ \tightlist
+ \item
+ version \texttt{0.3d} released.
+ \item
+ added \texttt{Make:httex} command for Plain TeX support.
+ \item
+ added \texttt{detect\_engine} extension. It supports detection of
+ the used engine and format from TeX Shop or TeXWorks magic comments.
+ These comments can look like:
+ \texttt{\%!TEX\ TS-program\ =\ xelatex}.
+ \end{itemize}
+\item
+ 2020/01/22
+
+ \begin{itemize}
+ \tightlist
+ \item
+ fixed support for multiple indices in \texttt{make4ht-indexing.lua}.
+ \end{itemize}
+\item
+ 2019/12/29
+
+ \begin{itemize}
+ \tightlist
+ \item
+ use the \texttt{mathvariant="italic"} attribute for joined
+ \texttt{\textless{}mi\textgreater{}} elements.
+ \item
+ fixed comparison of element attributes in \texttt{joincharacters}
+ DOM filter.
+ \end{itemize}
+\item
+ 2019/12/28
+
+ \begin{itemize}
+ \tightlist
+ \item
+ print warning if the input file doesn't exist.
+ \end{itemize}
+\item
+ 2019/12/17
+
+ \begin{itemize}
+ \tightlist
+ \item
+ added \texttt{booktabs} DOM filter.
+ \item
+ load the \texttt{booktabs} in \texttt{common\_domfilters} by
+ default.
+ \end{itemize}
+\item
+ 2019/12/14
+
+ \begin{itemize}
+ \tightlist
+ \item
+ fixed bug in the \texttt{tablerows} DOM filter -- it could remove
+ table rows if they contained only one column with elements that
+ contained no text content.
+ \end{itemize}
+\item
2019/11/28
\begin{itemize}
diff --git a/support/make4ht/domfilters/make4ht-booktabs.lua b/support/make4ht/domfilters/make4ht-booktabs.lua
new file mode 100644
index 0000000000..edec8adbd8
--- /dev/null
+++ b/support/make4ht/domfilters/make4ht-booktabs.lua
@@ -0,0 +1,82 @@
+
+local function find_cmidrules(current_rows)
+ -- save rows with cmidrules here
+ local matched_rows = {}
+ local continue = false
+ for row_no, row in ipairs(current_rows) do
+ local columnposition = 1
+ local matched_cmidrule = false
+
+ for _, col in ipairs(row:query_selector("td")) do
+ -- keep track of culumns
+ local span = tonumber(col:get_attribute("colspan")) or 1
+ local cmidrule = col:query_selector(".cmidrule")
+ -- column contain cmidrule
+ if #cmidrule > 0 then
+ -- remove any child elements, we don't need them anymore
+ col._children = {}
+ -- only one cmidrule can be on each row, save the position, column span and all attributes
+ matched_rows[row_no] = {attributes = col._attr, column = columnposition, span = span, continue = continue}
+ matched_cmidrule = true
+ end
+ columnposition = columnposition + span
+ end
+ if matched_cmidrule then
+ -- save the row number of the first cmidrule on the current row
+ continue = continue or row_no
+ else
+ continue = false
+ end
+
+ end
+ -- save the table rows count, so we can loop over them sequentially later
+ matched_rows.length = #current_rows
+ return matched_rows
+end
+
+local function update_row(current_rows, match, newspan, i)
+ local row_to_update = current_rows[match.continue]
+ -- insert spanning column if necessary
+ if newspan > 0 then
+ local td = row_to_update:create_element("td", {colspan=tostring(newspan), span="nazdar"})
+ row_to_update:add_child_node(td)
+ end
+ -- insert the rule column
+ local td = row_to_update:create_element("td", match.attributes)
+ row_to_update:add_child_node(td)
+ -- remove unnecessary row
+ current_rows[i]:remove_node()
+end
+
+local function join_rows(matched_rows,current_rows)
+ for i = 1, matched_rows.length do
+ local match = matched_rows[i]
+ if match then
+ -- we only need to process rows that place subsequent cmidrules on the same row
+ local continue = match.continue
+ if continue then
+ local prev_row = matched_rows[continue]
+ -- find column where the previous cmidrule ends
+ local prev_end = prev_row.column + prev_row.span
+ local newspan = match.column - prev_end
+ update_row(current_rows, match, newspan, i)
+ -- update the current row position
+ prev_row.column = match.column
+ prev_row.span = match.span
+ end
+ end
+ end
+end
+
+local function process_booktabs(dom)
+ local tables = dom:query_selector("table")
+ for _, tbl in ipairs(tables) do
+ local current_rows = tbl:query_selector("tr")
+ local matched_rows = find_cmidrules(current_rows)
+ join_rows(matched_rows, current_rows)
+ end
+ return dom
+end
+
+return process_booktabs
+
diff --git a/support/make4ht/domfilters/make4ht-joincharacters.lua b/support/make4ht/domfilters/make4ht-joincharacters.lua
index 5830eae78e..4493aef023 100644
--- a/support/make4ht/domfilters/make4ht-joincharacters.lua
+++ b/support/make4ht/domfilters/make4ht-joincharacters.lua
@@ -3,14 +3,35 @@ local log = logging.new("joincharacters")
local charclasses = {
span=true,
mn = true,
- mi = true
}
+local function update_mathvariant(curr)
+ -- when we join several <mi> elements, they will be rendered incorrectly
+ -- we must set the mathvariant attribute
+ local parent = curr:get_parent()
+ -- set mathvariant only if it haven't been set by the parent element
+ if not parent:get_attribute("mathvariant") then
+ -- curr._attr = curr._attr or {}
+ local mathvariant = "italic"
+ -- the joined elements don't have attributes
+ curr._attr = curr._attr or {}
+ curr:set_attribute("mathvariant", mathvariant)
+ end
+end
+
+local table_count = function(tbl)
+ local tbl = tbl or {}
+ local i = 0
+ for k,v in pairs(tbl) do i = i + 1 end
+ return i
+end
+
+
local has_matching_attributes = function (el, next_el)
local el_attr = el._attr or {}
local next_attr = next_el._attr or {}
-- if the number of attributes doesn't match, elements don't match
- if #next_attr ~= #el_attr then return false end
+ if table_count(next_attr) ~= table_count(el_attr) then return false end
for k, v in pairs(el_attr) do
-- if any attribute doesn't match, elements don't match
if v~=next_attr[k] then return false end
@@ -18,6 +39,7 @@ local has_matching_attributes = function (el, next_el)
return true
end
+
local function join_characters(obj,par)
-- join adjanced span and similar elements inserted by
-- tex4ht to just one object.
@@ -25,45 +47,48 @@ local function join_characters(obj,par)
local options = get_filter_settings "joincharacters"
local charclasses = options.charclasses or par.charclasses or charclasses
-
- obj:traverse_elements(function(el)
- local get_name = function(curr)
- return string.lower(curr:get_element_name())
- end
- local get_class = function(next_el)
- return next_el:get_attribute("class") or next_el:get_attribute("mathvariant")
- end
- local is_span = function(next_el)
- return charclasses[get_name(next_el)]
+ local get_name = function(curr)
+ return string.lower(curr:get_element_name())
+ end
+ local get_class = function(next_el)
+ return next_el:get_attribute("class") or next_el:get_attribute("mathvariant")
+ end
+ local is_span = function(next_el)
+ return charclasses[get_name(next_el)]
+ end
+ local join_elements = function(el, next_el)
+ -- it the following element match, copy it's children to the current element
+ for _, child in ipairs(next_el:get_children()) do
+ el:add_child_node(child)
end
+ -- remove the next element
+ next_el:remove_node()
+ end
- local function get_next(curr, class)
- local next_el = curr:get_next_node()
- if next_el and next_el:is_element() and is_span(next_el) then
- return next_el
- -- if the next node is space followed by a matching element, we should add this space
- elseif next_el and next_el:is_text() and get_next(next_el, class) then
- local text = next_el._text
- -- match only text containing just whitespace
- if text:match("^%s+$") then return next_el end
- end
+ local function get_next(curr, class)
+ local next_el = curr:get_next_node()
+ if next_el and next_el:is_element() and is_span(next_el) then
+ return next_el
+ -- if the next node is space followed by a matching element, we should add this space
+ elseif next_el and next_el:is_text() and get_next(next_el, class) then
+ local text = next_el._text
+ -- match only text containing just whitespace
+ if text:match("^%s+$") then return next_el end
end
+ end
+
+ obj:traverse_elements(function(el)
-- loop over all elements and test if the current element is in a list of
-- 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
-- as the current one
- while next_el do
+ while next_el do
-- save the next element because we will remove it later
local real_next = get_next(next_el)
if get_name(el) == get_name(next_el) and has_matching_attributes(el,next_el) and not el:get_attribute("id") then
- -- it the following element match, copy it's children to the current element
- for _, child in ipairs(next_el:get_children()) do
- el:add_child_node(child)
- end
- -- remove the next element
- next_el:remove_node()
+ join_elements(el, next_el)
-- add the whitespace
elseif next_el:is_text() then
local s = next_el._text
@@ -80,6 +105,36 @@ local function join_characters(obj,par)
end
end)
+ -- process <mi> elements
+ obj:traverse_elements(function(el)
+ local function get_next_mi(curr)
+ local next_el = curr:get_next_node()
+ if next_el and next_el:is_element() then
+ return next_el
+ end
+ end
+ local function has_no_attributes(x)
+ return table_count(x._attr) == 0
+ end
+
+ -- join only subsequential <mi> elements with no attributes
+ if get_name(el) == "mi" and has_no_attributes(el) then
+ local next_el = get_next_mi(el)
+ while next_el do
+ local real_next = get_next_mi(next_el)
+ if get_name(next_el) == "mi" and has_no_attributes(next_el) then
+ join_elements(el, next_el)
+ -- set math variant to italic
+ -- (if the parent <mstyle> element doesn't set it to something else)
+ update_mathvariant(el)
+ else
+ -- break the loop otherwise
+ real_next = nil
+ end
+ next_el = real_next
+ end
+ end
+ end)
-- join text nodes in an element into one
obj:traverse_elements(function(el)
-- save the text
diff --git a/support/make4ht/domfilters/make4ht-tablerows.lua b/support/make4ht/domfilters/make4ht-tablerows.lua
index 155c24529a..3e3931b368 100644
--- a/support/make4ht/domfilters/make4ht-tablerows.lua
+++ b/support/make4ht/domfilters/make4ht-tablerows.lua
@@ -1,5 +1,16 @@
local log = logging.new ("tablerows")
return function(dom)
+ local has_child_elements = function(child)
+ -- detect if the element contains child elements
+ local child_elements = 0
+ local children = child:get_children()
+ for _, el in ipairs(children) do
+ local step = el:is_element() and 1 or 0
+ -- log:info("element name", el._name)
+ child_elements = child_elements + step
+ end
+ return child_elements > 0
+ end
local is_empty_row = function(row)
local not_empty = false
local element_count = 0
@@ -11,7 +22,8 @@ return function(dom)
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
+ if element_count > 1 or has_child_elements(child) then return false end
+
-- detect if it contains only whitespace
not_empty = child:get_text():gsub("%s","") ~= "" or not_empty
end
diff --git a/support/make4ht/extensions/make4ht-ext-common_domfilters.lua b/support/make4ht/extensions/make4ht-ext-common_domfilters.lua
index ec687cd042..63232b22ce 100644
--- a/support/make4ht/extensions/make4ht-ext-common_domfilters.lua
+++ b/support/make4ht/extensions/make4ht-ext-common_domfilters.lua
@@ -26,7 +26,7 @@ function M.modify_build(make)
make:match("4om$", process, {charclasses= charclasses})
count = 2
else
- local process = filter {"fixinlines", "idcolons", "joincharacters", "mathmlfixes", "tablerows"}
+ local process = filter {"fixinlines", "idcolons", "joincharacters", "mathmlfixes", "tablerows","booktabs"}
make:match("html$", process)
count = 1
end
diff --git a/support/make4ht/extensions/make4ht-ext-detect_engine.lua b/support/make4ht/extensions/make4ht-ext-detect_engine.lua
new file mode 100644
index 0000000000..8bb91d3a69
--- /dev/null
+++ b/support/make4ht/extensions/make4ht-ext-detect_engine.lua
@@ -0,0 +1,91 @@
+-- support magic comments used by TeXShop and TeXWorks to detect used engine and format
+--
+local M = {}
+local log = logging.new("detect engine")
+local htlatex = require "make4ht-htlatex"
+
+-- we must change build sequence when Plain TeX is requested
+local change_table = {
+ tex = {
+ htlatex = "etex",
+ command = htlatex.httex
+ },
+ pdftex = {
+ htlatex = "etex",
+ command = htlatex.httex
+ },
+ etex = {
+ htlatex = "etex",
+ command = htlatex.httex
+ },
+ luatex = {
+ htlatex = "dviluatex",
+ command = htlatex.httex
+ },
+ xetex = {
+ htlatex = "xetex -no-pdf",
+ command = htlatex.httex
+ },
+ xelatex = {
+ htlatex = "xelatex -no-pdf",
+ },
+ lualatex = {
+ htlatex = "dvilualatex",
+ },
+ pdflatex = {
+ htlatex = "latex"
+ }
+
+}
+
+local function find_magic_program(filename)
+ -- find the magic line containing program name
+ local get_comment = function(line)
+ return line:match("%s*%%%s*(.+)")
+ end
+ local empty_line = function(line) return line:match("^%s*$") end
+ for line in io.lines(filename) do
+ local comment = get_comment(line)
+ -- read line after line from the file, break the processing after first non comment or non empty line
+ if not comment and not empty_line(line) then return nil, "Cannot find program name" end
+ comment = comment or "" -- comment is nil for empty lines
+ local program = comment:match("!%s*[Tt][Ee][Xx].-program%s*=%s*([^%s]+)")
+ if program then return program:lower() end
+ end
+end
+
+-- update htlatex entries with detected program
+local function update_build_sequence(program, build_seq)
+ -- handle Plain TeX
+ local replaces = change_table[program] or {}
+ local is_xetex = program:match("xe") -- we must handle xetex in tex4ht
+ for pos, entry in ipairs(build_seq) do
+ if entry.name == "htlatex" then
+ -- handle httex
+ entry.command = replaces.command or entry.command
+ local params = entry.params or {}
+ params.htlatex = replaces.htlatex or params.htlatex
+ entry.params = params
+ elseif is_xetex and entry.name == "tex4ht" then
+ -- tex4ht must process .xdv file if the TeX file was compiled by XeTeX
+ entry.params.tex4ht_par = entry.params.tex4ht_par .. " -.xdv"
+ end
+ end
+end
+
+
+function M.modify_build(make)
+ -- find magic comments in the TeX file
+ local build_seq = make.build_seq
+ local tex_file = make.params.tex_file
+ local program, msg = find_magic_program(tex_file)
+ if program then
+ log:info("Found program name", program)
+ update_build_sequence(program, build_seq)
+ else
+ log:warning("Cannot find magic line with the program name")
+ end
+ return make
+end
+
+return M
diff --git a/support/make4ht/make4ht b/support/make4ht/make4ht
index 10311088d7..d90bd5b970 100755
--- a/support/make4ht/make4ht
+++ b/support/make4ht/make4ht
@@ -29,7 +29,7 @@ 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.3c"
+local version = "v0.3d"
mkparams.version_number = version
local args = mkparams.get_args()
diff --git a/support/make4ht/make4ht-doc.pdf b/support/make4ht/make4ht-doc.pdf
index 949fa599c6..287a06722a 100644
--- a/support/make4ht/make4ht-doc.pdf
+++ b/support/make4ht/make4ht-doc.pdf
Binary files differ
diff --git a/support/make4ht/make4ht-htlatex.lua b/support/make4ht/make4ht-htlatex.lua
index 214972e8bb..f141070406 100644
--- a/support/make4ht/make4ht-htlatex.lua
+++ b/support/make4ht/make4ht-htlatex.lua
@@ -51,11 +51,17 @@ Make.latex_command = "${htlatex} --interaction=${interaction} ${latex_par} '\\ma
"\\documentstyle[tex4ht]}}}\\makeatother\\HCode ${tex4ht_sty_par}.a.b.c."..
"\\input \"\\detokenize{${tex_file}}\"'"
+Make.plain_command = '${htlatex} --interaction=${interaction} ${latex_par}' ..
+"'\\def\\Link#1.a.b.c.{\\expandafter\\def\\csname tex4ht\\endcsname{\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\input tex4ht.sty }}" ..
+"\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else\\expandafter\\Link\\fi}" ..
+"\\HCode ${tex4ht_sty_par}.a.b.c.\\input \"\\detokenize{${tex_file}}\"'"
+
local m = {}
-function m.htlatex(par)
- local command = Make.latex_command
+function m.htlatex(par, latex_command)
+ -- latex_command can be also plain_command for Plain TeX
+ local command = latex_command or Make.latex_command
local devnull = " > /dev/null 2>&1"
if os.type == "windows" then
command = command:gsub("'",'')
@@ -72,4 +78,15 @@ function m.htlatex(par)
return Make.testlogfile(par)
end
+function m.httex(par)
+ local newpar = {}
+ for k,v in pairs(par) do newpar[k] = v end
+ -- change executable name from *latex to *tex
+ newpar.htlatex = newpar.htlatex:gsub("latex", "tex")
+ -- plain tex command doesn't support etex extensions
+ -- which are necessary for TeX4ht. just quick hack to fix this
+ if newpar.htlatex == "tex" then newpar.htlatex = "etex" end
+ return m.htlatex(newpar, Make.plain_command)
+end
+
return m
diff --git a/support/make4ht/make4ht-indexing.lua b/support/make4ht/make4ht-indexing.lua
index 5801df509b..072536556a 100644
--- a/support/make4ht/make4ht-indexing.lua
+++ b/support/make4ht/make4ht-indexing.lua
@@ -177,6 +177,9 @@ local run_indexing_command = function(command, par)
if not status then xindylog:warning(msg) end
-- remove the temporary idx file
os.remove(newidxfile)
+ -- null the indfile, it is necessary in order to support
+ -- multiple indices
+ par.indfile = nil
end
M.get_utf8 = get_utf8
diff --git a/support/make4ht/make4ht-odtfilter.lua b/support/make4ht/make4ht-odtfilter.lua
deleted file mode 100644
index 97f0b8df67..0000000000
--- a/support/make4ht/make4ht-odtfilter.lua
+++ /dev/null
@@ -1,33 +0,0 @@
-local mkutils = require "mkutils"
-local zip = require "zip"
-
-
--- use function to change contents of the ODT file
-local function update_odt(odtfilename, file_path, fn)
- -- get name of the odt file
- local odtname = mkutils.remove_extension(odtfilename) .. ".odt"
- -- open and read contents of the requested file inside ODT file
- local odtfile = zip.open(odtname)
- local local_file = odtfile:open(file_path)
- local content = local_file:read("*all")
- local_file:close()
- odtfile:close()
- -- update the content using user function
- content = fn(content)
- -- write the updated file
- local local_file_file = io.open(file_path,"w")
- local_file_file:write(content)
- local_file_file:close()
- os.execute("zip " .. odtname .. " " .. file_path)
- os.remove(file_path)
-end
-
-Make:match("tmp$", function(name, par)
- update_odt(name, "content.xml", function(content)
- return content:gsub("%&%#x([A-Fa-f0-9]+);", function(entity)
- -- convert hexadecimal entity to Unicode
- print(entity,utfchar(tonumber(entity, 16)))
- return utfchar(tonumber(entity, 16))
- end)
- end)
-end)
diff --git a/support/make4ht/mkparams.lua b/support/make4ht/mkparams.lua
index eff8c99670..0254de9ac2 100644
--- a/support/make4ht/mkparams.lua
+++ b/support/make4ht/mkparams.lua
@@ -134,6 +134,11 @@ local function handle_jobname(input, args)
return latex_params, input
end
+local function tex_file_not_exits(tex_file)
+ -- try to find the input file, return false if we cannot find it
+ return not (kpse.find_file(tex_file, "tex") or kpse.find_file(tex_file .. ".tex", "tex"))
+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)
@@ -191,6 +196,10 @@ local function process_args(args)
local compiler = args.lua and "dvilualatex" or args.xetex and "xelatex --no-pdf" or "latex"
local tex_file, is_tmp_file = handle_input_file(args.filename)
+ -- test if the file exists
+ if not is_tmp_file and tex_file_not_exits(tex_file) then
+ log:warning("Cannot find input file: " .. tex_file)
+ end
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)
diff --git a/support/make4ht/mkutils.lua b/support/make4ht/mkutils.lua
index 944ab99cc7..a4f16fb71a 100644
--- a/support/make4ht/mkutils.lua
+++ b/support/make4ht/mkutils.lua
@@ -338,7 +338,10 @@ env.Make:add("test","test the variables: ${tex4ht_sty_par} ${htlatex} ${input}
local htlatex = require "make4ht-htlatex"
env.Make:add("htlatex", htlatex.htlatex
,{correct_exit=0})
-
+env.Make:add("htttex", htlatex.httex, {
+ htlatex = "etex",
+ correct_exit=0
+})
env.Make:add("latexmk", function(par)
local settings = get_filter_settings "htlatex" or {}
diff --git a/support/make4ht/readme.tex b/support/make4ht/readme.tex
index a326ce1010..b221473e02 100644
--- a/support/make4ht/readme.tex
+++ b/support/make4ht/readme.tex
@@ -303,6 +303,16 @@ clean the output HTML files using filters.
clean the HTML file using DOM filters. It is more powerful than
\texttt{common\_filters}. Used DOM filters are \texttt{fixinlines},
\texttt{idcolons}, \texttt{joincharacters}, and \texttt{tablerows}.
+\item[detect\_engine]
+detect engine and format necessary for the document compilation from the
+magic comments supported by \LaTeX~editors such as TeXShop or TeXWorks.
+Add something like the following line at the beginning of the main
+\TeX~file:
+
+\texttt{\%!TEX\ TS-program\ =\ xelatex}
+
+It supports also Plain \TeX, use for example \texttt{tex} or
+\texttt{luatex} as the program name.
\item[dvisvgm\_hashes]
efficient generation of SVG pictures using Dvisvgm. It can utilize
multiple processor cores and generates only changed images.
@@ -468,6 +478,8 @@ the TeX log file.
\item[\texttt{Make:htlatex}]
One call to the TeX engine with special configuration for loading of the
\texttt{tex4ht.sty} package.
+\item[\texttt{Make:httex}]
+Variant of \texttt{Make:htlatex} suitable for Plain \TeX.
\item[\texttt{Make:latexmk}]
Use \texttt{Latexmk} for the document compilation. \texttt{tex4ht.sty}
will be loaded automatically.
@@ -639,6 +651,9 @@ Available DOM filters:
\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 synchronization.
+\item[booktabs]
+fix lines produced by the \texttt{\textbackslash{}cmidrule} command
+provided by the Booktabs package.
\item[collapsetoc]
collapse table of contents to contain only top-level sectioning level
and sections on the current page.
diff --git a/support/pdfbook2/README b/support/pdfbook2/README
index 2b72d40f06..e27738188b 100644
--- a/support/pdfbook2/README
+++ b/support/pdfbook2/README
@@ -1,8 +1,8 @@
pdfbook2 - transform pdf files to booklets
==========================================
- pdfbook2 v1.3 (https://github.com/jenom/pdfbook2)
- (c) 2015 - 2019 Johannes Neumann (http://www.neumannjo.de)
+ pdfbook2 v1.4 (https://github.com/jenom/pdfbook2)
+ (c) 2015 - 2020 Johannes Neumann (http://www.neumannjo.de)
licensed under GPLv3 (http://www.gnu.org/licenses/gpl-3.0)
based on pdfbook by David Firth with help from Marco Pessotto
@@ -45,7 +45,7 @@ INSTALLATION
REQUIREMENTS
- python 2.7, pdfjam, pdfcrop and their dependencies.
+ python 3.6, pdfjam, pdfcrop and their dependencies.
EXAMPLES
@@ -115,6 +115,12 @@ OPTIONS
CHANGELOG
+ 1.4 2020/01/20
+
+ - migration to Python 3
+ - fixed bug if the input document had only one page
+ - fix for signature option not working
+
1.3 2019/08/12
- removed wait after popen to prevent deadlock with very large documents
diff --git a/support/pdfbook2/pdfbook2 b/support/pdfbook2/pdfbook2
index c5bc8677db..f9bb012cc7 100755
--- a/support/pdfbook2/pdfbook2
+++ b/support/pdfbook2/pdfbook2
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
""" pdfbook2 - transform pdf files to booklets
This program is free software: you can redistribute it and/or modify
@@ -16,219 +16,303 @@
"""
-import sys
-import subprocess
import os
-from optparse import OptionParser, OptionGroup, HelpFormatter
import shutil
+import subprocess
+import sys
+from optparse import HelpFormatter, OptionGroup, OptionParser
-
-#===============================================================================
+# ===============================================================================
# Create booklet for file $name
-#===============================================================================
+# ===============================================================================
-def booklify( name, opts ):
- #------------------------------------------------------ Check if file exists
- print "\nProcessing", name
- if not os.path.isfile( name ):
- print "SKIP: file not found."
+
+def booklify(name, opts):
+ # ------------------------------------------------------ Check if file exists
+ print("\nProcessing", name)
+ if not os.path.isfile(name):
+ print("SKIP: file not found.")
return
- print "Getting bounds...",
+ print("Getting bounds...", end=" ")
sys.stdout.flush()
- #---------------------------------------------------------- useful constants
- bboxName = "%%HiResBoundingBox:"
+ # ---------------------------------------------------------- useful constants
+ bboxName = b"%%HiResBoundingBox:"
tmpFile = ".crop-tmp.pdf"
- #------------------------------------------------- find min/max bounding box
+ # ------------------------------------------------- find min/max bounding box
if opts.crop:
- p = subprocess.Popen( ["pdfcrop", "--verbose",
- "--resolution", repr( opts.resolution ),
- name, tmpFile],
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE )
+ p = subprocess.Popen(
+ ["pdfcrop", "--verbose", "--resolution", repr(opts.resolution), name, tmpFile],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
out, err = p.communicate()
- if len( err ) != 0:
- print err
- print "\n\nABORT: Problem getting bounds"
- sys.exit( 1 )
+ if len(err) != 0:
+ print(err)
+ print("\n\nABORT: Problem getting bounds")
+ sys.exit(1)
lines = out.splitlines()
- bboxes = [s[len( bboxName ) + 1:] for s in lines if s.startswith( bboxName )]
- bounds = [[float( x ) for x in bbox.split()] for bbox in bboxes ]
- minLOdd = min( [bound[0] for bound in bounds[::2] ] )
- maxROdd = max( [bound[2] for bound in bounds[::2] ] )
- minLEven = min( [bound[0] for bound in bounds[1::2] ] )
- maxREven = max( [bound[2] for bound in bounds[1::2] ] )
- minT = min( [bound[1] for bound in bounds ] )
- maxB = max( [bound[3] for bound in bounds ] )
+ bboxes = [s[len(bboxName) + 1 :] for s in lines if s.startswith(bboxName)]
+ bounds = [[float(x) for x in bbox.split()] for bbox in bboxes]
+ minLOdd = min([bound[0] for bound in bounds[::2]])
+ maxROdd = max([bound[2] for bound in bounds[::2]])
+ if len(bboxes) > 1:
+ minLEven = min([bound[0] for bound in bounds[1::2]])
+ maxREven = max([bound[2] for bound in bounds[1::2]])
+ else:
+ minLEven = minLOdd
+ maxREven = maxROdd
+ minT = min([bound[1] for bound in bounds])
+ maxB = max([bound[3] for bound in bounds])
widthOdd = maxROdd - minLOdd
widthEven = maxREven - minLEven
- maxWidth = max( widthOdd, widthEven )
+ maxWidth = max(widthOdd, widthEven)
minLOdd -= maxWidth - widthOdd
maxREven += maxWidth - widthEven
- print "done"
+ print("done")
sys.stdout.flush()
- #--------------------------------------------- crop file to area of interest
- print "cropping...",
+ # --------------------------------------------- crop file to area of interest
+ print("cropping...", end=" ")
sys.stdout.flush()
- p = subprocess.Popen( ["pdfcrop",
- "--bbox-odd", "{L} {T} {R} {B}".format( L = minLOdd - opts.innerMargin / 2,
- T = minT - opts.topMargin,
- R = maxROdd + opts.outerMargin,
- B = maxB + opts.outerMargin ),
- "--bbox-even", "{L} {T} {R} {B}".format( L = minLEven - opts.outerMargin,
- T = minT - opts.topMargin,
- R = maxREven + opts.innerMargin / 2,
- B = maxB + opts.outerMargin ),
- "--resolution", repr( opts.resolution ),
- name,
- tmpFile],
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE )
+ p = subprocess.Popen(
+ [
+ "pdfcrop",
+ "--bbox-odd",
+ "{L} {T} {R} {B}".format(
+ L=minLOdd - opts.innerMargin / 2,
+ T=minT - opts.topMargin,
+ R=maxROdd + opts.outerMargin,
+ B=maxB + opts.outerMargin,
+ ),
+ "--bbox-even",
+ "{L} {T} {R} {B}".format(
+ L=minLEven - opts.outerMargin,
+ T=minT - opts.topMargin,
+ R=maxREven + opts.innerMargin / 2,
+ B=maxB + opts.outerMargin,
+ ),
+ "--resolution",
+ repr(opts.resolution),
+ name,
+ tmpFile,
+ ],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
out, err = p.communicate()
- if len( err ) != 0:
- print err
- print "\n\nABORT: Problem with cropping"
- sys.exit( 1 )
- print "done"
+ if len(err) != 0:
+ print(err)
+ print("\n\nABORT: Problem with cropping")
+ sys.exit(1)
+ print("done")
sys.stdout.flush()
else:
- shutil.copy( name, tmpFile )
+ shutil.copy(name, tmpFile)
- #-------------------------------------------------------- create the booklet
- print "create booklet...",
+ # -------------------------------------------------------- create the booklet
+ print("create booklet...", end=" ")
sys.stdout.flush()
- pdfJamCallList = [ "pdfjam",
- "--booklet", "true",
- "--landscape",
- "--suffix", "book",
- "--signature", repr( opts.signature ),
- tmpFile ]
+ pdfJamCallList = [
+ "pdfjam",
+ "--landscape",
+ "--suffix",
+ "book",
+ tmpFile,
+ ]
+
+ # add option signature if it is defined else booklet
+ if opts.signature != 0:
+ pdfJamCallList.append("--signature")
+ pdfJamCallList.append(repr(opts.signature))
+ else:
+ pdfJamCallList.append("--booklet")
+ pdfJamCallList.append("true")
# add option --paper to call
if opts.paper is not None:
- pdfJamCallList.append( "--paper" )
- pdfJamCallList.append( opts.paper )
+ pdfJamCallList.append("--paper")
+ pdfJamCallList.append(opts.paper)
# add option --short-edge to call
if opts.shortedge:
# check if everyshi.sty exists as texlive recommends
- p = subprocess.Popen( ["kpsewhich", "everyshi.sty"],
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE )
+ p = subprocess.Popen(
+ ["kpsewhich", "everyshi.sty"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
+ )
out, err = p.communicate()
- if len( out ) == 0:
- print "\n\nABORT: The everyshi.sty latex package is needed for short-edge."
- sys.exit( 1 )
+ if len(out) == 0:
+ print("\n\nABORT: The everyshi.sty latex package is needed for short-edge.")
+ sys.exit(1)
else:
- pdfJamCallList.append( "--preamble" )
- pdfJamCallList.append( r"\usepackage{everyshi}\makeatletter\EveryShipout{\ifodd\c@page\pdfpageattr{/Rotate 180}\fi}\makeatother" )
+ pdfJamCallList.append("--preamble")
+ pdfJamCallList.append(
+ r"\usepackage{everyshi}\makeatletter\EveryShipout{\ifodd\c@page\pdfpageattr{/Rotate 180}\fi}\makeatother"
+ )
# run call to pdfJam to make booklet
- p = subprocess.Popen( pdfJamCallList,
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE )
+ p = subprocess.Popen(pdfJamCallList, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
- #-------------------------------------------- move file and remove temp file
- os.rename( tmpFile[:-4] + "-book.pdf", name[:-4] + "-book.pdf" )
- os.remove( tmpFile )
- print "done"
+ # -------------------------------------------- move file and remove temp file
+ os.rename(tmpFile[:-4] + "-book.pdf", name[:-4] + "-book.pdf")
+ os.remove(tmpFile)
+ print("done")
sys.stdout.flush()
-#===============================================================================
+# ===============================================================================
# Help formatter
-#===============================================================================
+# ===============================================================================
+
-class MyHelpFormatter ( HelpFormatter ):
+class MyHelpFormatter(HelpFormatter):
"""Format help with indented section bodies.
"""
- def __init__( self,
- indent_increment = 4,
- max_help_position = 16,
- width = None,
- short_first = 0 ):
- HelpFormatter.__init__(
- self, indent_increment, max_help_position, width, short_first )
+ def __init__(self, indent_increment=4, max_help_position=16, width=None, short_first=0):
+ HelpFormatter.__init__(self, indent_increment, max_help_position, width, short_first)
- def format_usage( self, usage ):
- return ( "USAGE\n\n%*s%s\n" ) % ( self.indent_increment, "", usage )
+ def format_usage(self, usage):
+ return ("USAGE\n\n%*s%s\n") % (self.indent_increment, "", usage)
- def format_heading( self, heading ):
- return "%*s%s\n\n" % ( self.current_indent, "", heading.upper() )
+ def format_heading(self, heading):
+ return "%*s%s\n\n" % (self.current_indent, "", heading.upper())
-#===============================================================================
+# ===============================================================================
# main programm
-#===============================================================================
+# ===============================================================================
if __name__ == "__main__":
- #------------------------------------------------------------ useful strings
+ # ------------------------------------------------------------ useful strings
usageString = "Usage: %prog [options] file1 [file2 ...]"
versionString = """
- %prog v1.3 (https://github.com/jenom/pdfbook2)
- (c) 2015 Johannes Neumann (http://www.neumannjo.de)
+ %prog v1.4 (https://github.com/jenom/pdfbook2)
+ (c) 2015 - 2020 Johannes Neumann (http://www.neumannjo.de)
licensed under GPLv3 (http://www.gnu.org/licenses/gpl-3.0)
based on pdfbook by David Firth with help from Marco Pessotto\n"""
defaultString = " (default: %default)"
- #------------------------------------------------- create commandline parser
- parser = OptionParser( usage = usageString, version = versionString,
- formatter = MyHelpFormatter( indent_increment = 4 ) )
-
- generalGroup = OptionGroup( parser, "General" )
- generalGroup.add_option( "-p", "--paper", dest = "paper", type = "str", action = "store",
- metavar = "STR",
- help = "Format of the output paper dimensions as latex keyword (e.g. a4paper, letterpaper, legalpaper, ...)" )
- generalGroup.add_option( "-s", "--short-edge", dest = "shortedge", action = "store_true",
- help = "Format the booklet for short-edge double-sided printing",
- default = False )
- generalGroup.add_option( "-n", "--no-crop", dest = "crop", action = "store_false",
- help = "Prevent the cropping to the content area",
- default = True )
- parser.add_option_group( generalGroup )
-
- marginGroup = OptionGroup( parser, "Margins" )
- marginGroup.add_option( "-o", "--outer-margin", type = "int", default = 40,
- dest = "outerMargin", action = "store", metavar = "INT",
- help = "Defines the outer margin in the booklet" + defaultString )
- marginGroup.add_option( "-i", "--inner-margin", type = "int", default = 150,
- dest = "innerMargin", action = "store", metavar = "INT",
- help = "Defines the inner margin between the pages in the booklet" + defaultString )
- marginGroup.add_option( "-t", "--top-margin", type = "int", default = 30,
- dest = "topMargin", action = "store", metavar = "INT",
- help = "Defines the top margin in the booklet" + defaultString )
- marginGroup.add_option( "-b", "--bottom-margin", type = "int", default = 30, metavar = "INT",
- dest = "bottomMargin", action = "store",
- help = "Defines the bottom margin in the booklet" + defaultString )
- parser.add_option_group( marginGroup )
-
- advancedGroup = OptionGroup( parser, "Advanced" )
- advancedGroup.add_option( "--signature", dest = "signature", action = "store", type = "int",
- help = "Define the signature for the booklet handed to pdfjam, needs to be multiple of 4" + defaultString,
- default = 4, metavar = "INT" )
- advancedGroup.add_option( "--signature*", dest = "signature", action = "store", type = "int",
- help = "Same as --signature", metavar = "INT" )
- advancedGroup.add_option( "--resolution", dest = "resolution", action = "store", type = "int",
- help = "Resolution used by ghostscript in bp" + defaultString,
- metavar = "INT", default = 72 )
- parser.add_option_group( advancedGroup )
+ # ------------------------------------------------- create commandline parser
+ parser = OptionParser(
+ usage=usageString, version=versionString, formatter=MyHelpFormatter(indent_increment=4)
+ )
+
+ generalGroup = OptionGroup(parser, "General")
+ generalGroup.add_option(
+ "-p",
+ "--paper",
+ dest="paper",
+ type="str",
+ action="store",
+ metavar="STR",
+ help="Format of the output paper dimensions as latex keyword (e.g. a4paper, letterpaper, legalpaper, ...)",
+ )
+ generalGroup.add_option(
+ "-s",
+ "--short-edge",
+ dest="shortedge",
+ action="store_true",
+ help="Format the booklet for short-edge double-sided printing",
+ default=False,
+ )
+ generalGroup.add_option(
+ "-n",
+ "--no-crop",
+ dest="crop",
+ action="store_false",
+ help="Prevent the cropping to the content area",
+ default=True,
+ )
+ parser.add_option_group(generalGroup)
+
+ marginGroup = OptionGroup(parser, "Margins")
+ marginGroup.add_option(
+ "-o",
+ "--outer-margin",
+ type="int",
+ default=40,
+ dest="outerMargin",
+ action="store",
+ metavar="INT",
+ help="Defines the outer margin in the booklet" + defaultString,
+ )
+ marginGroup.add_option(
+ "-i",
+ "--inner-margin",
+ type="int",
+ default=150,
+ dest="innerMargin",
+ action="store",
+ metavar="INT",
+ help="Defines the inner margin between the pages in the booklet" + defaultString,
+ )
+ marginGroup.add_option(
+ "-t",
+ "--top-margin",
+ type="int",
+ default=30,
+ dest="topMargin",
+ action="store",
+ metavar="INT",
+ help="Defines the top margin in the booklet" + defaultString,
+ )
+ marginGroup.add_option(
+ "-b",
+ "--bottom-margin",
+ type="int",
+ default=30,
+ metavar="INT",
+ dest="bottomMargin",
+ action="store",
+ help="Defines the bottom margin in the booklet" + defaultString,
+ )
+ parser.add_option_group(marginGroup)
+
+ advancedGroup = OptionGroup(parser, "Advanced")
+ advancedGroup.add_option(
+ "--signature",
+ dest="signature",
+ action="store",
+ type="int",
+ help="Define the signature for the booklet handed to pdfjam, needs to be multiple of 4"
+ + defaultString,
+ default=0,
+ metavar="INT",
+ )
+ advancedGroup.add_option(
+ "--signature*",
+ dest="signature",
+ action="store",
+ type="int",
+ help="Same as --signature",
+ metavar="INT",
+ )
+ advancedGroup.add_option(
+ "--resolution",
+ dest="resolution",
+ action="store",
+ type="int",
+ help="Resolution used by ghostscript in bp" + defaultString,
+ metavar="INT",
+ default=72,
+ )
+ parser.add_option_group(advancedGroup)
opts, args = parser.parse_args()
- #------------------------------------ show help if started without arguments
- if len( args ) == 0:
+ # ------------------------------------ show help if started without arguments
+ if len(args) == 0:
parser.print_version()
parser.print_help()
- print ""
- sys.exit( 2 )
+ print("")
+ sys.exit(2)
- #------------------------------------------- run for each provided file name
+ # ------------------------------------------- run for each provided file name
parser.print_version()
for arg in args:
- booklify( arg, opts )
+ booklify(arg, opts)
diff --git a/support/pdfbook2/pdfbook2.1 b/support/pdfbook2/pdfbook2.1
index a6c61c0f90..c761447b53 100644
--- a/support/pdfbook2/pdfbook2.1
+++ b/support/pdfbook2/pdfbook2.1
@@ -1,4 +1,4 @@
-.TH pdfbook2 1 "August 12, 2019" "" "pdfbook2 - transform pdf files to booklets"
+.TH pdfbook2 1 "January 22, 2020" "" "pdfbook2 - transform pdf files to booklets"
.SH NAME
pdfbook2 \- transform pdf files into booklets for double-sided printing
@@ -107,9 +107,9 @@ Resolution used by ghostscript in bp (default: 72)
.BR pdfcrop (1)
.SH ABOUT
-pdfbook2 v1.3 (https://github.com/jenom/pdfbook2)
+pdfbook2 v1.4 (https://github.com/jenom/pdfbook2)
.br
-(c) 2015 - 2019 Johannes Neumann (http://www.neumannjo.de)
+(c) 2015 - 2020 Johannes Neumann (http://www.neumannjo.de)
.br
licensed under GPLv3 (http://www.gnu.org/licenses/gpl-3.0)
.br