isodoc 2.10.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/isodoc.gemspec +9 -4
  4. data/lib/isodoc/base_style/all.css +96 -0
  5. data/lib/isodoc/base_style/reset.css +96 -0
  6. data/lib/isodoc/base_style/reset.scss +75 -0
  7. data/lib/isodoc/base_style/scripts.html +45 -14
  8. data/lib/isodoc/class_utils.rb +4 -2
  9. data/lib/isodoc/convert.rb +0 -21
  10. data/lib/isodoc/css.rb +2 -4
  11. data/lib/isodoc/function/blocks.rb +37 -52
  12. data/lib/isodoc/function/blocks_example_note.rb +75 -29
  13. data/lib/isodoc/function/cleanup.rb +9 -44
  14. data/lib/isodoc/function/inline.rb +67 -13
  15. data/lib/isodoc/function/lists.rb +20 -14
  16. data/lib/isodoc/function/references.rb +9 -126
  17. data/lib/isodoc/function/reqt.rb +4 -3
  18. data/lib/isodoc/function/section.rb +30 -35
  19. data/lib/isodoc/function/section_titles.rb +22 -11
  20. data/lib/isodoc/function/table.rb +3 -3
  21. data/lib/isodoc/function/terms.rb +49 -26
  22. data/lib/isodoc/function/to_word_html.rb +46 -16
  23. data/lib/isodoc/function/utils.rb +11 -12
  24. data/lib/isodoc/gem_tasks.rb +1 -1
  25. data/lib/isodoc/html_function/footnotes.rb +2 -1
  26. data/lib/isodoc/html_function/html.rb +5 -7
  27. data/lib/isodoc/html_function/postprocess.rb +25 -6
  28. data/lib/isodoc/html_function/postprocess_cover.rb +13 -7
  29. data/lib/isodoc/html_function/postprocess_footnotes.rb +52 -47
  30. data/lib/isodoc/init.rb +31 -0
  31. data/lib/isodoc/metadata.rb +20 -13
  32. data/lib/isodoc/metadata_contributor.rb +5 -10
  33. data/lib/isodoc/metadata_date.rb +1 -1
  34. data/lib/isodoc/presentation_function/autonum.rb +153 -0
  35. data/lib/isodoc/presentation_function/bibdata.rb +4 -5
  36. data/lib/isodoc/presentation_function/block.rb +107 -40
  37. data/lib/isodoc/presentation_function/concepts.rb +184 -19
  38. data/lib/isodoc/presentation_function/docid.rb +78 -0
  39. data/lib/isodoc/presentation_function/erefs.rb +55 -31
  40. data/lib/isodoc/presentation_function/image.rb +64 -19
  41. data/lib/isodoc/presentation_function/index.rb +180 -0
  42. data/lib/isodoc/presentation_function/inline.rb +47 -16
  43. data/lib/isodoc/presentation_function/math.rb +128 -71
  44. data/lib/isodoc/presentation_function/metadata.rb +29 -66
  45. data/lib/isodoc/presentation_function/refs.rb +75 -28
  46. data/lib/isodoc/presentation_function/reqt.rb +7 -4
  47. data/lib/isodoc/presentation_function/section.rb +51 -129
  48. data/lib/isodoc/presentation_function/sourcecode.rb +52 -19
  49. data/lib/isodoc/presentation_function/terms.rb +107 -118
  50. data/lib/isodoc/presentation_function/title.rb +107 -0
  51. data/lib/isodoc/presentation_function/xrefs.rb +37 -17
  52. data/lib/isodoc/presentation_xml_convert.rb +24 -10
  53. data/lib/isodoc/version.rb +1 -1
  54. data/lib/isodoc/word_function/body.rb +9 -37
  55. data/lib/isodoc/word_function/comments.rb +0 -1
  56. data/lib/isodoc/word_function/footnotes.rb +1 -1
  57. data/lib/isodoc/word_function/lists.rb +13 -9
  58. data/lib/isodoc/word_function/postprocess.rb +6 -0
  59. data/lib/isodoc/word_function/postprocess_cover.rb +6 -4
  60. data/lib/isodoc/word_function/postprocess_table.rb +1 -1
  61. data/lib/isodoc/word_function/postprocess_toc.rb +10 -6
  62. data/lib/isodoc/word_function/table.rb +2 -2
  63. data/lib/isodoc/xref/clause_order.rb +41 -0
  64. data/lib/isodoc/xref/xref_anchor.rb +31 -16
  65. data/lib/isodoc/xref/xref_counter.rb +18 -3
  66. data/lib/isodoc/xref/xref_counter_types.rb +14 -0
  67. data/lib/isodoc/xref/xref_gen.rb +61 -37
  68. data/lib/isodoc/xref/xref_gen_seq.rb +148 -81
  69. data/lib/isodoc/xref/xref_sect_gen.rb +84 -88
  70. data/lib/isodoc/xref/xref_util.rb +53 -4
  71. data/lib/isodoc/xref.rb +29 -10
  72. data/lib/isodoc/xslfo_convert.rb +14 -9
  73. data/lib/isodoc-yaml/i18n-ar.yaml +17 -13
  74. data/lib/isodoc-yaml/i18n-de.yaml +17 -13
  75. data/lib/isodoc-yaml/i18n-en.yaml +18 -17
  76. data/lib/isodoc-yaml/i18n-es.yaml +17 -13
  77. data/lib/isodoc-yaml/i18n-fr.yaml +17 -13
  78. data/lib/isodoc-yaml/i18n-ja.yaml +56 -52
  79. data/lib/isodoc-yaml/i18n-ru.yaml +17 -16
  80. data/lib/isodoc-yaml/i18n-zh-Hans.yaml +17 -13
  81. data/lib/nokogiri/xml/node.rb +14 -0
  82. data/lib/relaton/render-isodoc/config.yml +1 -1
  83. metadata +90 -13
@@ -0,0 +1,78 @@
1
+ module IsoDoc
2
+ class PresentationXMLConvert < ::IsoDoc::Convert
3
+ def docid_prefixes(docxml)
4
+ docxml.xpath(ns("//references/bibitem/docidentifier")).each do |i|
5
+ i.children = docid_prefix(i["type"], to_xml(i.children))
6
+ end
7
+ end
8
+
9
+ def pref_ref_code(bib)
10
+ bib["suppress_identifier"] == "true" and return nil
11
+ ret = bib.xpath(ns("./docidentifier[@scope = 'biblio-tag']"))
12
+ ret.empty? or return ret.map { |x| to_xml(x.children) }
13
+ ret = pref_ref_code_parse(bib) or return nil
14
+ ins = bib.at(ns("./docidentifier[last()]"))
15
+ ret.reverse_each do |r|
16
+ ins.next = "<docidentifier scope='biblio-tag'>#{docid_l10n(r)}</docidentifier>"
17
+ end
18
+ ret
19
+ end
20
+
21
+ def pref_ref_code_parse(bib)
22
+ data, = @bibrender.parse(bib)
23
+ ret = data[:authoritative_identifier] or return nil
24
+ ret.empty? and return nil
25
+ ret
26
+ end
27
+
28
+ # returns [metanorma, non-metanorma, DOI/ISSN/ISBN] identifiers
29
+ def bibitem_ref_code(bib)
30
+ id, id1, id2, id3 = bibitem_ref_code_prep(bib)
31
+ id || id1 || id2 || id3 and return [id, id1, id2, id3]
32
+ bib["suppress_identifier"] == "true" and return [nil, nil, nil, nil]
33
+ [nil, no_identifier(bib), nil, nil]
34
+ end
35
+
36
+ def bibitem_ref_code_prep(bib)
37
+ id = bib.at(ns("./docidentifier[@type = 'metanorma']"))
38
+ id1 = pref_ref_code(bib)
39
+ id2 = bib.at(ns("./docidentifier[#{SKIP_DOCID}]"))
40
+ id3 = bib.at(ns("./docidentifier[@type = 'metanorma-ordinal']"))
41
+ [id, id1, id2, id3]
42
+ end
43
+
44
+ def no_identifier(bib)
45
+ @i18n.no_identifier or return nil
46
+ id = Nokogiri::XML::Node.new("docidentifier", bib.document)
47
+ id << @i18n.no_identifier
48
+ id
49
+ end
50
+
51
+ def bracket_if_num(num)
52
+ num.nil? and return nil
53
+ num = to_xml(num.children).sub(/^\[/, "").sub(/\]$/, "")
54
+ /^\d+$/.match?(num) and return "[#{num}]"
55
+ num
56
+ end
57
+
58
+ def unbracket1(ident)
59
+ ident.nil? and return nil
60
+ ident.is_a?(String) or ident = to_xml(ident.children)
61
+ ident.sub(/^\[/, "").sub(/\]$/, "")
62
+ end
63
+
64
+ def unbracket(ident)
65
+ if ident.respond_to?(:size)
66
+ ident.map { |x| unbracket1(x) }.join("&#xA0;/ ")
67
+ else unbracket1(ident)
68
+ end
69
+ end
70
+
71
+ def render_identifier(ident)
72
+ { metanorma: bracket_if_num(ident[0]),
73
+ sdo: unbracket(ident[1]),
74
+ doi: unbracket(ident[2]),
75
+ ordinal: bracket_if_num(ident[3]) }
76
+ end
77
+ end
78
+ end
@@ -3,10 +3,24 @@ require "metanorma-utils"
3
3
  module IsoDoc
4
4
  class PresentationXMLConvert < ::IsoDoc::Convert
5
5
  def citeas(xmldoc)
6
- xmldoc.xpath(ns("//eref | //origin | //quote/source")).each do |e|
6
+ xmldoc.xpath(ns("//fmt-eref | //fmt-origin | //fmt-link"))
7
+ .each do |e|
8
+ sem_xml_descendant?(e) and next
7
9
  e["bibitemid"] && e["citeas"] or next
8
- a = @xrefs.anchor(e["bibitemid"], :xref, false) and e["citeas"] = a
10
+ a = @xrefs.anchor(e["bibitemid"], :xref, false) or next
11
+ e["citeas"] = citeas_cleanup(a)
12
+ # link generated in collection postprocessing from eref
13
+ e.name == "fmt-link" && e.text.empty? and e.children = e["citeas"]
14
+ end
15
+ end
16
+
17
+ def citeas_cleanup(ref)
18
+ if /</.match?(ref)
19
+ xml = Nokogiri::XML("<root>#{ref}</root>")
20
+ xml.xpath("//semx").each { |x| x.replace(x.children) }
21
+ ref = to_xml(xml.at("//root").children)
9
22
  end
23
+ ref
10
24
  end
11
25
 
12
26
  def expand_citeas(text)
@@ -15,11 +29,13 @@ module IsoDoc
15
29
  end
16
30
 
17
31
  def erefstack1(elem)
18
- locs = elem.xpath(ns("./eref")).map do |e|
19
- [e["connective"], to_xml(e)]
32
+ locs = elem.xpath(ns("./semx/fmt-eref")).map do |e|
33
+ [e["connective"], to_xml(e.parent.remove)]
20
34
  end.flatten
21
35
  ret = resolve_eref_connectives(locs)
22
- elem.replace(ret[1])
36
+ elem["id"] ||= "_#{UUIDTools::UUID.random_create}"
37
+ elem.next = "<semx element='erefstack' source='#{elem['id']}'>#{ret[1]}</semx>"
38
+ #elem.replace(ret[1])
23
39
  end
24
40
 
25
41
  def eref_localities(refs, target, node)
@@ -38,9 +54,9 @@ module IsoDoc
38
54
  ret = resolve_eref_connectives(eref_locality_stacks(refs, target,
39
55
  node))
40
56
  node.delete("droploc") unless droploc
41
- eref_localities1({ target: target, number: "pl",
57
+ eref_localities1({ target:, number: "pl",
42
58
  type: refs.first.at(ns("./locality/@type")).text,
43
- from: l10n(ret[1..-1].join), node: node, lang: @lang })
59
+ from: l10n(ret[1..].join), node:, lang: @lang })
44
60
  end
45
61
 
46
62
  def can_conflate_eref_rendering?(refs)
@@ -48,16 +64,13 @@ module IsoDoc
48
64
  refs.all? { |r| r.name == "localityStack" } &&
49
65
  refs.all? { |r| r.xpath(ns("./locality")).size == 1 }) or return false
50
66
  first = refs.first.at(ns("./locality/@type")).text
51
- refs.all? do |r|
52
- r.at(ns("./locality/@type")).text == first
53
- end
67
+ refs.all? { |r| r.at(ns("./locality/@type")).text == first }
54
68
  end
55
69
 
56
70
  def resolve_eref_connectives(locs)
57
71
  locs = resolve_comma_connectives(locs)
58
72
  locs = resolve_to_connectives(locs)
59
- return locs if locs.size < 3
60
-
73
+ locs.size < 3 and return locs
61
74
  locs = locs.each_slice(2).with_object([]) do |a, m|
62
75
  m << { conn: a[0], label: a[1] }
63
76
  end
@@ -90,7 +103,8 @@ module IsoDoc
90
103
  locs1 = []
91
104
  until locs.empty?
92
105
  if locs[1] == "to"
93
- locs1 << @i18n.chain_to.sub("%1", locs[0]).sub("%2", locs[2])
106
+ locs1 << connectives_spans(@i18n.chain_to.sub("%1", locs[0])
107
+ .sub("%2", locs[2]))
94
108
  locs.shift(3)
95
109
  else locs1 << locs.shift
96
110
  end
@@ -143,14 +157,13 @@ module IsoDoc
143
157
  def eref_localities0(ref, _idx, target, node)
144
158
  if ref["type"] == "whole" then @i18n.wholeoftext
145
159
  else
146
- eref_localities1({ target: target, type: ref["type"], number: "sg",
160
+ eref_localities1({ target:, type: ref["type"], number: "sg",
147
161
  from: ref.at(ns("./referenceFrom"))&.text,
148
- upto: ref.at(ns("./referenceTo"))&.text, node: node,
162
+ upto: ref.at(ns("./referenceTo"))&.text, node:,
149
163
  lang: @lang })
150
164
  end
151
165
  end
152
166
 
153
- # def eref_localities1_zh(_target, type, from, upto, node)
154
167
  def eref_localities1_zh(opt)
155
168
  ret = "第#{opt[:from]}" if opt[:from]
156
169
  ret += "&#x2013;#{opt[:upto]}" if opt[:upto]
@@ -159,11 +172,9 @@ module IsoDoc
159
172
  ret
160
173
  end
161
174
 
162
- # def eref_localities1(target, type, from, upto, node, lang = "en")
163
175
  def eref_localities1(opt)
164
176
  opt[:type] == "anchor" and return nil
165
177
  opt[:lang] == "zh" and
166
- # return l10n(eref_localities1_zh(target, type, from, upto, node))
167
178
  return l10n(eref_localities1_zh(opt))
168
179
  ret = eref_locality_populate(opt[:type], opt[:node], opt[:number])
169
180
  ret += " #{opt[:from]}" if opt[:from]
@@ -184,18 +195,20 @@ module IsoDoc
184
195
  end
185
196
 
186
197
  def eref2link(docxml)
187
- docxml.xpath(ns("//eref | //origin[not(termref)] | //quote/source"))
198
+ docxml.xpath(ns("//display-text")).each { |f| f.replace(f.children) }
199
+ docxml.xpath(ns("//fmt-eref | //fmt-origin[not(.//termref)]"))
188
200
  .each do |e|
201
+ sem_xml_descendant?(e) and next
189
202
  href = eref_target(e) or next
190
203
  e.xpath(ns("./locality | ./localityStack")).each(&:remove)
191
- if /^#/.match?(href) then eref2xref(e)
204
+ if href[:type] == :anchor then eref2xref(e)
192
205
  else eref2link1(e, href)
193
206
  end
194
207
  end
195
208
  end
196
209
 
197
210
  def eref2xref(node)
198
- node.name = "xref"
211
+ node.name = "fmt-xref"
199
212
  node["target"] = node["bibitemid"]
200
213
  node.delete("bibitemid")
201
214
  node.delete("citeas")
@@ -203,7 +216,9 @@ module IsoDoc
203
216
  end
204
217
 
205
218
  def eref2link1(node, href)
206
- repl = "<link target='#{href}'>#{node.children}</link>"
219
+ url = href[:link]
220
+ att = href[:type] == :attachment ? "attachment='true'" : ""
221
+ repl = "<fmt-link #{att} target='#{url}'>#{node.children}</link>"
207
222
  node["type"] == "footnote" and repl = "<sup>#{repl}</sup>"
208
223
  node.replace(repl)
209
224
  end
@@ -215,20 +230,29 @@ module IsoDoc
215
230
  end
216
231
 
217
232
  def eref_target(node)
218
- url = suffix_url(eref_url(node["bibitemid"]))
233
+ u = eref_url(node["bibitemid"]) or return nil
234
+ url = suffix_url(u[:link])
219
235
  anchor = node.at(ns(".//locality[@type = 'anchor']"))
220
- return url if url.nil? || /^#/.match?(url) || !anchor
236
+ /^#/.match?(url) || !anchor and return { link: url, type: u[:type] }
237
+ { link: "#{url}##{anchor.text.strip}", type: u[:type] }
238
+ end
221
239
 
222
- "#{url}##{anchor.text.strip}"
240
+ def eref_url_prep(id)
241
+ @bibitem_lookup.nil? and return nil
242
+ @bibitem_lookup[id]
223
243
  end
224
244
 
225
245
  def eref_url(id)
226
- @bibitem_lookup.nil? and return nil
227
- b = @bibitem_lookup[id] or return nil
228
- url = b.at(ns("./uri[@type = 'citation'][@language = '#{@lang}']")) ||
229
- b.at(ns("./uri[@type = 'citation']")) and return url.text
230
- b["hidden"] == "true" and return b.at(ns("./uri"))&.text
231
- "##{id}"
246
+ b = eref_url_prep(id) or return nil
247
+ %i(attachment citation).each do |x|
248
+ u = b.at(ns("./uri[@type = '#{x}'][@language = '#{@lang}']")) ||
249
+ b.at(ns("./uri[@type = '#{x}']")) and return { link: u.text, type: x }
250
+ end
251
+ if b["hidden"] == "true"
252
+ u = b.at(ns("./uri")) or return nil
253
+ { link: u.text, type: :citation }
254
+ else { link: "##{id}", type: :anchor }
255
+ end
232
256
  end
233
257
  end
234
258
  end
@@ -20,8 +20,11 @@ module IsoDoc
20
20
  end
21
21
 
22
22
  def svgmap_extract(elem)
23
- if f = elem.at(ns("./figure")) then elem.replace(f)
24
- else elem.remove
23
+ if elem.at(ns("./figure"))# then elem.replace(f)
24
+ n = semx_fmt_dup(elem)
25
+ n.xpath(ns("./target")).each(&:remove)
26
+ elem.next = n
27
+ #else elem.remove
25
28
  end
26
29
  end
27
30
 
@@ -38,21 +41,59 @@ module IsoDoc
38
41
 
39
42
  svg = Base64.strict_decode64(elem["src"]
40
43
  .sub(%r{^data:image/svg\+xml;(charset=[^;]+;)?base64,}, ""))
41
- x = Nokogiri::XML.fragment(svg.sub(/<\?xml[^>]*>/, "")) do |config|
42
- config.huge
43
- end
44
+ x = Nokogiri::XML.fragment(svg.sub(/<\?xml[^<>]*>/, ""), &:huge)
44
45
  elem["src"] = ""
45
46
  elem.children = x
46
47
  end
47
48
 
49
+ def subfigure_delim
50
+ ""
51
+ # "<span class='fmt-label-delim'>)</span>"
52
+ end
53
+
54
+ def figure_delim(_elem)
55
+ block_delim
56
+ end
57
+
58
+ def figure_name(elem)
59
+ "<span class='fmt-element-name'>#{figure_label(elem)}</span> "
60
+ end
61
+
48
62
  def figure1(elem)
49
- return sourcecode1(elem) if elem["class"] == "pseudocode" ||
50
- elem["type"] == "pseudocode"
51
- return if elem.at(ns("./figure")) && !elem.at(ns("./name"))
63
+ elem["class"] == "pseudocode" || elem["type"] == "pseudocode" and
64
+ return sourcecode1(elem)
65
+ figure_fn(elem)
66
+ figure_key(elem.at(ns("./dl")))
67
+ lbl = @xrefs.anchor(elem["id"], :label, false)
68
+ lbl and a = autonum(elem["id"], lbl)
69
+ figname = figure_name(elem)
70
+ (elem.parent.name == "figure") && a and
71
+ a += subfigure_delim
72
+ lbl && figure_label?(elem) and s = "#{figname}#{a}"
73
+ prefix_name(elem, { caption: figure_delim(elem) }, l10n(s&.strip), "name")
74
+ end
75
+
76
+ # move footnotes into key, and get rid of footnote reference
77
+ # since it is in diagram
78
+ def figure_fn(elem)
79
+ fn = elem.xpath(ns(".//fn")) - elem.xpath(ns("./name//fn"))
80
+ fn.empty? and return
81
+ dl = figure_key_insert_pt(elem)
82
+ fn.each do |f|
83
+ dl.previous = "<dt><p><sup>#{f['reference']}</sup></p></dt>" \
84
+ "<dd>#{f.remove.children.to_xml}</dd>"
85
+ end
86
+ end
52
87
 
53
- lbl = @xrefs.anchor(elem["id"], :label, false) or return
54
- prefix_name(elem, block_delim,
55
- l10n("#{figure_label(elem)} #{lbl}"), "name")
88
+ def figure_key_insert_pt(elem)
89
+ elem.at(ns("//dl/name"))&.next ||
90
+ elem.at(ns("//dl"))&.children&.first ||
91
+ elem.add_child("<dl> </dl>").first.children.first
92
+ end
93
+
94
+ def figure_label?(elem)
95
+ elem.at(ns("./figure")) && !elem.at(ns("./name")) and return false
96
+ true
56
97
  end
57
98
 
58
99
  def figure_label(elem)
@@ -61,6 +102,14 @@ module IsoDoc
61
102
  lower2cap klasslbl
62
103
  end
63
104
 
105
+ def figure_key(dlist)
106
+ dlist or return
107
+ dlist["class"] = "formula_dl"
108
+ dlist.at(ns("./name")) and return
109
+ dlist.previous =
110
+ "<p keep-with-next='true'><strong>#{@i18n.key}</strong></p>"
111
+ end
112
+
64
113
  def eps2svg(img)
65
114
  return unless eps?(img["mimetype"])
66
115
 
@@ -74,7 +123,8 @@ module IsoDoc
74
123
  def svg_emf_double(img)
75
124
  if emf?(img["mimetype"])
76
125
  img = emf_encode(img)
77
- img.children.first.previous = emf_to_svg(img)
126
+ # img.children.first.previous = emf_to_svg(img)
127
+ img.add_first_child emf_to_svg(img)
78
128
  elsif img["mimetype"] == "image/svg+xml"
79
129
  src = svg_to_emf(img) or return
80
130
  img.add_child("<emf/>")
@@ -123,16 +173,11 @@ module IsoDoc
123
173
 
124
174
  def svg_to_emf(node)
125
175
  @output_formats[:doc] or return
126
-
127
176
  svg_impose_height_attr(node)
128
-
129
- if node.elements&.first&.name == "svg" || %r{^data:}.match?(node["src"])
177
+ node.elements&.first&.name == "svg" || %r{^data:}.match?(node["src"]) and
130
178
  return svg_to_emf_from_node(node)
131
- end
132
-
133
179
  target_path = imgfile_suffix(node["src"], "emf")
134
- return target_path if File.exist?(target_path)
135
-
180
+ File.exist?(target_path) and return target_path
136
181
  svg_to_emf_from_node(node, target_path)
137
182
  end
138
183
 
@@ -0,0 +1,180 @@
1
+ module IsoDoc
2
+ class PresentationXMLConvert < ::IsoDoc::Convert
3
+ def enable_indexsect
4
+ false
5
+ end
6
+
7
+ def add_id
8
+ %(id="_#{UUIDTools::UUID.random_create}")
9
+ end
10
+
11
+ def strip_index(docxml)
12
+ docxml.xpath(ns("//index | //index-xref | //indexsect")).each(&:remove)
13
+ end
14
+
15
+ def index(xml)
16
+ if enable_indexsect && xml.at(ns("//index"))
17
+ i = xml.at(ns("//indexsect")) ||
18
+ xml.root.add_child("<indexsect #{add_id}><title>#{@i18n.index}" \
19
+ "</title></indexsect>").first
20
+ index = sort_indexterms(xml.xpath(ns("//index")),
21
+ xml.xpath(ns("//index-xref[@also = 'false']")),
22
+ xml.xpath(ns("//index-xref[@also = 'true']")))
23
+ index1(xml, i, index)
24
+ else strip_index(xml)
25
+ end
26
+ end
27
+
28
+ def index1(docxml, indexsect, index)
29
+ c = indexsect.add_child("<ul></ul>").first
30
+ index.keys.sort.each do |k|
31
+ words = index[k].keys.each_with_object({}) do |w, v|
32
+ v[sortable(w).downcase] = w
33
+ end
34
+ words.keys.localize(@lang.to_sym).sort.to_a.each do |w|
35
+ c.add_child index_entries(words, index[k], w)
36
+ end
37
+ end
38
+ index1_cleanup(docxml)
39
+ end
40
+
41
+ def index1_cleanup(docxml)
42
+ docxml.xpath(ns("//indexsect//xref")).each do |x|
43
+ x.children.remove
44
+ end
45
+ @xrefs.bookmark_anchor_names(docxml)
46
+ end
47
+
48
+ def sortable(str)
49
+ str or return " "
50
+ HTMLEntities.new.decode(Nokogiri::XML.fragment(str).text)
51
+ end
52
+
53
+ def index_entries_opt
54
+ { xref_lbl: ", ", see_lbl: ", #{see_lbl}", also_lbl: ", #{also_lbl}" }
55
+ end
56
+
57
+ def index_entries(words, index, primary)
58
+ ret = index_entries_head(words[primary],
59
+ index.dig(words[primary], nil, nil),
60
+ index_entries_opt)
61
+ words2 = index[words[primary]]&.keys&.compact
62
+ &.each_with_object({}) { |w, v| v[w.downcase] = w }
63
+ unless words2.empty?
64
+ ret += "<ul>"
65
+ words2.keys.localize(@lang.to_sym).sort.to_a.each do |w|
66
+ ret += index_entries2(words2, index[words[primary]], w)
67
+ end
68
+ ret += "</ul>"
69
+ end
70
+ "#{ret}</li>"
71
+ end
72
+
73
+ def index_entries2(words, index, secondary)
74
+ ret = index_entries_head(words[secondary],
75
+ index.dig(words[secondary], nil),
76
+ index_entries_opt)
77
+ words3 = index[words[secondary]]&.keys&.compact
78
+ &.each_with_object({}) { |w, v| v[w.downcase] = w }
79
+ unless words3.empty?
80
+ ret += "<ul>"
81
+ words3.keys.localize(@lang.to_sym).sort.to_a.each do |w|
82
+ ret += (index_entries_head(words3[w],
83
+ index[words[secondary]][words3[w]],
84
+ index_entries_opt) + "</li>")
85
+ end
86
+ ret += "</ul>"
87
+ end
88
+ "#{ret}</li>"
89
+ end
90
+
91
+ def index_entries_head(head, entries, opt)
92
+ ret = "<li>#{head}"
93
+ xref = entries&.dig(:xref)&.join(", ")
94
+ see = index_entries_see(entries, :see)
95
+ also = index_entries_see(entries, :also)
96
+ ret += "#{opt[:xref_lbl]} #{xref}" if xref
97
+ ret += "#{opt[:see_lbl]} #{see}" if see
98
+ ret += "#{opt[:also_lbl]} #{also}" if also
99
+ ret
100
+ end
101
+
102
+ def index_entries_see(entries, label)
103
+ see_sort = entries&.dig(label) or return nil
104
+ x = see_sort.each_with_object({}) do |w, v|
105
+ v[sortable(w).downcase] = w
106
+ end
107
+ x.keys.localize(@lang.to_sym).sort.to_a.map do |k|
108
+ # see_sort[k]
109
+ x[k]
110
+ end.join(", ")
111
+ end
112
+
113
+ def see_lbl
114
+ @lang == "en" ? @i18n.see : "<em>#{@i18n.see}</em>"
115
+ end
116
+
117
+ def also_lbl
118
+ @lang == "en" ? @i18n.see_also : "<em>#{@i18n.see_also}</em>"
119
+ end
120
+
121
+ def sort_indexterms(terms, see, also)
122
+ index = extract_indexterms(terms)
123
+ index = extract_indexsee(index, see, :see)
124
+ index = extract_indexsee(index, also, :also)
125
+ index.keys.sort.each_with_object({}) do |k, v|
126
+ v[sortable(k)[0].upcase.transliterate] ||= {}
127
+ v[sortable(k)[0].upcase.transliterate][k] = index[k]
128
+ end
129
+ end
130
+
131
+ def extract_indexsee(val, terms, label)
132
+ terms.each_with_object(val) do |t, v|
133
+ term, term2, term3 = extract_indexterms_init(t)
134
+ term_hash_init(v, term, term2, term3, label)
135
+ v[term][term2][term3][label] << to_xml(t.at(ns("./target"))&.children)
136
+ t.remove
137
+ end
138
+ end
139
+
140
+ def xml_encode_attr(str)
141
+ HTMLEntities.new.encode(str, :basic, :hexadecimal)
142
+ .gsub(/&#x([^;]+);/) do |_x|
143
+ "&#x#{$1.upcase};"
144
+ end
145
+ end
146
+
147
+ # attributes are decoded into UTF-8,
148
+ # elements in extract_indexsee are still in entities
149
+ def extract_indexterms(terms)
150
+ terms.each_with_object({}) do |t, v|
151
+ term, term2, term3 = extract_indexterms_init(t)
152
+ to = t["to"] ? "to='#{t['to']}' " : ""
153
+ index2bookmark(t)
154
+ term_hash_init(v, term, term2, term3, :xref)
155
+ v[term][term2][term3][:xref] << "<xref target='#{t['id']}' " \
156
+ "#{to}pagenumber='true'/>"
157
+ end
158
+ end
159
+
160
+ def extract_indexterms_init(term)
161
+ %w(primary secondary tertiary).each_with_object([]) do |x, m|
162
+ m << to_xml(term.at(ns("./#{x}"))&.children)
163
+ end
164
+ end
165
+
166
+ def term_hash_init(hash, term, term2, term3, label)
167
+ hash[term] ||= {}
168
+ hash[term][term2] ||= {}
169
+ hash[term][term2][term3] ||= {}
170
+ hash[term][term2][term3][label] ||= []
171
+ end
172
+
173
+ def index2bookmark(node)
174
+ node.name = "bookmark"
175
+ node.children.each(&:remove)
176
+ node["id"] = "_#{UUIDTools::UUID.random_create}"
177
+ node.delete("to")
178
+ end
179
+ end
180
+ end