From fdb7d0585af6b9054e895d6f3047766f799b6098 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Fri, 1 Nov 2019 21:00:21 +0000 Subject: make4ht (1nov19) git-svn-id: svn://tug.org/texlive/trunk@52603 c570f23f-e606-0410-a88d-b1316a301751 --- .../scripts/make4ht/domfilters/make4ht-aeneas.lua | 3 +- .../make4ht/domfilters/make4ht-collapsetoc.lua | 198 +++++++++++++++++++++ .../make4ht/domfilters/make4ht-joincharacters.lua | 8 +- .../make4ht/domfilters/make4ht-joincolors.lua | 3 +- .../make4ht/domfilters/make4ht-odtimagesize.lua | 3 +- .../make4ht/domfilters/make4ht-odtpartable.lua | 9 + .../make4ht/domfilters/make4ht-tablerows.lua | 58 ++++++ .../make4ht/extensions/common_domfilters.lua | 29 +-- .../scripts/make4ht/extensions/dvisvgm_hashes.lua | 7 +- .../make4ht/extensions/preprocess_input.lua | 108 +++++++++++ .../scripts/make4ht/extensions/staticsite.lua | 3 +- .../texmf-dist/scripts/make4ht/extensions/tidy.lua | 3 +- .../scripts/make4ht/filters/make4ht-domfilter.lua | 7 +- .../filters/make4ht-entities-to-unicode.lua | 8 +- .../make4ht/filters/make4ht-mathjaxnode.lua | 11 +- .../scripts/make4ht/filters/make4ht-staticsite.lua | 6 +- .../scripts/make4ht/filters/make4ht-svg-height.lua | 4 +- .../texmf-dist/scripts/make4ht/formats/docbook.lua | 3 +- Master/texmf-dist/scripts/make4ht/formats/odt.lua | 23 +-- Master/texmf-dist/scripts/make4ht/make4ht | 32 +++- .../scripts/make4ht/make4ht-errorlogparser.lua | 97 ++++++++++ .../texmf-dist/scripts/make4ht/make4ht-htlatex.lua | 75 ++++++++ .../scripts/make4ht/make4ht-indexing.lua | 190 ++++++++++++++++++++ Master/texmf-dist/scripts/make4ht/make4ht-lib.lua | 29 +-- .../texmf-dist/scripts/make4ht/make4ht-logging.lua | 105 +++++++++++ .../texmf-dist/scripts/make4ht/make4ht-xtpipes.lua | 15 +- Master/texmf-dist/scripts/make4ht/mkparams.lua | 134 +++++++++----- Master/texmf-dist/scripts/make4ht/mkutils.lua | 131 +++++++------- 28 files changed, 1114 insertions(+), 188 deletions(-) create mode 100644 Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua create mode 100644 Master/texmf-dist/scripts/make4ht/domfilters/make4ht-odtpartable.lua create mode 100644 Master/texmf-dist/scripts/make4ht/domfilters/make4ht-tablerows.lua create mode 100644 Master/texmf-dist/scripts/make4ht/extensions/preprocess_input.lua create mode 100755 Master/texmf-dist/scripts/make4ht/make4ht-errorlogparser.lua create mode 100755 Master/texmf-dist/scripts/make4ht/make4ht-htlatex.lua create mode 100755 Master/texmf-dist/scripts/make4ht/make4ht-indexing.lua create mode 100755 Master/texmf-dist/scripts/make4ht/make4ht-logging.lua (limited to 'Master/texmf-dist/scripts/make4ht') diff --git a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-aeneas.lua b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-aeneas.lua index 0b93c958404..4a21c893323 100644 --- a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-aeneas.lua +++ b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-aeneas.lua @@ -5,6 +5,7 @@ -- local cssquery = require "luaxml-cssquery" local mkutils = require "mkutils" +local log = logging.new "aeneas" -- Table of CSS selectors to be skipped. local skip_elements = { "math", "svg"} @@ -111,7 +112,7 @@ local function aeneas(dom, par) then local idtitle idtitle, id = make_id(id, id_prefix) - print(el._name, first_child._text) + log:debug(el._name, first_child._text) el:set_attribute("id", idtitle) return el end diff --git a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua new file mode 100644 index 00000000000..0b77b9dbbba --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua @@ -0,0 +1,198 @@ +local domobject = require "luaxml-domobject" + +local filter = require "make4ht-filter" +local log = logging.new "collapsetoc" + + +local toc_levels = {"partToc", "chapterToc", "sectionToc", "subsectionToc", "subsubsectionToc"} + +local debug_print = function(...) log:debug(...) end + + +-- return toc element type and it's id +local function get_id(el) + local name = el:get_attribute "class" + local id + local a = el:query_selector "a" or {} + local first = a[1] + if first then + local href = first:get_attribute "href" + id = href:match("#(.+)$") + end + return name, id +end + +local function remove_sections(part_elements, currentpart) + -- we need to remove toc entries from the previous part if the + -- current document isn't part of it + if currentpart == false then + for _, part in ipairs(part_elements) do + part:remove_node() + end + end +end + +local function make_toc_selector(toc_levels) + local level_classes = {} + for _, l in ipairs(toc_levels) do + level_classes[#level_classes+1] = "." .. l + end + return table.concat(level_classes, ", ") +end + +local function find_toc_levels(toc) + -- find toc levels used in the document + -- it ecpects that sectioning levels appears in the TOC in the descending order + local levels, used = {}, {} + local level = 1 + -- we still expect the standard class names + local toc_selector = make_toc_selector(toc_levels) + for _, el in ipairs(toc:query_selector(toc_selector)) do + local class = el:get_attribute("class") + if not used[class] then + table.insert(levels, class) + used[class] = level + level = level + 1 + end + end + return levels, used +end + +local function remove_levels(toc, matched_ids) + -- sort the matched sections according to their levels + local levels, level_numbers = find_toc_levels(toc) + debug_print("remove levels", #levels) + -- for _, level in ipairs(levels) do + -- print(level, level_numbers[level], matched_ids[level]) + -- end + local keep_branch = false + local matched_levels = {} + local toc_selector = make_toc_selector(toc_levels) + for _, el in ipairs(toc:query_selector(toc_selector)) do + local name, id = get_id(el) + -- get the current toc hiearchy level + local level = level_numbers[name] + -- get the matched id for the current level + local level_id = matched_ids[name] + local matched = level_id == id + local remove = true + -- we will use this for toc elements at lower hiearchy than is the top sectioning level on the page + if matched then keep_branch = true end + -- find the parent level to the current section level + local parent_level = toc_levels[level - 1] + local parent_matched = matched_levels[parent_level] + if matched then + debug_print("match",name, id, level_id, level, #levels) + keep_branch = true + remove = false + elseif level==1 then + -- don't remove the top level + debug_print("part",name, id, level_id, level) + remove = false + matched_levels = {} + if not matched then keep_branch = false end + elseif keep_branch then + -- if level >= (#levels - 1) then + if level > matched_ids._levels then + debug_print("level",name, id, level_id, level, parent_level, parent_matched) + remove = false + elseif matched_ids.ids[id] then + debug_print("matched id",name, id, level_id, level, parent_level, parent_matched) + remove = false + elseif parent_matched then + debug_print("parent_matched",name, id, level_id, level, parent_level, parent_matched) + keep_branch = false + remove = false + end + -- else + -- print("remove", name, id, level_id, level, #matched_ids) + -- end + elseif parent_matched then + debug_print("parent_matched alternative",name, id, level_id, level, parent_level, parent_matched) + remove = false + else + debug_print("else",name, id, level_id, level, keep_branch) + keep_branch = false + end + matched_levels[name] = matched + if remove then + el:remove_node() + --print(name,id, level_id, matched) + end + end + +end + + + + + +-- local process = filter{ function(s) + -- local dom = domobject.parse(s) +local function collapse_toc(dom, par) + -- set options + local options = get_filter_settings "collapsetoc" + local toc_query = par.toc_query or options.toc_query or ".tableofcontents" + local title_query = par.title_query or options.title_query or ".partHead a, .chapterHead a, .sectionHead a, .subsectionHead a" + toc_levels = par.toc_levels or options.toc_levels or toc_levels + -- keep track of current id of each sectioning level + local current_ids, matched_ids = {}, {_levels = 0, ids = {}} + -- search sectioning elements + local titles = dom:query_selector(title_query) + local section_ids = {} + for _, x in ipairs(titles) do + -- get their id attributes and save them in a table + section_ids[#section_ids+1] = x:get_attribute("id") + end + + -- we need to retrieve the first table of contents + local toctables = dom:query_selector(toc_query) or {} + -- process only when we got a TOC + debug_print("toc query", toc_query, #toctables) + if #toctables > 0 then + local tableofcontents = toctables[1] + -- all toc entries are in span elements + local toc = tableofcontents:query_selector("span") + local currentpart = false + local part_elements = {} + for _, el in ipairs(toc) do + -- get sectioning level and id of the current TOC entry + local name, id = get_id(el) + -- set the id of the current sectioning level + current_ids[name] = id + for _, sectid in ipairs(section_ids) do + -- detect if the current TOC entry match some sectioning element in the current document + if id == sectid then + currentpart = true + -- save the current id as a matched id + matched_ids.ids[id] = true + -- copy the TOC hiearchy for the current toc level + for i, level in ipairs(toc_levels) do + -- print("xxx",i, level, current_ids[level]) + matched_ids[level] = current_ids[level] + -- set the maximum matched level + if i > matched_ids._levels then matched_ids._levels = i end + if level == name then break end + end + debug_print("match", id) + end + end + end + remove_levels(tableofcontents, matched_ids) + + -- remove sections from the last part + -- remove_sections(part_elements,currentpart) + -- remove unneeded br elements + local br = tableofcontents:query_selector("br") + for _, el in ipairs(br) do el:remove_node() end + -- remove unneded whitespace + for _, el in ipairs(tableofcontents:get_children()) do + if el:is_text() then el:remove_node() end + end + end + return dom +end + +return collapse_toc + +-- Make:match("html$", process) diff --git a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-joincharacters.lua b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-joincharacters.lua index 99d3058d7dd..30ed6e1382a 100644 --- a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-joincharacters.lua +++ b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-joincharacters.lua @@ -1,4 +1,4 @@ -local charclases = { +local charclasses = { span=true, mn = true, } @@ -8,7 +8,7 @@ local function join_characters(obj,par) -- tex4ht to just one object. local par = par or {} local options = get_filter_settings "joincharacters" - local charclases = options.charclases or par.charclases or charclases + local charclasses = options.charclasses or par.charclasses or charclasses obj:traverse_elements(function(el) local get_name = function(curr) @@ -18,7 +18,7 @@ local function join_characters(obj,par) return next_el:get_attribute("class") end local is_span = function(next_el) - return charclases[get_name(next_el)] + return charclasses[get_name(next_el)] end local function get_next(curr, class) @@ -33,7 +33,7 @@ local function join_characters(obj,par) end end -- loop over all elements and test if the current element is in a list of - -- processed elements (charclases) + -- processed elements (charclasses) if is_span(el) then local next_el = get_next(el) -- loop over the following elements and test whether they are of the same type diff --git a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-joincolors.lua b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-joincolors.lua index 94a3a054e61..730f66a7a0d 100644 --- a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-joincolors.lua +++ b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-joincolors.lua @@ -1,4 +1,5 @@ local cssfiles = {} +local log = logging.new "joincolors" -- keep mapping between span ids and colors @@ -45,7 +46,7 @@ local function process_css_files(dom) for _, el in ipairs(dom:query_selector("link")) do local href = el:get_attribute("href") or "" if not cssfiles[href] and href:match("css$") then - print("Load CSS file ", href) + log:debug("Load CSS file ", href) cssfiles[href] = true process_css(href) end diff --git a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-odtimagesize.lua b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-odtimagesize.lua index 4ce8f0d92ea..2b87372fdb1 100644 --- a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-odtimagesize.lua +++ b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-odtimagesize.lua @@ -1,3 +1,4 @@ +local log = logging.new "odtimagesize" -- set correct dimensions to frames around images return function(dom) local frames = dom:query_selector("draw|frame") @@ -9,7 +10,7 @@ return function(dom) local height = image:get_attribute("svg:height") if widht then frame:set_attribute("svg:width", width) end if height then frame:set_attribute("svg:height", height) end - print("image dimensions", width, height) + log:debug("image dimensions", width, height) end end return dom diff --git a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-odtpartable.lua b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-odtpartable.lua new file mode 100644 index 00000000000..f4e84b55d96 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-odtpartable.lua @@ -0,0 +1,9 @@ +-- find all tables inside paragraphs, replace the found paragraphs with the child table +return function(dom) + for _,table in ipairs(dom:query_selector("text|p table|table")) do + -- replace the paragraph by its child element + local parent = table:get_parent() + parent:replace_node(table) + end + return dom +end diff --git a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-tablerows.lua b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-tablerows.lua new file mode 100644 index 00000000000..155c24529ac --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-tablerows.lua @@ -0,0 +1,58 @@ +local log = logging.new ("tablerows") +return function(dom) + local is_empty_row = function(row) + local not_empty = false + local element_count = 0 + -- ignore hline rows + local row_class = row:get_attribute("class") + if row_class == "hline" or row_class == "cline" then return false end + -- detect if the row contain only one empty child + for _,child in ipairs(row:get_children() or {}) do + if child:is_element() then + element_count = element_count + 1 + -- empty rows contain only one element, it is not empty otherwise + if element_count > 1 then return false end + -- detect if it contains only whitespace + not_empty = child:get_text():gsub("%s","") ~= "" or not_empty + end + end + -- print("element count", element_count, not_empty) + return element_count == 1 and not_empty == false + end + local is_not_styled = function(row, css) + -- get the id attribute and escape it, so it can be used in regexp + local id = row:get_attribute("id") + if not id then return true end -- no styling without id + local search_term = "%#" .. id:gsub("%-", "%%-") + -- if the CSS file contains the row id ( elements can also have id + -- that matches this pattern, so we should keep the row if we match them too) + return not css:match(search_term) + end + local load_css_files = function() + -- the empty rows can be styled using CSS, for example configuration for + -- Booktabs does that. We shouldn't remove such rows. + local cssfiles = {} + for _, link in ipairs(dom:query_selector("head link")) do + local src = link:get_attribute("href") + if src then + local f = io.open(src, "r") + if f then + local contents = f:read("*all") + f:close() + table.insert(cssfiles, contents) + end + end + end + return table.concat(cssfiles, "\n") + end + local css = load_css_files() + for _, tbl in ipairs(dom:query_selector("table")) do + -- find the empty rows + for _, row in ipairs(tbl:query_selector("tr")) do + if is_empty_row(row) and is_not_styled(row, css) then row:remove_node() end + end + + end + return dom +end + diff --git a/Master/texmf-dist/scripts/make4ht/extensions/common_domfilters.lua b/Master/texmf-dist/scripts/make4ht/extensions/common_domfilters.lua index 7c9a2dfaea4..9399492d907 100644 --- a/Master/texmf-dist/scripts/make4ht/extensions/common_domfilters.lua +++ b/Master/texmf-dist/scripts/make4ht/extensions/common_domfilters.lua @@ -1,25 +1,34 @@ local M = {} +-- this variable will hold the output format name +local current_format + local filter = require "make4ht-domfilter" -- local process = filter {"fixinlines", "idcolons", "joincharacters" } -- filters support only html formats function M.test(format) - if format == "odt" then return false end + current_format = format + -- if format == "odt" then return false end return true end function M.modify_build(make) - local process = filter {"fixinlines", "idcolons", "joincharacters"} - make:match("html$", process) - local matches = make.matches - -- the filters should be first match to be executed, especially if tidy - -- should be executed as well - if #matches > 1 then - local last = matches[#matches] - table.insert(matches, 1, last) - matches[#matches] = nil + -- number of filters that should be moved to the beginning + local count = 0 + if current_format == "odt" then + -- some formats doesn't make sense in the ODT format + local process = filter {"joincharacters"} + local charclasses = {mn = true, ["text:span"] = true} + make:match("4oo$", process, {charclasses= charclasses}) + -- match math documents + make:match("4om$", process, {charclasses= charclasses}) + count = 2 + else + local process = filter {"fixinlines", "idcolons", "joincharacters", "tablerows"} + make:match("html$", process) + count = 1 end return make end diff --git a/Master/texmf-dist/scripts/make4ht/extensions/dvisvgm_hashes.lua b/Master/texmf-dist/scripts/make4ht/extensions/dvisvgm_hashes.lua index 83010ac388f..87f58abd57c 100644 --- a/Master/texmf-dist/scripts/make4ht/extensions/dvisvgm_hashes.lua +++ b/Master/texmf-dist/scripts/make4ht/extensions/dvisvgm_hashes.lua @@ -1,6 +1,7 @@ local dvireader = require "make4ht-dvireader" local mkutils = require "mkutils" local filter = require "make4ht-filter" +local log = logging.new "dvisvgm_hashes" local dvisvgm_par = {} @@ -149,7 +150,7 @@ end local function execute_dvisvgm(idvfile, pages) if #pages < 1 then return nil, "No pages to convert" end local command, logs = prepare_command(idvfile, pages) - print(command) + log:info(command) os.execute(command) local generated_pages = {} for _, dvisvgmlog in ipairs(logs) do @@ -184,7 +185,7 @@ local function get_dvi_pages(arg) local output_name = make_hashed_name(arg.input, hash) output_map[tex4ht_name] = output_name if not mkutils.file_exists(output_name) then - print(output_name) + log:debug("output file: ".. output_name) to_convert[#to_convert+1] = page end end @@ -246,7 +247,7 @@ function M.modify_build(make) function(str) return str:gsub('src="([^"]+)', function(filename) local newname = output_map[filename] or filename - print("newname", newname) + log:debug("newname", newname) return 'src="'.. newname end) end diff --git a/Master/texmf-dist/scripts/make4ht/extensions/preprocess_input.lua b/Master/texmf-dist/scripts/make4ht/extensions/preprocess_input.lua new file mode 100644 index 00000000000..3155a8a7810 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/extensions/preprocess_input.lua @@ -0,0 +1,108 @@ +-- preprocess R literate sources or Markdown files to LaTeX +local M = {} +local log = logging.new "preprocess_input" +local mkutils = require "mkutils" + +local commands = { + knitr = { command = 'Rscript -e "library(knitr); knit(\'${tex_file}\', output=\'${tmp_file}\')"'}, + pandoc = { command = 'pandoc -f ${input_format} -s -o \'${tmp_file}\' -t latex \'${tex_file}\''} +} +local filetypes = { + rnw = {sequence = {"knitr"} }, + rtex = {sequence = {"knitr"}}, + rmd = {sequence = {"knitr", "pandoc"}, options = {input_format = "markdown"}}, + rrst = {sequence = {"knitr", "pandoc"}, options = {input_format = "rst"}}, + md = {sequence = {"pandoc"}, options = {input_format = "markdown"}}, + rst = {sequence = {"pandoc"}, options = {input_format = "rst"}}, +} + + +local function execute_sequence(sequence, arg, make) + -- keep track of all generated tmp files + local temp_files = {} + -- the temporary file for the current compilation step + -- should become the tex_file for the next one. It doesn't + -- matter that it isn't TeX file in some cases + local previous_temp + for _, cmd_name in ipairs(sequence) do + local tmp_name = os.tmpname() + temp_files[#temp_files+1] = tmp_name + -- make the temp file name accessible to the executed commands + arg.tmp_file = tmp_name + -- the current temporary file should become tex_file in the next step + -- in the first execution of the compilation sequence we will use the + -- actual input file name + arg.tex_file = previous_temp or arg.tex_file + previous_temp = tmp_name + -- get the command to execute + local cmd = commands[cmd_name] + -- fill the command template with make4ht arguments and execute + local command = cmd.command % arg + log:info(command) + mkutils.execute(command) + end + return temp_files +end + +local function get_preprocessing_pipeline(input_file) + -- detect the file extension + local extension = input_file:match("%.(.-)$") + if not extension then return nil, "Cannot get extension: " .. input_file end + -- the table with file actions is case insensitive + -- the extension is converted to lowercase in order + -- to support both .rnw and .Rnw + extension = string.lower(extension) + local matched = filetypes[extension] + if not matched then return nil, "Unsupported extension: " .. extension end + return matched +end + +-- join the make4ht params and command options tables +local function make_options(arg, command_options) + local options = {} + local command_options = command_options or {} + for k,v in pairs(arg) do options[k] = v end + for k,v in pairs(command_options) do options[k] = v end + return options +end + +M.modify_build = function(make) + + -- get access to the main argumens + local arg = make.params + -- get the execution sequence for the input format + local matched, msg = get_preprocessing_pipeline(arg.tex_file) + if not matched then + log:error("preprocess_input error: ".. msg) + return + end + -- prepare options + local options = make_options(arg, matched.options) + -- run the execution sequence + local temp_files = execute_sequence(matched.sequence or {}, options, make) + -- the last temporary file contains the actual TeX file + local last_temp_file = temp_files[#temp_files] + -- remove the intermediate temp files + if #temp_files > 2 then + for i = 1, #temp_files - 1 do + log:debug("Removing temporary file", temp_files[i]) + os.remove(temp_files[i]) + end + end + if last_temp_file then + -- update all commands in the .mk4 file with the temp file as tex_file + local update_params = function(cmd) + local params = cmd.params + params.tex_file = last_temp_file + params.is_tmp_file = true + end + for _, cmd in ipairs(make.build_seq) do + update_params(cmd) + end + -- also update the main params + update_params(make) + end + return make +end + +return M diff --git a/Master/texmf-dist/scripts/make4ht/extensions/staticsite.lua b/Master/texmf-dist/scripts/make4ht/extensions/staticsite.lua index fcb6c09e17d..a4204bc1733 100644 --- a/Master/texmf-dist/scripts/make4ht/extensions/staticsite.lua +++ b/Master/texmf-dist/scripts/make4ht/extensions/staticsite.lua @@ -1,6 +1,7 @@ local M = {} local filter = require "make4ht-filter" local mkutils = require "mkutils" +local log = logging.new "staticsite" -- get the published file name local function get_slug(settings) @@ -15,7 +16,7 @@ local function get_slug(settings) local f = io.open(published_name, "r") local readtime = f:read("*line") time = tonumber(readtime) - print("Already pubslished", slug) + log:info("Already pubslished", slug) f:close() else -- escape diff --git a/Master/texmf-dist/scripts/make4ht/extensions/tidy.lua b/Master/texmf-dist/scripts/make4ht/extensions/tidy.lua index cf8ea9785a5..b381a05a8fb 100644 --- a/Master/texmf-dist/scripts/make4ht/extensions/tidy.lua +++ b/Master/texmf-dist/scripts/make4ht/extensions/tidy.lua @@ -1,5 +1,6 @@ local M = {} +local log = logging.new "tidy" function M.test(format) if format == "odt" then return false end return true @@ -41,7 +42,7 @@ function M.modify_build(make) local settings = get_filter_settings "tidy" or {} par.options = par.options or settings.options or "-utf8 -w 512 -ashtml -q" local command = "tidy ${options} ${filename}" % par - print("execute: ".. command) + log:info("running tidy: ".. command) -- os.execute(command) local run = io.popen(command, "r") local result = run:read("*all") diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-domfilter.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-domfilter.lua index 27d9ed871d1..f6df769d60c 100644 --- a/Master/texmf-dist/scripts/make4ht/filters/make4ht-domfilter.lua +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-domfilter.lua @@ -1,6 +1,7 @@ local filter_lib = require "make4ht-filterlib" local dom = require "luaxml-domobject" local mkutils = require "mkutils" +local log = logging.new "domfilter" local function load_filter(filtername) return require("domfilters.make4ht-"..filtername) @@ -42,8 +43,8 @@ local function filter(filters, name) return dom.parse(input) end) if not status then - print("DOM parsing of " .. filename .. " failed:") - print(domobject) + log:warning("DOM parsing of " .. filename .. " failed:") + log:warning(domobject) return nil, "DOM parsing failed" end for _,f in pairs(sequence) do @@ -53,7 +54,7 @@ local function filter(filters, name) if output then filter_lib.save_input_file(filename, output) else - print("DOM filter failed on ".. filename) + log:warning("DOM filter failed on ".. filename) end -- mark the filename as processed processed_files[filename] = true diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-entities-to-unicode.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-entities-to-unicode.lua index fb921a631a3..4bf106a79fc 100644 --- a/Master/texmf-dist/scripts/make4ht/filters/make4ht-entities-to-unicode.lua +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-entities-to-unicode.lua @@ -6,10 +6,12 @@ local disabled = { ["&"] = "&", ["<"] = "<", [">"] = ">"} return function(content) local content = content:gsub("%&%#x([A-Fa-f0-9]+);", function(entity) -- convert hexadecimal entity to Unicode - local newchar = utfchar(tonumber(entity, 16)) + local char_number = tonumber(entity, 16) + -- fix for non-breaking spaces, LO cannot open file when they are present as Unicode + if char_number == 160 then return " " end + local newchar = utfchar(char_number) -- we don't want to break XML validity with forbidden characters return disabled[newchar] or newchar end) - -- the non-breaking space character cause issues in the ODT opening - return content:gsub(string.char(160), " ") + return content end diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-mathjaxnode.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-mathjaxnode.lua index 2b453e4255b..748ecfd4839 100644 --- a/Master/texmf-dist/scripts/make4ht/filters/make4ht-mathjaxnode.lua +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-mathjaxnode.lua @@ -1,4 +1,5 @@ local mkutils = require "mkutils" +local log = logging.new("mathjaxnode") -- other possible value is page2svg local mathnodepath = "mjpage" -- options for MathJax command @@ -13,13 +14,13 @@ local cssfilename = "mathjax-chtml.css" local function compile(text) local tmpfile = os.tmpname() - print("Compile using MathJax") + log:info("Compile using MathJax") local command = mathnodepath .. " ".. options .. " > " .. tmpfile - print(command) + log:info(command) local commandhandle = io.popen(command,"w") commandhandle:write(text) commandhandle:close() - print("Result written to: ".. tmpfile) + log:info("Result written to: ".. tmpfile) local f = io.open(tmpfile) local content = f:read("*all") f:close() @@ -53,7 +54,7 @@ local function use_fonts(css) if not face:match("url%(") then return face end -- print(face) local family, filename = face:match(family_pattern) - print(family, filename) + log:info("use font: ",family, filename) local newfile = string.format("%s/%s.%s", fontdir, filename, fontformat) Make:add_file(newfile) return family_build:format(family, fontdir, filename, fontformat, fontformat) @@ -89,6 +90,6 @@ return function(text, arguments) save_css(cssfile, css) Make:add_file(cssfile) -- print(css) - print(cssfile) + log:info("CSS file: " .. cssfile) return newtext end diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-staticsite.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-staticsite.lua index 6378f0440aa..969d47ecaaa 100644 --- a/Master/texmf-dist/scripts/make4ht/filters/make4ht-staticsite.lua +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-staticsite.lua @@ -1,4 +1,5 @@ local domobj = require "luaxml-domobject" +local log = logging.new("staticsite") -- save the header settings in YAML format local function make_yaml(tbl, level) local t = {} @@ -69,7 +70,6 @@ local function get_header(tbl) end return function(s,par) - print(os.getenv "blog_home") local dom = domobj.parse(s) local properties = {} local head = dom:query_selector("head")[1] @@ -84,7 +84,7 @@ return function(s,par) properties.styles = styles local metas = {} for _, meta in ipairs(head:query_selector("meta")) do - print(meta:serialize()) + log:debug("parsed meta: " .. meta:serialize()) table.insert(metas, {charset= meta:get_attribute("charset"), content = meta:get_attribute("content"), property = meta:get_attribute("property"), name = meta:get_attribute("name")}) end properties.meta = metas @@ -92,7 +92,7 @@ return function(s,par) local body = dom:query_selector("body")[1] - print(get_header(properties)) + log:debug(get_header(properties)) -- return s return get_header(properties) .. body:serialize():gsub("", ""):gsub("", "") end diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-svg-height.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-svg-height.lua index 44e98748ea9..bb9f8dbdc60 100644 --- a/Master/texmf-dist/scripts/make4ht/filters/make4ht-svg-height.lua +++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-svg-height.lua @@ -1,6 +1,8 @@ +local log = logging.new("svg-height") -- Make:image("svg$", "dvisvgm -n -a -p ${page} -b preview -c 1.4,1.4 -s ${source} > ${output}") + local max = function(a,b) return a > b and a or b end @@ -34,7 +36,7 @@ return function(svg) max_height = get_max_height(path, max_height) end -- update the height only if the max_height is larger than height set in the SVG file - print(max_height, height) + log:debug("max height and height", max_height, height) if max_height > height then svg = update_height(svg, max_height) end diff --git a/Master/texmf-dist/scripts/make4ht/formats/docbook.lua b/Master/texmf-dist/scripts/make4ht/formats/docbook.lua index 9e5ffe9488b..3bd79eb83ed 100644 --- a/Master/texmf-dist/scripts/make4ht/formats/docbook.lua +++ b/Master/texmf-dist/scripts/make4ht/formats/docbook.lua @@ -6,6 +6,7 @@ local kpse = require "kpse" local filter = require "make4ht-filter" local domfilter = require "make4ht-domfilter" local xtpipeslib = require "make4ht-xtpipes" +local log = logging.new "docbook" function M.prepare_parameters(settings, extensions) settings.tex4ht_sty_par = settings.tex4ht_sty_par ..",docbook" @@ -25,7 +26,7 @@ local function call_xtpipes(make) make:match("xml$", matchfunction) move_matches(make) else - print "Cannot locate xtpipes. Try to set TEXMFROOT variable to a root directory of your TeX distribution" + log:warning "Cannot locate xtpipes. Try to set TEXMFROOT variable to a root directory of your TeX distribution" end end diff --git a/Master/texmf-dist/scripts/make4ht/formats/odt.lua b/Master/texmf-dist/scripts/make4ht/formats/odt.lua index 64b8793e2a7..c515a154a1b 100644 --- a/Master/texmf-dist/scripts/make4ht/formats/odt.lua +++ b/Master/texmf-dist/scripts/make4ht/formats/odt.lua @@ -6,6 +6,7 @@ local kpse = require "kpse" local filter = require "make4ht-filter" local domfilter = require "make4ht-domfilter" local xtpipeslib = require "make4ht-xtpipes" +local log = logging.new "odt" function M.prepare_parameters(settings, extensions) @@ -23,9 +24,12 @@ Odtfile.__index = Odtfile Odtfile.new = function(archivename) local self = setmetatable({}, Odtfile) - -- create temporary directory + -- create a temporary file local tmpname = os.tmpname() - tmpname = tmpname:match("([a-zA-Z0-9_%-]+)$") + -- remove a temporary file, we are interested only in the unique file name + os.remove(tmpname) + -- get the unique dir name + tmpname = tmpname:match("([a-zA-Z0-9_%-%.]+)$") local status, msg = lfs.mkdir(tmpname) if not status then return nil, msg end -- make picture dir @@ -68,10 +72,10 @@ function Odtfile:pack() lfs.chdir(self.archivelocation) -- make temporary mime type file self:make_mimetype() - os.execute(zip_command .. " -q0X " .. self.name .. " " .. self.mimetypename) + mkutils.execute(zip_command .. " -q0X " .. self.name .. " " .. self.mimetypename) -- remove it, so the next command doesn't overwrite it self:remove_mimetype() - os.execute(zip_command .." -r " .. self.name .. " *") + mkutils.execute(zip_command .." -r " .. self.name .. " *") lfs.chdir(currentdir) mkutils.cp(self.archivelocation .. "/" .. self.name, self.name) mkutils.delete_dir(self.archivelocation) @@ -99,7 +103,7 @@ local function call_xtpipes(make) move_matches(make) move_matches(make) else - print "Cannot locate xtpipes. Try to set TEXMFROOT variable to a root directory of your TeX distribution" + log:warning "Cannot locate xtpipes. Try to set TEXMFROOT variable to a root directory of your TeX distribution" end end @@ -111,7 +115,7 @@ local function prepare_output_files(lgfiles) local group = groups[extension] or {} table.insert(group, basename) groups[extension] = group - print(basename, extension) + log:debug("prepare output file", basename, extension) end return groups end @@ -130,7 +134,7 @@ function M.modify_build(make) -- expanded in tex4ht.env in Miktex or Debian call_xtpipes(make) -- fix the image dimensions wrongly set by xtpipes - local domfilters = domfilter {"t4htlinks"} + local domfilters = domfilter {"t4htlinks", "odtpartable"} make:match("4oo$", domfilters) -- execute it before xtpipes, because we don't want xtpipes to mess with t4htlink elements move_matches(make) @@ -138,9 +142,6 @@ function M.modify_build(make) local fixentities = filter {"entities-to-unicode"} make:match("4oo", fixentities) make:match("4om", fixentities) - -- execute fixentities as first - move_matches(make) - move_matches(make) -- build the ODT file. This match must be executed as a last one -- this will be executed as a first match, just to find the last filename @@ -161,7 +162,7 @@ function M.modify_build(make) local odtname = basename .. ".odt" local odt,msg = Odtfile.new(odtname) if not odt then - print("Cannot create ODT file: " .. msg) + log:error("Cannot create ODT file: " .. msg) end -- helper function for simple file moving local function move_file(group, dest) diff --git a/Master/texmf-dist/scripts/make4ht/make4ht b/Master/texmf-dist/scripts/make4ht/make4ht index 564bc6e7a04..b1ff84f7006 100755 --- a/Master/texmf-dist/scripts/make4ht/make4ht +++ b/Master/texmf-dist/scripts/make4ht/make4ht @@ -3,8 +3,10 @@ -- This package is subject of LPPL license, version 1.3 kpse.set_program_name("luatex") - - +-- logging should be globally available +logging = require "make4ht-logging" +if os.type == "windows" then logging.use_colors = false end +local log = logging.new("make4ht") local make4ht = require("make4ht-lib") local lapp = require("lapp-mk4") local mkutils = require("mkutils") @@ -13,7 +15,7 @@ local mk_config = require("make4ht-config") -- args string is here just as sample, we dont pass it it to -- mkparams.get_args() so default args string is used local args = [[ -make4ht - build system for tex4ht +make4ht - build system for TeX4ht Usage: make4ht [options] filename ["tex4ht.sty op." "tex4ht op." "t4ht op" "latex op"] -c,--config (default xhtml) Custom config file @@ -27,13 +29,16 @@ make4ht [options] filename ["tex4ht.sty op." "tex4ht op." "t4ht op" "latex op"] -- set version number. the template should be replaced by the -- actual version number by the build script -local version = "v0.2g" +local version = "v0.3" mkparams.version_number = version local args = mkparams.get_args() local parameters = mkparams.process_args(args) +log:status("Conversion started") +log:status("Input file: " .. parameters.tex_file) + local mode = parameters.mode local build_file = parameters.build_file @@ -48,7 +53,7 @@ if allowed_output_formats[ output_format ] then else -- load html5 as default output format if output_format then - print("Cannot load output format: ".. output_format) + log:warning("Cannot load output format: ".. output_format) end formatter = mkutils.load_output_format("html5") end @@ -56,7 +61,7 @@ end local configname = "make4ht" local conffile = mk_config.find_config(configname) or mk_config.find_xdg_config(configname) if conffile then - print("Using configuration file: " .. conffile) + log:info("Using configuration file: " .. conffile) mkutils.load_config(parameters, conffile) end local extensions = formatter.prepare_extensions(parameters.extensions) @@ -96,15 +101,26 @@ end -- allow output formats to modify the build process at the end make = formatter.modify_build(make) or make +make:match("tmp$", function(filename,params) + -- remove the temporary tex file created when the input comes from the standard input + if params.is_tmp_file then + log:info("removing temp file", params.tex_file) + os.remove(params.tex_file) + end + -- prevent copying of the temporary file to the outdir + return false,"tmp file" end +) -make:match("tmp$", function() return false,"tmp file" end) make:match(".*",function(filename,par) local outdir = '' --par["outdir"] and par["outdir"] .."/" or '' if par['outdir'] ~= "" then outdir = par['outdir'] .. '/' end - print("outdir: "..outdir) + log:info("outdir: "..outdir) local outfilename = outdir .. filename mkutils.copy(filename,outfilename) return true end) make:run() + +log:status("Conversion finished") + diff --git a/Master/texmf-dist/scripts/make4ht/make4ht-errorlogparser.lua b/Master/texmf-dist/scripts/make4ht/make4ht-errorlogparser.lua new file mode 100755 index 00000000000..b48957c6514 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/make4ht-errorlogparser.lua @@ -0,0 +1,97 @@ +local m = {} + +local function get_filename(chunk) + local filename = chunk:match("([^\n^%(]+)") + local first = filename:match("^[%./\\]+") + if first then return filename end + return false +end + +local function get_chunks(text) + -- parse log for particular included files + local chunks = {} + -- each file is enclosed in matching () brackets + local newtext = text:gsub("(%b())", function(a) + local chunk = string.sub(a,2,-2) + -- if no filename had been found in the chunk, it is probably not file chunk + -- so just return the original text + local filename = get_filename(chunk) + if not filename then return a end + local children, text = get_chunks(chunk) + table.insert(chunks, {filename = filename, text = text, children = children}) + return "" + end) + return chunks, newtext +end + + +function print_chunks(chunks, level) + local level = level or 0 + local indent = string.rep(" ", level) + for k,v in ipairs(chunks) do + print(indent .. (v.filename or "?"), string.len(v.text)) + print_chunks(v.children, level + 1) + end +end + +local function parse_errors(text) + local lines = {} + local errors = {} + local find_line_no = false + local error_lines = {} + for line in text:gmatch("([^\n]+)") do + lines[#lines+1] = line + end + for i = 1, #lines do + local line = lines[i] + -- error lines start with ! + local err = line:match("^!(.+)") + -- error lines can be on following lines + -- the format is l.number + local lineno = line:match("^l%.([0-9]+)") + if err then + errors[#errors+1] = err + -- we didn't find error line number since previous error, insert + if find_line_no then + error_lines[#error_lines+1] = false + end + find_line_no = true + elseif lineno then + find_line_no = false + error_lines[#error_lines+1] = tonumber(lineno) + end + i = i + 1 + end + return errors, error_lines +end + + +local function get_errors(chunks, errors) + local errors = errors or {} + for _, v in ipairs(chunks) do + local current_errors, error_lines = parse_errors(v.text) + for i, err in ipairs(current_errors) do + table.insert(errors, {filename = v.filename, error = err, line = error_lines[i] }) + end + errors = get_errors(v.children, errors) + end + return errors +end + + +function m.parse(log) + local chunks, newtext = get_chunks(log) + -- save the unparsed text that contains system messages + table.insert(chunks, {text = newtext, children = {}}) + -- print_chunks(chunks) + local errors = get_errors(chunks) + -- for _,v in ipairs(errors) do + -- print("error", v.filename, v.line, v.error) + -- end + return errors, chunks +end + + +m.print_chunks = print_chunks + +return m diff --git a/Master/texmf-dist/scripts/make4ht/make4ht-htlatex.lua b/Master/texmf-dist/scripts/make4ht/make4ht-htlatex.lua new file mode 100755 index 00000000000..214972e8bb9 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/make4ht-htlatex.lua @@ -0,0 +1,75 @@ +local log = logging.new "htlatex" + +local error_logparser = require("make4ht-errorlogparser") + +local Make = Make or {} +-- this function reads the LaTeX log file and tries to detect fatal errors in the compilation +local function testlogfile(par) + local logfile = par.input .. ".log" + local f = io.open(logfile,"r") + if not f then + log:warning("Make4ht: cannot open log file "..logfile) + return 1 + end + local content = f:read("*a") + -- test only the end of the log file, no need to run search functions on everything + local text = content:sub(-1256) + f:close() + -- parse log file for all errors in non-interactive modes + if par.interaction~="errorstopmode" then + -- the error log parsing can be slow, so detect errors first + if content:match("\n!") then + local errors, chunks = error_logparser.parse(content) + if #errors > 0 then + log:error("Compilation errors in the htlatex run") + log:error("Filename", "Line", "Message") + for _, err in ipairs(errors) do + log:error(err.filename or "?", err.line or "?", err.error) + end + end + end + end + -- test for fatal errors + if text:match("No pages of output") or text:match("TeX capacity exceeded, sorry") or text:match("That makes 100 errors") or text:match("Emergency stop") then return 1 end + return 0 +end + + +-- Make this function available in the build files +Make.testlogfile = testlogfile +--env.Make:add("htlatex", "${htlatex} ${latex_par} '\\\makeatletter\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else\\expandafter\\Link\\fi}\\def\\Link#1.a.b.c.{\\g@addto@macro\\@documentclasshook{\\RequirePackage[#1,html]{tex4ht}\\let\\HCode\\documentstyle\\def\\documentstyle{\\let\\documentstyle\\HCode\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\def\\HCode####1{\\documentstyle[tex4ht,}\\@ifnextchar[{\\HCode}{\\documentstyle[tex4ht]}}}\\makeatother\\HCode '${config}${tex4ht_sty_par}'.a.b.c.\\input ' ${input}") + +-- template for calling LaTeX with tex4ht loaded +Make.latex_command = "${htlatex} --interaction=${interaction} ${latex_par} '\\makeatletter".. +"\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode".. +"\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else".. +"\\expandafter\\Link\\fi}\\def\\Link#1.a.b.c.{\\g@addto@macro".. +"\\@documentclasshook{\\RequirePackage[#1,html]{tex4ht}${packages}}".. +"\\let\\HCode\\documentstyle\\def\\documentstyle{\\let\\documentstyle".. +"\\HCode\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\def".. +"\\HCode####1{\\documentstyle[tex4ht,}\\@ifnextchar[{\\HCode}{".. +"\\documentstyle[tex4ht]}}}\\makeatother\\HCode ${tex4ht_sty_par}.a.b.c.".. +"\\input \"\\detokenize{${tex_file}}\"'" + + +local m = {} + +function m.htlatex(par) + local command = Make.latex_command + local devnull = " > /dev/null 2>&1" + if os.type == "windows" then + command = command:gsub("'",'') + devnull = " > nul 2>&1" + end + par.interaction = par.interaction or "batchmode" + -- remove all terminal output from the batchmode + if par.interaction == "batchmode" then + command = command .. devnull + end + command = command % par + log:info("LaTeX call: "..command) + os.execute(command) + return Make.testlogfile(par) +end + +return m diff --git a/Master/texmf-dist/scripts/make4ht/make4ht-indexing.lua b/Master/texmf-dist/scripts/make4ht/make4ht-indexing.lua new file mode 100755 index 00000000000..5801df509bc --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/make4ht-indexing.lua @@ -0,0 +1,190 @@ + +local M = {} +local log = logging.new "indexing" + +-- Handle accented characters in files created with \usepackage[utf]{inputenc} +-- this code was originally part of https://github.com/michal-h21/iec2utf/ +local enc = {} + +local licrs = {} +local codepoint2utf = unicode.utf8.char +local used_encodings = {} + +-- load inputenc encoding file +local function load_encfiles(f) + local file= io.open(f,"r") + local encodings = file:read("*all") + file:close() + for codepoint, licr in encodings:gmatch('DeclareUnicodeCharacter(%b{})(%b{})') do + local codepoint = codepoint2utf(tonumber(codepoint:sub(2,-2),16)) + local licr= licr:sub(2,-2):gsub('@tabacckludge','') + licrs[licr] = codepoint + end +end + +local function sanitize_licr(l) + return l:gsub(" (.)",function(s) if s:match("[%a]") then return " "..s else return s end end):sub(2,-2) +end + +local load_enc = function(enc) + -- use default encodings if used doesn't provide one + enc = enc or {"T1","T2A","T2B","T2C","T3","T5", "LGR"} + for _,e in pairs(enc) do + local filename = e:lower() .. "enc.dfu" + -- don't process an enc file multiple times + if not used_encodings[filename] then + local dfufile = kpse.find_file(filename) + if dfufile then + load_encfiles(dfufile) + end + end + used_encodings[filename] = true + end +end + + + +local cache = {} + +local get_utf8 = function(input) + local output = input:gsub('\\IeC[%s]*(%b{})',function(iec) + -- remove \protect commands + local iec = iec:gsub("\\protect%s*", "") + local code = cache[iec] or licrs[sanitize_licr(iec)] or '\\IeC '..iec + -- print(iec, code) + cache[iec] = code + return code + end) + return output +end + + +-- parse the idx file produced by tex4ht +-- it replaces the document page numbers by index entry number +-- each index entry can then link to place in the HTML file where the +-- \index command had been used + +local parse_idx = function(content) + -- index entry number + local current_entry = 0 + -- map between index entry number and corresponding HTML file and destination + local map = {} + local buffer = {} + + for line in content:gmatch("([^\n]+)") do + if line:match("^\\beforeentry") then + -- increment index entry number + current_entry = current_entry + 1 + local file, dest = line:match("\\beforeentry{(.-)}{(.-)}") + map[current_entry] = {file = file, dest = dest} + elseif line:match("^\\indexentry") then + -- replace the page number with the current + -- index entry number + local result = line:gsub("{[0-9]+}", "{"..current_entry .."}") + buffer[#buffer+1] = get_utf8(result) + else + buffer[#buffer+1] = line + end + end + -- return table with page to dest map and updated idx file + return {map = map, idx = table.concat(buffer, "\n")} +end + +-- replace page numbers in the ind file with hyperlinks +local fix_idx_pages = function(content, idxobj) + local buffer = {} + local entries = idxobj.map + for line in content:gmatch("([^\n]+)") do + local line = line:gsub("(%s*\\%a+.-%,)(.+)$", function(start,rest) + return start .. rest:gsub("(%d+)", function(page) + local entry = entries[tonumber(page)] + if entry then + -- construct link to the index entry + return "\\Link[" .. entry.file .."]{".. entry.dest .."}{}" .. page .."\\EndLink{}" + else + return page + end + end) + end) + buffer[#buffer+1] = line + end + return table.concat(buffer, "\n") +end + +-- prepare the .idx file produced by tex4ht +-- for use with Xindy or Makeindex +local prepare_idx = function(filename) + local f = io.open(filename, "r") + if not f then return nil, "Cannot open file :".. tostring(filename) end + local content = f:read("*all") + local idx = parse_idx(content) + local idxname = os.tmpname() + local f = io.open(idxname, "w") + f:write(idx.idx) + f:close() + -- return the object with mapping between dummy page numbers + -- and link destinations in the files, and the temporary .idx file + -- these can be used for the processing with the index processor + return idx, idxname +end + +-- add links to a index file +local process_index = function(indname, idx) + local f = io.open(indname, "r") + if not f then return nil, "Cannot open .ind file: " .. tostring(indname) end + local content = f:read("*all") + f:close() + + local newcontent = fix_idx_pages(content, idx) + local f = io.open(indname,"w") + f:write(newcontent) + f:close() + return true +end + +local prepare_tmp_idx = function(par) + par.idxfile = par.idxfile or par.input .. ".idx" + -- construct the .ind name, based on the .idx name + par.indfile = par.indfile or par.idxfile:gsub("idx$", "ind") + load_enc() + -- save hyperlinks and clean the .idx file + local idxdata, newidxfile = prepare_idx(par.idxfile) + if not idxdata then + -- if the prepare_idx function returns nil, the second reuturned value contains error msg + return nil, newidxfile + end + return newidxfile, idxdata +end + +local run_indexing_command = function(command, par) + -- detect command name from the command. It will be the first word + local cmd_name = command:match("^[%a]+") or "indexing" + local xindylog = logging.new(cmd_name) + local newidxfile, idxdata = prepare_tmp_idx(par) + if not newidxfile then + -- the idxdata will contain error message in the case of error + xindylog:warning(idxdata) + return false + end + par.newidxfile = newidxfile + xindylog:debug("Prepared temporary idx file: ", newidxfile) + -- prepare modules + local xindy_call = command % par + xindylog:info(xindy_call) + local status = mkutils.execute(xindy_call) + -- insert correct links to the index + local status, msg = process_index(par.indfile, idxdata) + if not status then xindylog:warning(msg) end + -- remove the temporary idx file + os.remove(newidxfile) +end + +M.get_utf8 = get_utf8 +M.load_enc = load_enc +M.parse_idx = parse_idx +M.fix_idx_pages = fix_idx_pages +M.prepare_idx = prepare_idx +M.process_index = process_index +M.prepare_tmp_idx = prepare_tmp_idx +M.run_indexing_command = run_indexing_command +return M diff --git a/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua b/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua index 2ada398f869..91418d18b5a 100755 --- a/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua +++ b/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua @@ -2,6 +2,7 @@ --kpse.set_program_name("luatex") -- module(...,package.seeall) local m = {} +local log = logging.new "make4ht-lib" Make = {} --Make.params = {} @@ -17,13 +18,13 @@ Make.add = function(self,name,fn,par,rep) Make[name] = function(self,p,typ) local params = {} for k,v in pairs(self.params) do params[k] = v end - for k,v in pairs(par) do params[k] = v; print("setting param "..k) end + for k,v in pairs(par) do params[k] = v; log:info("setting param "..k) end local typ = typ or "make" local p = p or {} local fn = fn for k,v in pairs(p) do params[k]=v - print("Adding: ",k,v) + log:info("Adding: ",k,v) end -- print( fn % params) local command = { @@ -50,14 +51,14 @@ Make.run_command = function(self,filename,s) local command = s.command local params = s.params params["filename"] = filename - print("parse_lg process file: "..filename) + log:info("parse_lg process file: "..filename) --for k,v in pairs(params) do print(k..": "..v) end if type(command) == "function" then return command(filename,params) elseif type(command) == "string" then local run = command % params - print("Execute: " .. run) - return os.execute(run) + log:info("Execute: " .. run) + return mkutils.execute(run) end return false, "parse_lg: Command is not string or function" end @@ -95,8 +96,8 @@ Make.image_convert = function(self, images) command(p) elseif type(command) == "string" then local c = command % p - print("Make4ht convert: "..c) - os.execute(c) + log:info("Make4ht convert: "..c) + mkutils.execute(c) end break end @@ -125,7 +126,7 @@ Make.file_matches = function(self, files) msg = msg or "No message given" table.insert(statuses[file],status) if status == false then - print(msg) + log:info(msg) break end end @@ -173,14 +174,14 @@ Make.run = function(self) self.run_count[v.command] = run_count local repetition = v.repetition if repetition and run_count > repetition then - print ("Make4ht: ".. command .." can be executed only "..repetition .."x") + log:warning (command .." can be executed only "..repetition .."x") else - print("Make4ht: " .. command) - local status = os.execute(command) + log:info("executing: " .. command) + local status = mkutils.execute(command) table.insert(return_codes,{name=v.name,status=status}) end else - print("Unknown command type, must be string or function - " ..v.name..": "..type(v.command)) + log:warning("Unknown command type, must be string or function - " ..v.name..": "..type(v.command)) end local correct_exit = params.correct_exit or nil if correct_exit then @@ -188,7 +189,7 @@ Make.run = function(self) local current_status = last_return.status or 0 if current_status ~= correct_exit then local last_name = last_return.name or "unknown" - print("Make4ht: Fatal error. Command "..last_name .." returned exit code "..current_status) + log:fatal("Fatal error. Command "..last_name .." returned exit code "..current_status) os.exit(1) end end @@ -208,7 +209,7 @@ Make.run = function(self) end self:file_matches(files) else - print("No lg file. tex4ht run failed?") + log:warning("No lg file. tex4ht run failed?") end return return_codes end diff --git a/Master/texmf-dist/scripts/make4ht/make4ht-logging.lua b/Master/texmf-dist/scripts/make4ht/make4ht-logging.lua new file mode 100755 index 00000000000..c1d2b24a577 --- /dev/null +++ b/Master/texmf-dist/scripts/make4ht/make4ht-logging.lua @@ -0,0 +1,105 @@ +-- logging system for make4ht +-- inspired by https://github.com/rxi/log.lua +local logging = {} + +local levels = {} +-- level of bugs that should be shown +local show_level = 1 +local max_width = 0 + +logging.use_colors = true + +logging.modes = { + {name = "debug", color = 34}, + {name = "info", color = 32}, + {name = "status", color = 37}, + {name = "warning", color = 33}, + {name = "error", color = 31}, + {name = "fatal", color = 35} +} + +-- prepare table with mapping between mode names and corresponding levels + +function logging.prepare_levels(modes) + local modes = modes or logging.modes + logging.modes = modes + for level, mode in ipairs(modes) do + levels[mode.name] = level + mode.level = level + max_width = math.max(string.len(mode.name), max_width) + end +end + +-- the logging level is set once +function logging.set_level(name) + local level = levels[name] or 1 + show_level = level +end + +function logging.print_msg(header, message, color) + local color = color or 0 + -- use format for collors depending on the use_colors option + local header = "[" .. header .. "]" + local color_format = logging.use_colors and string.format("\27[%im%%s\27[0m%%s", color) or "%s%s" + -- the padding is maximal mode name width + brackets + space + local padded_header = string.format("%-".. max_width + 3 .. "s", header) + print(string.format(color_format, padded_header, message)) +end + +-- +function logging.new(module) + local obj = { + module = module, + output = function(self, output) + -- used for printing of output of commands + if show_level <= (levels[debug] or 1) then + print(output) + end + end + } + obj.__index = obj + -- make a function for each mode + for _, mode in ipairs(logging.modes) do + local name = mode.name + local color = mode.color + obj[name] = function(self, ...) + -- max width is saved in logging.prepare_levels + if mode.level >= show_level then + -- support variable number of parameters + local table_with_holes = table.pack(...) + local table_without_holes = {} + -- trick used to support the nil values in the varargs + -- https://stackoverflow.com/a/7186820/2467963 + for i= 1, table_with_holes.n do + table.insert(table_without_holes, tostring(table_with_holes[i]) or "") + end + local msg = table.concat(table_without_holes, "\t") + logging.print_msg(string.upper(name), string.format("%s: %s", self.module, msg), color) + end + end + end + return setmetatable({}, obj) + +end + + +-- prepare default levels +logging.prepare_levels() + +-- for _, mode in ipairs(logging.modes) do +-- logging.print_msg(mode.name,"xxxx", mode.color) +-- end + +-- local cls = logging.new("sample") +-- cls:warning("hello") +-- cls:error("world") +-- cls:info("set new level") +-- logging.set_level("error") +-- cls:info("level set") +-- cls:error("just print the error") + + +return logging + + + diff --git a/Master/texmf-dist/scripts/make4ht/make4ht-xtpipes.lua b/Master/texmf-dist/scripts/make4ht/make4ht-xtpipes.lua index 78014baff1c..3f5b51e07b8 100755 --- a/Master/texmf-dist/scripts/make4ht/make4ht-xtpipes.lua +++ b/Master/texmf-dist/scripts/make4ht/make4ht-xtpipes.lua @@ -1,5 +1,6 @@ local M = {} +local log = logging.new "xtpipes" -- find if tex4ht.jar exists in a path local function find_tex4ht_jar(path) local jar_file = path .. "/tex4ht/bin/tex4ht.jar" @@ -38,7 +39,7 @@ end function M.get_xtpipes(selfautoparent) -- make pattern using TeX distro path - local pattern = string.format("java -classpath %s/tex4ht/bin/tex4ht.jar xtpipes -i %s/tex4ht/xtpipes/ -o ${outputfile} ${filename}", selfautoparent, selfautoparent) + local pattern = string.format('java -classpath "%s/tex4ht/bin/tex4ht.jar" xtpipes -i "%s/tex4ht/xtpipes/" -o "${outputfile}" "${filename}"', selfautoparent, selfautoparent) -- call xtpipes on a temporary file local matchfunction = function(filename) -- move the matched file to a temporary file, xtpipes will write it back to the original file @@ -46,19 +47,19 @@ function M.get_xtpipes(selfautoparent) local tmpfile = basename ..".tmp" mkutils.mv(filename, tmpfile) local command = pattern % {filename = tmpfile, outputfile = filename} - print(command) - local status = os.execute(command) + log:info("execute: " ..command) + local status = mkutils.execute(command) if status > 0 then -- if xtpipes failed to process the file, it may mean that it was bad-formed xml -- we can try to make it well-formed using Tidy - local tidy_command = "tidy -utf8 -xml -asxml -q -o ${filename} ${tmpfile}" % {tmpfile = tmpfile, filename = filename} - print("xtpipes failed trying tidy") - print(tidy_command) + local tidy_command = 'tidy -utf8 -xml -asxml -q -o "${filename}" "${tmpfile}"' % {tmpfile = tmpfile, filename = filename} + log:warning("xtpipes failed trying tidy") + log:debug(tidy_command) local status = os.execute(tidy_command) if status > 0 then -- if tidy failed as well, just use the original file -- it will probably produce corrupted ODT file though - print("Tidy failed as well") + log:warning("Tidy failed as well") mkutils.mv(tmpfile, filename) end end diff --git a/Master/texmf-dist/scripts/make4ht/mkparams.lua b/Master/texmf-dist/scripts/make4ht/mkparams.lua index 4db967f2706..eff8c99670b 100755 --- a/Master/texmf-dist/scripts/make4ht/mkparams.lua +++ b/Master/texmf-dist/scripts/make4ht/mkparams.lua @@ -1,6 +1,7 @@ local lapp = require "lapp-mk4" local mkutils = require "mkutils" local m = {} -- use ugly module system for new lua versions support +local log = logging.new "mkparams" -- these two variables will be used in the version number -- progname will be set in get_args @@ -9,25 +10,28 @@ m.progname = "make4ht" m.version_number = "v0.1" m.optiontext = [[ -${progname} - build system for tex4ht +${progname} - build system for TeX4ht Usage: ${progname} [options] filename ["tex4ht.sty op."] ["tex4ht op."] ["t4ht op"] ["latex op"] Available options: + -a,--loglevel (default status) Set log level. + possible values: debug, info, status, warning, error, fatal -b,--backend (default tex4ht) Backend used for xml generation. possible values: tex4ht or lua4ht -c,--config (default xhtml) Custom config file -d,--output-dir (default nil) Output directory -e,--build-file (default nil) If build file is different than `filename`.mk4 -f,--format (default html5) Output file format - -h,-- help Display this message + -h,--help Display this message + -j,--jobname (default nil) Set the jobname -l,--lua Use lualatex for document compilation -m,--mode (default default) Switch which can be used in the makefile - -n,--no-tex4ht Disable dvi file processing with tex4ht command + -n,--no-tex4ht Disable dvi file processing with the tex4ht command -s,--shell-escape Enables running external programs from LaTeX -u,--utf8 For output documents in utf8 encoding - -x,--xetex Use xelatex for document compilation -v,--version Display version number + -x,--xetex Use xelatex for document compilation ]] -- test if the current command line argument should be passed to tex4ht, t4ht or latex @@ -36,11 +40,12 @@ local function is_escapedargument(arg) local ignored_options = {["-h"]=true, ["--help"]=true, ["-v"] = true, ["--version"]=true} if ignored_options[arg] then return false end -- in other cases, match if the argument starts with "-" character - return arg:match("^%-") + return arg:match("^%-.+") end local function get_args(parameters, optiontext) local parameters = parameters or {} parameters.progname = parameters.progname or "make4ht" + parameters.issue_tracker = parameters.issue_tracker or "https://github.com/michal-h21/make4ht/issues" parameters.postparams = parameters.postparams or "" local optiontext = optiontext or m.optiontext parameters.postfile = parameters.postfile or "" @@ -54,7 +59,7 @@ Positional optional argumens: Documentation: https://tug.org/applications/tex4ht/mn.html Issue tracker for tex4ht bugs: https://puszcza.gnu.org.ua/bugs/?group=tex4ht -Issue tracker for make4ht bugs: https://github.com/michal-h21/make4ht/issues +Issue tracker for ${progname} bugs: ${issue_tracker} ]] .. parameters.postfile -- we can pass arguments for tex4ht and t4ht after filename, but it will confuse lapp, thinking that these -- options are for make4ht. this may result in execution error or wrong option parsing @@ -88,6 +93,61 @@ local function get_format_extensions(format_string) return format, extensions end +-- detect if user specified -jobname in arguments to the TeX engine +-- or used the --jobname option for make4ht +local function handle_jobname(input, args) + -- parameters to the TeX engine + local latex_params = {} + local latex_cli_params = args[4] or "" + -- use the jobname as input name if it is specified + local jobname = args.jobname ~="nil" and args.jobname or nil + if jobname or not latex_cli_params:match("%-jobname") then + -- prefer jobname over input + input = jobname or input + -- we must strip out directories from jobname when full path to document is given + input = input:match("([^%/^%\\]+)$") + -- input also cannot contain spaces, replace them with underscores + input = input:gsub("%s", "_") + table.insert(latex_params,"-jobname="..input) + else + -- when user specifies -jobname, we must change name of the input file, + -- in order to be able to process correct dvi file with tex4ht and t4ht + local newinput + -- first contains quotation character or first character of the name + local first, rest = latex_cli_params:match("%-jobname%s*=?%s*(.)(.*)") + if first=='"' then + newinput=rest:match('([^"]+)') + elseif first=="'" then + newinput=rest:match("([^']+)") + elseif type(first)== "string" then + -- if the jobname is unquoted, it cannot contain space + -- join the first character and rest + rest = first.. rest + newinput = rest:match("([^ ]+)") + end + if newinput then + input = newinput + end + end + -- + table.insert(latex_params, latex_cli_params) + return latex_params, input +end + +-- use standard input instead of file if the filename is just `-` +-- return the filename and status if it is a tmp name +local function handle_input_file(filename) + -- return the original file name if it isn't just dash + if filename ~= "-" then return filename, false end + -- generate the temporary name. the added extension is important + local tmp_name = os.tmpname() + local contents = io.read("*all") + local f = io.open(tmp_name, "w") + f:write(contents) + f:close() + return tmp_name, true +end + local function process_args(args) local function get_inserter(args,tb) return function(key, value) @@ -98,6 +158,14 @@ local function process_args(args) end end + -- set error log level + logging.set_level(args.loglevel) + -- the default LaTeX --interaction parameter + local interaction = "batchmode" + if args.loglevel == "debug" then + interaction = "errorstopmode" + end + if args.version ==true then print(string.format("%s version %s", m.progname, m.version_number)) os.exit() @@ -122,36 +190,12 @@ local function process_args(args) local compiler = args.lua and "dvilualatex" or args.xetex and "xelatex --no-pdf" or "latex" - local tex_file = args.filename - local input = mkutils.remove_extension(args.filename) - local latex_params = {} + local tex_file, is_tmp_file = handle_input_file(args.filename) + local input = mkutils.remove_extension(tex_file) + -- the output file name can be influneced using -jobname parameter passed to the TeX engine + local latex_params, input = handle_jobname(input, args) local insert_latex = get_inserter(args,latex_params) insert_latex("shell-escape","-shell-escape") - local latex_cli_params = args[4] or "" - if not latex_cli_params:match("%-jobname") then - -- we must strip out directories from jobname when full path to document is given - input = input:match("([^%/^%\\]+)$") - -- input also cannot contain spaces, replace them with underscores - input = input:gsub("%s", "_") - table.insert(latex_params,"-jobname="..input) - else - -- when user specifies -jobname, we must change name of the input file, - -- in order to be able to process correct dvi file with tex4ht and t4ht - local newinput - local first, rest = latex_cli_params:match("%-jobname=(.)(.*)") - if first=='"' then - newinput=rest:match('([^"]+)') - elseif first=="'" then - newinput=rest:match("([^']+)") - elseif type(first)== "string" then - rest = first.. rest - newinput = rest:match("([^ ]+)") - end - if newinput then - input = newinput - end - end - table.insert(latex_params, latex_cli_params) --table.insert(latex_params,args["shell-escape"] and "-shell-escape") @@ -181,9 +225,9 @@ local function process_args(args) tex4ht = args[2] else tex4ht = args.utf8 and " -cmozhtf -utf8" or "" - if args.xetex then tex4ht = tex4ht .. " -.xdv" end - -- tex4ht = tex4ht .. xdv end + -- set the correct extension for tex4ht if xetex is used + if args.xetex then tex4ht = tex4ht .. " -.xdv" end local t4ht = args[3] or "" @@ -215,19 +259,21 @@ local function process_args(args) ,build_file = build_file ,output_format = outformat ,extensions = extensions + ,is_tmp_file = is_tmp_file + ,interaction = interaction --,t4ht_dir_format=t4ht_dir_format } if outdir then parameters.outdir = outdir end - print("Output dir: ",outdir) - print("Compiler: ", compiler) - print("Latex options: ", table.concat(latex_params," ")) - print("tex4ht.sty :",t4sty) - print("tex4ht",tex4ht) - print("build_file", build_file) + log:info("Output dir: "..outdir) + log:info("Compiler: "..compiler) + log:info("Latex options: ".. table.concat(latex_params," ")) + log:info("tex4ht.sty: "..t4sty) + log:info("tex4ht: "..tex4ht) + log:info("build_file: ".. build_file) if outformat~="nil" then - print("Output format", outformat) + log:info("Output format: ".. outformat) for _, ex in ipairs(extensions) do - print("Extension", ex.type .. ex.name) + log:info("Extension: ".. ex.type .. ex.name) end end return parameters diff --git a/Master/texmf-dist/scripts/make4ht/mkutils.lua b/Master/texmf-dist/scripts/make4ht/mkutils.lua index fd32c91e087..ba1cd6cbb9f 100755 --- a/Master/texmf-dist/scripts/make4ht/mkutils.lua +++ b/Master/texmf-dist/scripts/make4ht/mkutils.lua @@ -1,7 +1,10 @@ module(...,package.seeall) +local log = logging.new("mkutils") + local make4ht = require("make4ht-lib") local mkparams = require("mkparams") +local indexing = require("make4ht-indexing") --template engine function interp(s, tab) local tab = tab or {} @@ -61,11 +64,11 @@ end -- searching for converted images function parse_lg(filename) - print("Parse LG") + log:info("Parse LG") local outputimages,outputfiles,status={},{},nil local fonts, used_fonts = {},{} if not file_exists(filename) then - print("Cannot read log file: "..filename) + log:warning("Cannot read log file: "..filename) else local usedfiles={} for line in io.lines(filename) do @@ -114,7 +117,7 @@ local cp_func = os.type == "unix" and "cp" or "copy" function cp(src,dest) local command = string.format('%s "%s" "%s"', cp_func, src, dest) if cp_func == "copy" then command = command:gsub("/",'\\') end - print("Copy: "..command) + log:info("Copy: "..command) os.execute(command) end @@ -123,7 +126,7 @@ function mv(src, dest) local command = string.format('%s "%s" "%s"', mv_func, src, dest) -- fix windows paths if mv_func == "move" then command = command:gsub("/",'\\') end - print("Move: ".. command) + log:info("Move: ".. command) os.execute(command) end @@ -211,18 +214,32 @@ function copy(filename,outfilename) if not used_dir[path] then local to_create, msg = find_directories(parts) if not to_create then - print(msg) + log:warning(msg) return false end used_dir[path] = true local stat, msg = mkdirectories(to_create) - if not stat then print(msg) end + if not stat then log:warning(msg) end end lfs.chdir(currdir) cp(filename, path) return true end +function execute(command) + local f = io.popen(command, "r") + local output = f:read("*all") + -- rc will contain return codes of the executed command + local rc = {f:close()} + -- the status code is on the third position + -- https://stackoverflow.com/a/14031974/2467963 + local status = rc[3] + -- print the command line output only when requested through + -- log level + log:output(output) + return status, output +end + -- find the zip command function find_zip() if io.popen("zip -v","r"):close() then @@ -269,6 +286,7 @@ env.os = os env.io = io env.math = math env.unicode = unicode +env.logging = logging -- it is necessary to use the settings table @@ -317,57 +335,14 @@ env.Make = make4ht.Make env.Make.params = env.settings env.Make:add("test","test the variables: ${tex4ht_sty_par} ${htlatex} ${input} ${config}") --- this function reads the LaTeX log file and tries to detect fatal errors in the compilation -local function testlogfile(par) - local logfile = par.input .. ".log" - local f = io.open(logfile,"r") - if not f then - print("Make4ht: cannot open log file "..logfile) - return 1 - end - local len = f:seek("end") - -- test only the end of the log file, no need to run search functions on everything - local newlen = len - 1256 - -- but the value to seek must be greater than 0 - newlen = (newlen > 0) and newlen or 0 - f:seek("set", newlen) - local text = f:read("*a") - f:close() - if text:match("No pages of output") or text:match("TeX capacity exceeded, sorry") then return 1 end - return 0 -end - - --- Make this function available in the build files -Make.testlogfile = testlogfile ---env.Make:add("htlatex", "${htlatex} ${latex_par} '\\\makeatletter\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else\\expandafter\\Link\\fi}\\def\\Link#1.a.b.c.{\\g@addto@macro\\@documentclasshook{\\RequirePackage[#1,html]{tex4ht}\\let\\HCode\\documentstyle\\def\\documentstyle{\\let\\documentstyle\\HCode\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\def\\HCode####1{\\documentstyle[tex4ht,}\\@ifnextchar[{\\HCode}{\\documentstyle[tex4ht]}}}\\makeatother\\HCode '${config}${tex4ht_sty_par}'.a.b.c.\\input ' ${input}") - --- template for calling LaTeX with tex4ht loaded -Make.latex_command = "${htlatex} ${latex_par} '\\makeatletter".. -"\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode".. -"\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else".. -"\\expandafter\\Link\\fi}\\def\\Link#1.a.b.c.{\\g@addto@macro".. -"\\@documentclasshook{\\RequirePackage[#1,html]{tex4ht}${packages}}".. -"\\let\\HCode\\documentstyle\\def\\documentstyle{\\let\\documentstyle".. -"\\HCode\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\def".. -"\\HCode####1{\\documentstyle[tex4ht,}\\@ifnextchar[{\\HCode}{".. -"\\documentstyle[tex4ht]}}}\\makeatother\\HCode ${tex4ht_sty_par}.a.b.c.".. -"\\input \"\\detokenize{${tex_file}}\"'" - -env.Make:add("htlatex",function(par) - local command = Make.latex_command - if os.type == "windows" then - command = command:gsub("'",'') - end - command = command % par - print("LaTeX call: "..command) - os.execute(command) - return testlogfile(par) -end +local htlatex = require "make4ht-htlatex" +env.Make:add("htlatex", htlatex.htlatex ,{correct_exit=0}) env.Make:add("latexmk", function(par) + local settings = get_filter_settings "htlatex" or {} + par.interaction = par.interaction or settings.interaction or "batchmode" local command = Make.latex_command par.expanded = command % par -- quotes in latex_command must be escaped, they cause Latexmk error @@ -407,13 +382,13 @@ function load_config(settings, config_name) end local f = io.open(config_name,"r") if not f then - print("Cannot open config file", config_name) + log:info("Cannot open config file", config_name) return env end - print("Using build file", config_name) + log:info("Using build file", config_name) local code = f:read("*all") local fn, msg = run(code,env) - if not fn then print(msg) end + if not fn then log:warning(msg) end assert(fn) -- reload extensions from command line arguments for the "format" parameter for _,v in ipairs(saved_extensions) do @@ -423,25 +398,49 @@ function load_config(settings, config_name) end env.Make:add("xindy", function(par) - -- par.encoding = par.encoding or "utf8" - -- par.language = par.language or "english" - par.idxfile = par.idxfile or par.input .. ".idx" - local modules = par.modules or {par.input} + local xindylog = logging.new "xindy" + local settings = get_filter_settings "xindy" or {} + par.encoding = settings.encoding or par.encoding or "utf8" + par.language = settings.language or par.language or "english" + local modules = settings.modules or par.modules or {} local t = {} for k,v in ipairs(modules) do + xindylog:debug("Loading module: " ..v) t[#t+1] = "-M ".. v end par.moduleopt = table.concat(t, " ") - local xindy_call = "xindy -L ${language} -C ${encoding} ${moduleopt} ${idxfile}" % par - print(xindy_call) - return os.execute(xindy_call) -end, { language = "english", encoding = "utf8"}) + return indexing.run_indexing_command("texindy -L ${language} -C ${encoding} ${moduleopt} -o ${indfile} ${newidxfile}", par) +end, {}) + +env.Make:add("makeindex", function(par) + local makeindxcall = "makeindex ${options} -t ${ilgfile} -o ${indfile} ${newidxfile}" + local settings = get_filter_settings "makeindex" or {} + par.options = settings.options or par.options or "" + par.ilgfile = par.input .. ".ilg" + local status = indexing.run_indexing_command(makeindxcall, par) + return status +end, {}) + +env.Make:add("xindex", function(par) + local xindex_call = "xindex -l ${language} ${options} -o ${indfile} ${newidxfile}" + local settings = get_filter_settings "xindex" or {} + par.options = settings.options or par.options or "" + par.language = settings.language or par.language or "en" + local status = indexing.run_indexing_command(xindex_call, par) + return status +end, {}) + + local function find_lua_file(name) local extension_path = name:gsub("%.", "/") .. ".lua" return kpse.find_file(extension_path, "lua") end +-- for the BibLaTeX support +env.Make:add("biber", "biber ${input}") +env.Make:add("bibtex", "bibtex ${input}") + --- load the output format plugins function load_output_format(format_name) local format_library = "make4ht.formats."..format_name @@ -532,10 +531,10 @@ function load_extensions(extensions, format) if module_names[name] == true then local extension = load_extension(name,format) if extension then - print("Load extension", name) + log:info("Load extension", name) table.insert(extension_table, extension) else - print("Cannot load extension: ".. name) + log:warning("Cannot load extension: ".. name) end end end -- cgit v1.2.3