From e1192611f0655a1ccaff0dff2f53c7c65fa5db07 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Fri, 24 Jan 2020 03:00:54 +0000 Subject: CTAN sync 202001240300 --- support/make4ht/domfilters/make4ht-booktabs.lua | 82 +++++++++++++++ .../make4ht/domfilters/make4ht-joincharacters.lua | 113 +++++++++++++++------ support/make4ht/domfilters/make4ht-tablerows.lua | 14 ++- 3 files changed, 179 insertions(+), 30 deletions(-) create mode 100644 support/make4ht/domfilters/make4ht-booktabs.lua (limited to 'support/make4ht/domfilters') 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 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 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 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 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 -- cgit v1.2.3