From 3479a92321ed7fe7e2133d3daec2d4f5fd53fbc6 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Sat, 2 Nov 2019 03:01:48 +0000 Subject: CTAN sync 201911020301 --- support/make4ht/domfilters/make4ht-aeneas.lua | 3 +- support/make4ht/domfilters/make4ht-collapsetoc.lua | 198 +++++++++++++++++++++ .../make4ht/domfilters/make4ht-joincharacters.lua | 8 +- support/make4ht/domfilters/make4ht-joincolors.lua | 3 +- .../make4ht/domfilters/make4ht-odtimagesize.lua | 3 +- support/make4ht/domfilters/make4ht-odtpartable.lua | 9 + support/make4ht/domfilters/make4ht-tablerows.lua | 58 ++++++ 7 files changed, 275 insertions(+), 7 deletions(-) create mode 100644 support/make4ht/domfilters/make4ht-collapsetoc.lua create mode 100644 support/make4ht/domfilters/make4ht-odtpartable.lua create mode 100644 support/make4ht/domfilters/make4ht-tablerows.lua (limited to 'support/make4ht/domfilters') diff --git a/support/make4ht/domfilters/make4ht-aeneas.lua b/support/make4ht/domfilters/make4ht-aeneas.lua index 0b93c95840..4a21c89332 100644 --- a/support/make4ht/domfilters/make4ht-aeneas.lua +++ b/support/make4ht/domfilters/make4ht-aeneas.lua @@ -5,6 +5,7 @@ -- local cssquery = require "luaxml-cssquery" local mkutils = require "mkutils" +local log = logging.new "aeneas" -- Table of CSS selectors to be skipped. local skip_elements = { "math", "svg"} @@ -111,7 +112,7 @@ local function aeneas(dom, par) then local idtitle idtitle, id = make_id(id, id_prefix) - print(el._name, first_child._text) + log:debug(el._name, first_child._text) el:set_attribute("id", idtitle) return el end diff --git a/support/make4ht/domfilters/make4ht-collapsetoc.lua b/support/make4ht/domfilters/make4ht-collapsetoc.lua new file mode 100644 index 0000000000..0b77b9dbbb --- /dev/null +++ b/support/make4ht/domfilters/make4ht-collapsetoc.lua @@ -0,0 +1,198 @@ +local domobject = require "luaxml-domobject" + +local filter = require "make4ht-filter" +local log = logging.new "collapsetoc" + + +local toc_levels = {"partToc", "chapterToc", "sectionToc", "subsectionToc", "subsubsectionToc"} + +local debug_print = function(...) log:debug(...) end + + +-- return toc element type and it's id +local function get_id(el) + local name = el:get_attribute "class" + local id + local a = el:query_selector "a" or {} + local first = a[1] + if first then + local href = first:get_attribute "href" + id = href:match("#(.+)$") + end + return name, id +end + +local function remove_sections(part_elements, currentpart) + -- we need to remove toc entries from the previous part if the + -- current document isn't part of it + if currentpart == false then + for _, part in ipairs(part_elements) do + part:remove_node() + end + end +end + +local function make_toc_selector(toc_levels) + local level_classes = {} + for _, l in ipairs(toc_levels) do + level_classes[#level_classes+1] = "." .. l + end + return table.concat(level_classes, ", ") +end + +local function find_toc_levels(toc) + -- find toc levels used in the document + -- it ecpects that sectioning levels appears in the TOC in the descending order + local levels, used = {}, {} + local level = 1 + -- we still expect the standard class names + local toc_selector = make_toc_selector(toc_levels) + for _, el in ipairs(toc:query_selector(toc_selector)) do + local class = el:get_attribute("class") + if not used[class] then + table.insert(levels, class) + used[class] = level + level = level + 1 + end + end + return levels, used +end + +local function remove_levels(toc, matched_ids) + -- sort the matched sections according to their levels + local levels, level_numbers = find_toc_levels(toc) + debug_print("remove levels", #levels) + -- for _, level in ipairs(levels) do + -- print(level, level_numbers[level], matched_ids[level]) + -- end + local keep_branch = false + local matched_levels = {} + local toc_selector = make_toc_selector(toc_levels) + for _, el in ipairs(toc:query_selector(toc_selector)) do + local name, id = get_id(el) + -- get the current toc hiearchy level + local level = level_numbers[name] + -- get the matched id for the current level + local level_id = matched_ids[name] + local matched = level_id == id + local remove = true + -- we will use this for toc elements at lower hiearchy than is the top sectioning level on the page + if matched then keep_branch = true end + -- find the parent level to the current section level + local parent_level = toc_levels[level - 1] + local parent_matched = matched_levels[parent_level] + if matched then + debug_print("match",name, id, level_id, level, #levels) + keep_branch = true + remove = false + elseif level==1 then + -- don't remove the top level + debug_print("part",name, id, level_id, level) + remove = false + matched_levels = {} + if not matched then keep_branch = false end + elseif keep_branch then + -- if level >= (#levels - 1) then + if level > matched_ids._levels then + debug_print("level",name, id, level_id, level, parent_level, parent_matched) + remove = false + elseif matched_ids.ids[id] then + debug_print("matched id",name, id, level_id, level, parent_level, parent_matched) + remove = false + elseif parent_matched then + debug_print("parent_matched",name, id, level_id, level, parent_level, parent_matched) + keep_branch = false + remove = false + end + -- else + -- print("remove", name, id, level_id, level, #matched_ids) + -- end + elseif parent_matched then + debug_print("parent_matched alternative",name, id, level_id, level, parent_level, parent_matched) + remove = false + else + debug_print("else",name, id, level_id, level, keep_branch) + keep_branch = false + end + matched_levels[name] = matched + if remove then + el:remove_node() + --print(name,id, level_id, matched) + end + end + +end + + + + + +-- local process = filter{ function(s) + -- local dom = domobject.parse(s) +local function collapse_toc(dom, par) + -- set options + local options = get_filter_settings "collapsetoc" + local toc_query = par.toc_query or options.toc_query or ".tableofcontents" + local title_query = par.title_query or options.title_query or ".partHead a, .chapterHead a, .sectionHead a, .subsectionHead a" + toc_levels = par.toc_levels or options.toc_levels or toc_levels + -- keep track of current id of each sectioning level + local current_ids, matched_ids = {}, {_levels = 0, ids = {}} + -- search sectioning elements + local titles = dom:query_selector(title_query) + local section_ids = {} + for _, x in ipairs(titles) do + -- get their id attributes and save them in a table + section_ids[#section_ids+1] = x:get_attribute("id") + end + + -- we need to retrieve the first table of contents + local toctables = dom:query_selector(toc_query) or {} + -- process only when we got a TOC + debug_print("toc query", toc_query, #toctables) + if #toctables > 0 then + local tableofcontents = toctables[1] + -- all toc entries are in span elements + local toc = tableofcontents:query_selector("span") + local currentpart = false + local part_elements = {} + for _, el in ipairs(toc) do + -- get sectioning level and id of the current TOC entry + local name, id = get_id(el) + -- set the id of the current sectioning level + current_ids[name] = id + for _, sectid in ipairs(section_ids) do + -- detect if the current TOC entry match some sectioning element in the current document + if id == sectid then + currentpart = true + -- save the current id as a matched id + matched_ids.ids[id] = true + -- copy the TOC hiearchy for the current toc level + for i, level in ipairs(toc_levels) do + -- print("xxx",i, level, current_ids[level]) + matched_ids[level] = current_ids[level] + -- set the maximum matched level + if i > matched_ids._levels then matched_ids._levels = i end + if level == name then break end + end + debug_print("match", id) + end + end + end + remove_levels(tableofcontents, matched_ids) + + -- remove sections from the last part + -- remove_sections(part_elements,currentpart) + -- remove unneeded br elements + local br = tableofcontents:query_selector("br") + for _, el in ipairs(br) do el:remove_node() end + -- remove unneded whitespace + for _, el in ipairs(tableofcontents:get_children()) do + if el:is_text() then el:remove_node() end + end + end + return dom +end + +return collapse_toc + +-- Make:match("html$", process) diff --git a/support/make4ht/domfilters/make4ht-joincharacters.lua b/support/make4ht/domfilters/make4ht-joincharacters.lua index 99d3058d7d..30ed6e1382 100644 --- a/support/make4ht/domfilters/make4ht-joincharacters.lua +++ b/support/make4ht/domfilters/make4ht-joincharacters.lua @@ -1,4 +1,4 @@ -local charclases = { +local charclasses = { span=true, mn = true, } @@ -8,7 +8,7 @@ local function join_characters(obj,par) -- tex4ht to just one object. local par = par or {} local options = get_filter_settings "joincharacters" - local charclases = options.charclases or par.charclases or charclases + local charclasses = options.charclasses or par.charclasses or charclasses obj:traverse_elements(function(el) local get_name = function(curr) @@ -18,7 +18,7 @@ local function join_characters(obj,par) return next_el:get_attribute("class") end local is_span = function(next_el) - return charclases[get_name(next_el)] + return charclasses[get_name(next_el)] end local function get_next(curr, class) @@ -33,7 +33,7 @@ local function join_characters(obj,par) end end -- loop over all elements and test if the current element is in a list of - -- processed elements (charclases) + -- processed elements (charclasses) if is_span(el) then local next_el = get_next(el) -- loop over the following elements and test whether they are of the same type diff --git a/support/make4ht/domfilters/make4ht-joincolors.lua b/support/make4ht/domfilters/make4ht-joincolors.lua index 94a3a054e6..730f66a7a0 100644 --- a/support/make4ht/domfilters/make4ht-joincolors.lua +++ b/support/make4ht/domfilters/make4ht-joincolors.lua @@ -1,4 +1,5 @@ local cssfiles = {} +local log = logging.new "joincolors" -- keep mapping between span ids and colors @@ -45,7 +46,7 @@ local function process_css_files(dom) for _, el in ipairs(dom:query_selector("link")) do local href = el:get_attribute("href") or "" if not cssfiles[href] and href:match("css$") then - print("Load CSS file ", href) + log:debug("Load CSS file ", href) cssfiles[href] = true process_css(href) end diff --git a/support/make4ht/domfilters/make4ht-odtimagesize.lua b/support/make4ht/domfilters/make4ht-odtimagesize.lua index 4ce8f0d92e..2b87372fdb 100644 --- a/support/make4ht/domfilters/make4ht-odtimagesize.lua +++ b/support/make4ht/domfilters/make4ht-odtimagesize.lua @@ -1,3 +1,4 @@ +local log = logging.new "odtimagesize" -- set correct dimensions to frames around images return function(dom) local frames = dom:query_selector("draw|frame") @@ -9,7 +10,7 @@ return function(dom) local height = image:get_attribute("svg:height") if widht then frame:set_attribute("svg:width", width) end if height then frame:set_attribute("svg:height", height) end - print("image dimensions", width, height) + log:debug("image dimensions", width, height) end end return dom diff --git a/support/make4ht/domfilters/make4ht-odtpartable.lua b/support/make4ht/domfilters/make4ht-odtpartable.lua new file mode 100644 index 0000000000..f4e84b55d9 --- /dev/null +++ b/support/make4ht/domfilters/make4ht-odtpartable.lua @@ -0,0 +1,9 @@ +-- find all tables inside paragraphs, replace the found paragraphs with the child table +return function(dom) + for _,table in ipairs(dom:query_selector("text|p table|table")) do + -- replace the paragraph by its child element + local parent = table:get_parent() + parent:replace_node(table) + end + return dom +end diff --git a/support/make4ht/domfilters/make4ht-tablerows.lua b/support/make4ht/domfilters/make4ht-tablerows.lua new file mode 100644 index 0000000000..155c24529a --- /dev/null +++ b/support/make4ht/domfilters/make4ht-tablerows.lua @@ -0,0 +1,58 @@ +local log = logging.new ("tablerows") +return function(dom) + local is_empty_row = function(row) + local not_empty = false + local element_count = 0 + -- ignore hline rows + local row_class = row:get_attribute("class") + if row_class == "hline" or row_class == "cline" then return false end + -- detect if the row contain only one empty child + for _,child in ipairs(row:get_children() or {}) do + if child:is_element() then + element_count = element_count + 1 + -- empty rows contain only one element, it is not empty otherwise + if element_count > 1 then return false end + -- detect if it contains only whitespace + not_empty = child:get_text():gsub("%s","") ~= "" or not_empty + end + end + -- print("element count", element_count, not_empty) + return element_count == 1 and not_empty == false + end + local is_not_styled = function(row, css) + -- get the id attribute and escape it, so it can be used in regexp + local id = row:get_attribute("id") + if not id then return true end -- no styling without id + local search_term = "%#" .. id:gsub("%-", "%%-") + -- if the CSS file contains the row id ( elements can also have id + -- that matches this pattern, so we should keep the row if we match them too) + return not css:match(search_term) + end + local load_css_files = function() + -- the empty rows can be styled using CSS, for example configuration for + -- Booktabs does that. We shouldn't remove such rows. + local cssfiles = {} + for _, link in ipairs(dom:query_selector("head link")) do + local src = link:get_attribute("href") + if src then + local f = io.open(src, "r") + if f then + local contents = f:read("*all") + f:close() + table.insert(cssfiles, contents) + end + end + end + return table.concat(cssfiles, "\n") + end + local css = load_css_files() + for _, tbl in ipairs(dom:query_selector("table")) do + -- find the empty rows + for _, row in ipairs(tbl:query_selector("tr")) do + if is_empty_row(row) and is_not_styled(row, css) then row:remove_node() end + end + + end + return dom +end + -- cgit v1.2.3