summaryrefslogtreecommitdiff
path: root/support/make4ht/domfilters/make4ht-inlinecss.lua
blob: 3240df275c58e0a8ac07086bed97e32022897164 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
local cssquery  = require "luaxml-cssquery"

local log = logging.new("inlinecss")

local cssrules = {}
local cssobj   = cssquery()

local function parse_rule(line)
  -- parse CSS selector and attributes
  -- they are always on one line in the CSS file produced by TeX4ht
  local selector, values = line:match("%s*(.-)%s*(%b{})")
  if values then
    values = values:sub(2,-2)
  end
  return selector, values
end

local function join_values(old, new)
  -- correctly joins two attribute lists, depending on the ending
  local separator = ";"
  if not old then return new end
  -- if old already ends with ;, then don't use semicolon as a separator
  if old:match(";%s*$") then separator = "" end
  return old .. separator .. new
end

local function parse_css(filename)
  local css_file = io.open(filename, "r")
  if not css_file then return nil, "cannot load css file: " .. (filename or "") end
  local newlines = {}
  for line in css_file:lines() do
    -- match lines that contain # or =, as these can be id or attribute selectors
    if line:match("[%#%=].-{") then
      -- update attributes for the current selector
      local selector, value = parse_rule(line)
      local oldvalue = cssrules[selector] 
      cssrules[selector] = join_values(oldvalue, value)
    else
      newlines[#newlines+1] = line
    end
  end
  -- we need to add css rules
  for selector, value in pairs(cssrules) do
    cssobj:add_selector(selector, function(dom) end, {value=value})
  end
  css_file:close()
  -- write new version of the CSS file, without rules for ids and attributes
  local css_file = io.open(filename, "w")
  css_file:write(table.concat(newlines, "\n"))
  css_file:close()
  return true
end

local processed = false

-- process the HTML file and insert inline CSS for id and attribute selectors
return function(dom, par)
  if not processed then 
    -- process the CSS file before everything else, but only once
    processed = true
    local css_file = mkutils.file_in_builddir(par.input .. ".css", par)
    local status, msg = parse_css(css_file)
    if not status then log:warning(msg) end
  end
  -- loop over all elements in the current page
  dom:traverse_elements(function(curr)
    -- use CSS object to match if the current element
    -- is matched by id attribute selector
    local matched = cssobj:match_querylist(curr)
    if #matched > 0 then
      -- join possible already existing style attribute with values from the CSS file
      local values = curr:get_attribute("style")
      -- join values of all matched rules
      for _,rule in ipairs(matched) do
        values = join_values(values, rule.params.value)
      end
      curr:set_attribute("style", values)
    end

  end)
  return dom
end