summaryrefslogtreecommitdiff
path: root/support/make4ht/formats/make4ht-jats.lua
blob: 41eabf874490090f6f4333aa556437d75481fa73 (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
local M = {}
local xtpipeslib = require "make4ht-xtpipes"
local domfilter = require "make4ht-domfilter"


-- some elements need to be moved from the document flow to the document meta
local article_meta 
local elements_to_move_to_meta = {}
local function move_to_meta(el)
  -- we don't move elements immediatelly, because it would prevent them from further 
  -- processing in the filter. so we save them in an array, and move them once 
  -- the full DOM was processed
  table.insert(elements_to_move_to_meta, el)
end

local elements_to_move_to_title = {}
local function move_to_title_group(el)
  -- there can be only one title and subtitle
  local name = el:get_element_name()
  if not elements_to_move_to_title[name] then
    elements_to_move_to_title[name] = el
  end
end

local elements_to_move_to_contribs = {}
local function move_to_contribs(el)
  table.insert(elements_to_move_to_contribs, el)
end



local function process_moves()
  if article_meta then
    if elements_to_move_to_title["article-title"] 
      and #article_meta:query_selector("title_group") == 0 then -- don't move anything if user added title_group from a config file
      local title_group = article_meta:create_element("title_group")
      for _, name in ipairs{ "article-title", "subtitle" } do
        local v = elements_to_move_to_title[name] 
        if v then
          title_group:add_child_node(v:copy_node())
          v:remove_node()
        end
      end
      article_meta:add_child_node(title_group)
    end
    if #elements_to_move_to_contribs > 0 then
      local contrib_group = article_meta:create_element("contrib-group")
      for _, el in ipairs(elements_to_move_to_contribs) do
        contrib_group:add_child_node(el:copy_node())
        el:remove_node()
      end
      article_meta:add_child_node(contrib_group)
    end
    for _, el in ipairs(elements_to_move_to_meta) do
      -- move elemnt's copy, and remove the original
      article_meta:add_child_node(el:copy_node())
      el:remove_node()
    end
  end
end

local function has_no_text(el)
  -- detect if element contains only whitespace
  if el:get_text():match("^%s*$") then
    --- if it contains any elements, it has text
    for _, child in ipairs(el:get_children()) do
      if child:is_element() then return false end
    end
    return true
  end
  return false
end

local function is_xref_id(el)
  return el:get_element_name() == "xref" and el:get_attribute("id") and el:get_attribute("rid") == nil and has_no_text(el)
end
-- set id to parent element for <xref> that contain only id
local function xref_to_id(el)
  local parent = el:get_parent()
  -- set id only if it doesn't exist yet
  if parent:get_attribute("id") == nil then
    parent:set_attribute("id", el:get_attribute("id"))
    el:remove_node()
  end
end

local function make_text(el)
  local text = el:get_text():gsub("^%s*", ""):gsub("%s*$", "")
  local text_el = el:create_text_node(text)
  el._children = {text_el}
end

local function is_empty_par(el)
  return el:get_element_name() == "p" and has_no_text(el)
end

local function handle_links(el, params)
  -- we must distinguish between internal links in the document, and external links
  -- to websites etc. these needs to be changed to the <ext-link> element.
  local link = el:get_attribute("rid") 
  if link then
    -- try to remove \jobname.xml from the beginning of the link
    -- if the rest starts with #, then it is an internal link
    local local_link = link:gsub("^" .. params.input .. ".xml", "")
    if local_link:match("^%#") then
      el:set_attribute("rid", local_link)
    else
      -- change element to ext-link for extenal links
      el._name = "ext-link"
      el:set_attribute("rid", nil)
      el:set_attribute("xlink:href", link)
    end
  end
end

local function handle_maketitle(el)
  -- <maketitle> is special element produced by TeX4ht from LaTeX's \maketitle
  -- we need to pick interesting info from there, and move it to the header
  local function is_empty(selector)
    return #article_meta:query_selector(selector) == 0
  end
  -- move <aff> to <contrib>
  local affiliations = {}
  for _, aff in ipairs(el:query_selector("aff")) do
    local id = aff:get_attribute("id") 
    if id then 
      for _,mark in ipairs(aff:query_selector("affmark")) do mark:remove_node() end
      affiliations[id] = aff:copy_node() 
    end
  end
  if is_empty("contrib") then
    for _, contrib in ipairs(el:query_selector("contrib")) do
      for _, affref in ipairs(contrib:query_selector("affref")) do
        local id = affref:get_attribute("rid") or ""
        -- we no longer need this node
        affref:remove_node()
        local linked_affiliation = affiliations[id]
        if linked_affiliation then
          contrib:add_child_node(linked_affiliation)
        end
      end
      for _, string_name in ipairs(contrib:query_selector("string-name")) do
        make_text(string_name)
      end
      move_to_contribs(contrib:copy_node())
      -- we need to remove it from here, even though we remove <maketitle> later
      -- we got doubgle contributors without that
      contrib:remove_node()
    end
  end
  if is_empty("pub-date") then
    for _, date in ipairs(el:query_selector("date")) do
      date._name = "pub-date"
      for _, s in ipairs(date:query_selector("string-date")) do
        make_text(s)
      end
      move_to_meta(date:copy_node())
    end
  end
  el:remove_node()
end





function M.prepare_parameters(settings, extensions)
  settings.tex4ht_sty_par = settings.tex4ht_sty_par ..",jats"
  settings = mkutils.extensions_prepare_parameters(extensions, settings)
  return settings
end

function M.prepare_extensions(extensions)
  return extensions
end

function M.modify_build(make)
  filter_settings("joincharacters", {charclasses = {italic=true, bold=true}})

  local process =  domfilter {
    function(dom, params)
      dom:traverse_elements(function(el)
        -- some elements need special treatment
        local el_name = el:get_element_name()
        if is_xref_id(el) then
          xref_to_id(el)
        elseif el_name == "article-meta" then
          -- save article-meta element for further processig
          article_meta = el
        elseif el_name == "article-title" then
          move_to_title_group(el)
        elseif el_name == "subtitle" then
          move_to_title_group(el)
        elseif el_name == "abstract" then
          move_to_meta(el)
        elseif el_name == "string-name" then
          make_text(el)
        elseif el_name == "contrib" then
          move_to_contribs(el)
        elseif is_empty_par(el) then
          -- remove empty paragraphs
          el:remove_node()
        elseif el_name == "xref" then
          handle_links(el, params)
        elseif el_name == "maketitle" then
          handle_maketitle(el)
        elseif el_name == "div" and el:get_attribute("class") == "maketitle" then
          el:remove_node()
        end

      end)
      -- move elements that are marked for move
      process_moves()
      return dom
    end, "joincharacters","mathmlfixes", "tablerows","booktabs"
  }
  local charclasses = {["mml:mi"] = true, ["mml:mn"] = true , italic = true, bold=true, roman = true, ["mml:mtext"] = true, mi=true, mn=true}
  make:match("xml$", process, {charclasses = charclasses})
  return make
end

return M