metanorma-standoc 2.0.2 → 2.0.5

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/lib/metanorma/standoc/base.rb +73 -23
  3. data/lib/metanorma/standoc/basicdoc.rng +5 -3
  4. data/lib/metanorma/standoc/biblio.rng +5 -3
  5. data/lib/metanorma/standoc/blocks.rb +4 -4
  6. data/lib/metanorma/standoc/cleanup_amend.rb +33 -30
  7. data/lib/metanorma/standoc/cleanup_boilerplate.rb +29 -5
  8. data/lib/metanorma/standoc/cleanup_image.rb +117 -3
  9. data/lib/metanorma/standoc/cleanup_ref.rb +32 -3
  10. data/lib/metanorma/standoc/cleanup_ref_dl.rb +25 -16
  11. data/lib/metanorma/standoc/cleanup_section.rb +2 -2
  12. data/lib/metanorma/standoc/cleanup_terms_designations.rb +4 -2
  13. data/lib/metanorma/standoc/cleanup_text.rb +39 -17
  14. data/lib/metanorma/standoc/cleanup_xref.rb +82 -13
  15. data/lib/metanorma/standoc/converter.rb +2 -1
  16. data/lib/metanorma/standoc/inline.rb +21 -12
  17. data/lib/metanorma/standoc/isodoc.rng +73 -3
  18. data/lib/metanorma/standoc/macros.rb +14 -2
  19. data/lib/metanorma/standoc/macros_embed.rb +35 -14
  20. data/lib/metanorma/standoc/macros_note.rb +4 -3
  21. data/lib/metanorma/standoc/processor.rb +6 -1
  22. data/lib/metanorma/standoc/ref.rb +20 -16
  23. data/lib/metanorma/standoc/ref_utility.rb +8 -6
  24. data/lib/metanorma/standoc/render.rb +7 -3
  25. data/lib/metanorma/standoc/table.rb +8 -10
  26. data/lib/metanorma/standoc/term_lookup_cleanup.rb +16 -10
  27. data/lib/metanorma/standoc/terms.rb +10 -7
  28. data/lib/metanorma/standoc/utils.rb +3 -1
  29. data/lib/metanorma/standoc/validate.rb +81 -7
  30. data/lib/metanorma/standoc/version.rb +1 -1
  31. data/metanorma-standoc.gemspec +2 -2
  32. data/spec/assets/a2.adoc +4 -2
  33. data/spec/assets/a3.adoc +2 -2
  34. data/spec/assets/a3a.adoc +7 -0
  35. data/spec/metanorma/base_spec.rb +1 -1
  36. data/spec/metanorma/cleanup_blocks_spec.rb +136 -0
  37. data/spec/metanorma/cleanup_spec.rb +34 -23
  38. data/spec/metanorma/cleanup_terms_spec.rb +16 -4
  39. data/spec/metanorma/inline_spec.rb +31 -0
  40. data/spec/metanorma/isobib_cache_spec.rb +2 -2
  41. data/spec/metanorma/macros_plantuml_spec.rb +41 -42
  42. data/spec/metanorma/macros_spec.rb +267 -4
  43. data/spec/metanorma/processor_spec.rb +17 -13
  44. data/spec/metanorma/refs_spec.rb +632 -460
  45. data/spec/metanorma/section_spec.rb +1 -1
  46. data/spec/metanorma/validate_spec.rb +108 -0
  47. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +48 -48
  48. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec1.yml +13 -13
  49. data/spec/vcr_cassettes/hide_refs.yml +100 -100
  50. data/spec/vcr_cassettes/isobib_get_123.yml +12 -12
  51. data/spec/vcr_cassettes/isobib_get_123_1.yml +25 -25
  52. data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +35 -35
  53. data/spec/vcr_cassettes/isobib_get_123_2001.yml +11 -11
  54. data/spec/vcr_cassettes/isobib_get_124.yml +13 -13
  55. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +18 -18
  56. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +86 -66
  57. metadata +10 -9
@@ -2,7 +2,7 @@ module Metanorma
2
2
  module Standoc
3
3
  module Refs
4
4
  def set_date_range(date, text)
5
- matched = /^(?<from>[0-9]+)(-+(?<to>[0-9]+))?$/.match text
5
+ matched = /^(?<from>[0-9]+)(?:-+(?<to>[0-9]+))?$/.match text
6
6
  return unless matched[:from]
7
7
 
8
8
  if matched[:to]
@@ -75,7 +75,8 @@ module Metanorma
75
75
 
76
76
  def analyse_ref_repo_path(ret)
77
77
  return ret unless m =
78
- /^(?<type>repo|path):\((?<key>[^,]+),?(?<id>.*)\)$/.match(ret[:id])
78
+ /^(?<type>repo|path):\((?<key>[^,]+),?(?<id>.*)\)$/
79
+ .match(ret[:id])
79
80
 
80
81
  id = m[:id].empty? ? m[:key].sub(%r{^[^/]+/}, "") : m[:id]
81
82
  ret.merge(id: id, type: m[:type], key: m[:key], nofetch: true)
@@ -112,10 +113,11 @@ module Metanorma
112
113
  { id: match[:anchor], type: "standard" }
113
114
  end
114
115
 
115
- MALFORMED_REF =
116
- "no anchor on reference, markup may be malformed: see "\
117
- "https://www.metanorma.com/author/topics/document-format/bibliography/ , "\
118
- "https://www.metanorma.com/author/iso/topics/markup/#bibliographies".freeze
116
+ MALFORMED_REF = <<~REF.freeze
117
+ no anchor on reference, markup may be malformed: see
118
+ https://www.metanorma.com/author/topics/document-format/bibliography/ ,
119
+ https://www.metanorma.com/author/iso/topics/markup/#bibliographies
120
+ REF
119
121
 
120
122
  def ref_normalise(ref)
121
123
  ref.gsub(/&amp;amp;/, "&amp;").gsub(%r{^<em>(.*)</em>}, "\\1")
@@ -25,6 +25,9 @@ module Metanorma
25
25
  sectionsplit: node.attr("sectionsplit"),
26
26
  baseassetpath: node.attr("base-asset-path"),
27
27
  aligncrosselements: node.attr("align-cross-elements"),
28
+ tocfigures: @tocfigures,
29
+ toctables: @toctables,
30
+ tocrecommendations: @tocrecommendations,
28
31
  }
29
32
  end
30
33
 
@@ -61,12 +64,13 @@ module Metanorma
61
64
  bare: node.attr("bare"),
62
65
  baseassetpath: node.attr("base-asset-path"),
63
66
  aligncrosselements: node.attr("align-cross-elements"),
67
+ tocfigures: @tocfigures,
68
+ toctables: @toctables,
69
+ tocrecommendations: @tocrecommendations,
64
70
  }
65
71
 
66
72
  if fonts_manifest = node.attr(FONTS_MANIFEST)
67
- attrs[IsoDoc::XslfoPdfConvert::MN2PDF_OPTIONS] = {
68
- IsoDoc::XslfoPdfConvert::MN2PDF_FONT_MANIFEST => fonts_manifest,
69
- }
73
+ attrs[IsoDoc::XslfoPdfConvert::MN2PDF_FONT_MANIFEST] = fonts_manifest
70
74
  end
71
75
 
72
76
  attrs
@@ -19,9 +19,7 @@ module Metanorma
19
19
  xml.table **attr_code(table_attrs(node)) do |xml_table|
20
20
  colgroup(node, xml_table)
21
21
  table_name(node, xml_table)
22
- %i(head body foot).reject do |tblsec|
23
- node.rows[tblsec].empty?
24
- end
22
+ %i(head body foot).reject { |tblsec| node.rows[tblsec].empty? }
25
23
  table_head_body_and_foot node, xml_table
26
24
  end
27
25
  end
@@ -50,12 +48,12 @@ module Metanorma
50
48
  end
51
49
  end
52
50
 
53
- def table_cell1(cell, thd)
54
- thd << if cell.style == :asciidoc
55
- cell.content
56
- else
57
- cell.text
58
- end
51
+ def table_cell1(cell)
52
+ if cell.style == :asciidoc
53
+ cell.content
54
+ else
55
+ cell.text
56
+ end
59
57
  end
60
58
 
61
59
  def table_cell(node, xml_tr, tblsec)
@@ -65,7 +63,7 @@ module Metanorma
65
63
  cell_tag = "td"
66
64
  cell_tag = "th" if tblsec == :head || node.style == :header
67
65
  xml_tr.send cell_tag, **attr_code(cell_attributes) do |thd|
68
- table_cell1(node, thd)
66
+ thd << table_cell1(node)
69
67
  end
70
68
  end
71
69
 
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true.
2
2
  require "metanorma/standoc/utils"
3
3
 
4
-
5
4
  module Metanorma
6
5
  module Standoc
7
6
  # Intelligent term lookup xml modifier
@@ -58,7 +57,7 @@ module Metanorma
58
57
 
59
58
  def set_termxref_tags_target
60
59
  xmldoc.xpath("//termxref").each do |node|
61
- target = normalize_ref_id(node.text)
60
+ target = normalize_ref_id(node)
62
61
  if termlookup[:term][target].nil? && termlookup[:symbol][target].nil?
63
62
  remove_missing_ref(node, target)
64
63
  next
@@ -78,7 +77,7 @@ module Metanorma
78
77
 
79
78
  def remove_missing_ref_term(node, target)
80
79
  log.add("AsciiDoc Input", node,
81
- %(Error: Term reference in `term[#{target}]` missing: \
80
+ %(Error: Term reference to `#{target}` missing: \
82
81
  "#{target}" is not defined in document))
83
82
  node.name = "strong"
84
83
  node&.at("../xrefrender")&.remove
@@ -110,15 +109,15 @@ module Metanorma
110
109
  if type == "term" || ((!type || node.parent.name == "related") && t)
111
110
  node["target"] = t
112
111
  elsif type == "symbol" ||
113
- ((!type || node.parent.name == "related") && s)
112
+ ((!type || node.parent.name == "related") && s)
114
113
  node["target"] = s
115
114
  end
116
115
  end
117
116
 
118
117
  def replace_automatic_generated_ids_terms
119
118
  r = xmldoc.xpath("//term").each.with_object({}) do |n, res|
120
- normalize_id_and_memorize(n, res, "./preferred//name",
121
- "term")
119
+ normalize_id_and_memorize(n, res, "./preferred//name", "term")
120
+ normalize_id_and_memorize(n, res, "./admitted//name", "term")
122
121
  end
123
122
  s = xmldoc.xpath("//definitions//dt").each.with_object({}) do |n, res|
124
123
  normalize_id_and_memorize(n, res, ".", "symbol")
@@ -127,11 +126,13 @@ module Metanorma
127
126
  end
128
127
 
129
128
  def pref_secondary2primary
129
+ term = ""
130
130
  xmldoc.xpath("//term").each.with_object({}) do |n, res|
131
131
  n.xpath("./preferred//name").each_with_index do |p, i|
132
132
  i.zero? and term = p.text
133
133
  i.positive? and res[p.text] = term
134
134
  end
135
+ n.xpath("./admitted//name").each { |p| res[p.text] = term }
135
136
  end
136
137
  end
137
138
 
@@ -141,7 +142,7 @@ module Metanorma
141
142
  end
142
143
 
143
144
  def normalize_id_and_memorize_init(node, res_table, text_selector, prefix)
144
- term_text = normalize_ref_id(node.at(text_selector).text)
145
+ term_text = normalize_ref_id(node.at(text_selector)) or return
145
146
  unless AUTOMATIC_GENERATED_ID_REGEXP.match(node["id"]).nil? &&
146
147
  !node["id"].nil?
147
148
  id = unique_text_id(term_text, prefix)
@@ -155,12 +156,17 @@ module Metanorma
155
156
  node.xpath(text_selector).each_with_index do |p, i|
156
157
  next unless i.positive?
157
158
 
158
- res_table[normalize_ref_id(p.text)] = node["id"]
159
+ res_table[normalize_ref_id(p)] = node["id"]
159
160
  end
160
161
  end
161
162
 
162
- def normalize_ref_id(text)
163
- Metanorma::Utils::to_ncname(text.downcase.gsub(/[[:space:]]/, "-"))
163
+ def normalize_ref_id(term)
164
+ return nil if term.nil?
165
+
166
+ t = term.dup
167
+ t.xpath(".//index").map(&:remove)
168
+ Metanorma::Utils::to_ncname(t.text.strip.downcase
169
+ .gsub(/[[:space:]]+/, "-"))
164
170
  end
165
171
 
166
172
  def unique_text_id(text, prefix)
@@ -92,7 +92,7 @@ module Metanorma
92
92
  end
93
93
  end
94
94
 
95
- def term_source_attrs(_node, seen_xref)
95
+ def termsource_origin_attrs(_node, seen_xref)
96
96
  { case: seen_xref.children[0]["case"],
97
97
  droploc: seen_xref.children[0]["droploc"],
98
98
  bibitemid: seen_xref.children[0]["target"],
@@ -103,7 +103,7 @@ module Metanorma
103
103
  if seen_xref.children[0].name == "concept"
104
104
  xml_t.origin { |o| o << seen_xref.children[0].to_xml }
105
105
  else
106
- attrs = term_source_attrs(node, seen_xref)
106
+ attrs = termsource_origin_attrs(node, seen_xref)
107
107
  attrs.delete(:text)
108
108
  xml_t.origin **attr_code(attrs) do |o|
109
109
  o << seen_xref.children[0].children.to_xml
@@ -135,13 +135,16 @@ module Metanorma
135
135
  matched
136
136
  end
137
137
 
138
+ def termsource_attrs(node, matched)
139
+ status = node.attr("status") ||
140
+ (matched[:text] ? "modified" : "identical")
141
+ { status: status, type: node.attr("type") || "authoritative" }
142
+ end
143
+
138
144
  def termsource(node)
139
- matched = extract_termsource_refs(node.content, node) || return
145
+ matched = extract_termsource_refs(node.content, node) or return
140
146
  noko do |xml|
141
- status = node.attr("status") ||
142
- (matched[:text] ? "modified" : "identical")
143
- attrs = { status: status, type: node.attr("type") || "authoritative" }
144
- xml.termsource **attrs do |xml_t|
147
+ xml.termsource **termsource_attrs(node, matched) do |xml_t|
145
148
  seen_xref = Nokogiri::XML.fragment(matched[:xref])
146
149
  add_term_source(node, xml_t, seen_xref, matched)
147
150
  end
@@ -32,7 +32,9 @@ module Metanorma
32
32
  doc = ::Nokogiri::XML.parse(NOKOHEAD)
33
33
  fragment = doc.fragment("")
34
34
  ::Nokogiri::XML::Builder.with fragment, &block
35
- fragment.to_xml(encoding: "US-ASCII", indent: 0).lines.map do |l|
35
+ fragment.to_xml(encoding: "US-ASCII", indent: 0,
36
+ save_with: Nokogiri::XML::Node::SaveOptions::AS_XML)
37
+ .lines.map do |l|
36
38
  l.gsub(/>\n$/, ">").gsub(/\s*\n$/m, " ").gsub("&#150;", "\u0096")
37
39
  .gsub("&#151;", "\u0097").gsub("&#x96;", "\u0096")
38
40
  .gsub("&#x97;", "\u0097")
@@ -49,9 +49,79 @@ module Metanorma
49
49
  iev_validate(doc.root)
50
50
  concept_validate(doc, "concept", "refterm")
51
51
  concept_validate(doc, "related", "preferred//name")
52
+ table_validate(doc)
52
53
  @fatalerror.empty? or clean_abort(@fatalerror.join("\n"), doc.to_xml)
53
54
  end
54
55
 
56
+ def table_validate(doc)
57
+ doc.xpath("//table[colgroup]").each do |t|
58
+ maxrowcols_validate(t, t.xpath("./colgroup/col").size)
59
+ end
60
+ doc.xpath("//table[.//*[@colspan] | .//*[@rowspan]]").each do |t|
61
+ maxrowcols_validate(t, max_td_count(t))
62
+ end
63
+ end
64
+
65
+ def max_td_count(table)
66
+ max = 0
67
+ table.xpath(".//tr").each do |tr|
68
+ n = tr.xpath("./td | ./th").size
69
+ max < n and max = n
70
+ end
71
+ max
72
+ end
73
+
74
+ def maxrowcols_validate(table, maxcols)
75
+ cells2d = table.xpath(".//tr").each_with_object([]) { |_r, m| m << {} }
76
+ table.xpath(".//tr").each_with_index do |tr, r|
77
+ curr = 0
78
+ tr.xpath("./td | ./th").each do |td|
79
+ curr = maxcols_validate1(td, r, curr, cells2d, maxcols)
80
+ end
81
+ end
82
+ maxrows_validate(table, cells2d)
83
+ end
84
+
85
+ # code doesn't actually do anything, since Asciidoctor refuses to generate
86
+ # table with inconsistent column count
87
+ def maxcols_validate1(tcell, row, curr, cells2d, maxcols)
88
+ rs = tcell&.attr("rowspan")&.to_i || 1
89
+ cs = tcell&.attr("colspan")&.to_i || 1
90
+ curr = table_tracker_update(cells2d, row, curr, rs, cs)
91
+ maxcols_check(curr + cs - 1, maxcols, tcell)
92
+ curr + cs
93
+ end
94
+
95
+ def table_tracker_update(cells2d, row, curr, rowspan, colspan)
96
+ cells2d[row] ||= {}
97
+ while cells2d[row][curr]
98
+ curr += 1
99
+ end
100
+ (row..(row + rowspan - 1)).each do |y2|
101
+ cells2d[y2] ||= {}
102
+ (curr..(curr + colspan - 1)).each { |x2| cells2d[y2][x2] = 1 }
103
+ end
104
+ curr
105
+ end
106
+
107
+ def maxrows_validate(table, cells2d)
108
+ if cells2d.any? { |x| x.size != cells2d.first.size }
109
+ @log.add("Table", table,
110
+ "Table rows in table are inconsistent: check rowspan")
111
+ @fatalerror << "Table rows in table are inconsistent: check rowspan"
112
+ end
113
+ end
114
+
115
+ # if maxcols or maxrows negative, do not check them
116
+ def maxcols_check(col, maxcols, tcell)
117
+ if maxcols.positive? && col > maxcols
118
+ @log.add("Table", tcell, "Table exceeds maximum number of columns "\
119
+ "defined (#{maxcols})")
120
+ @fatalerror << "Table exceeds maximum number of columns defined "\
121
+ "(#{maxcols})"
122
+ end
123
+ end
124
+
55
125
  def norm_ref_validate(doc)
56
126
  found = false
57
127
  doc.xpath("//references[@normative = 'true']/bibitem").each do |b|
@@ -71,10 +141,9 @@ module Metanorma
71
141
  next if doc.at("//term[@id = '#{x['target']}']")
72
142
  next if doc.at("//definitions//dt[@id = '#{x['target']}']")
73
143
 
74
- ref = x&.at("../#{refterm}")&.text
75
144
  @log.add("Anchors", x,
76
- "#{tag.capitalize} #{ref} is pointing to "\
77
- "#{x['target']}, which is not a term or symbol")
145
+ "#{tag.capitalize} #{x&.at("../#{refterm}")&.text} is "\
146
+ "pointing to #{x['target']}, which is not a term or symbol")
78
147
  found = true
79
148
  end
80
149
  found and
@@ -120,20 +189,25 @@ module Metanorma
120
189
  end
121
190
  end
122
191
 
192
+ SVG_NS = "http://www.w3.org/2000/svg".freeze
193
+
194
+ WILDCARD_ATTRS =
195
+ "//*[@format] | //stem | //bibdata//description | "\
196
+ "//formattedref | //bibdata//note | //bibdata/abstract | "\
197
+ "//bibitem/abstract | //bibitem/note | //misc-container".freeze
198
+
123
199
  # RelaxNG cannot cope well with wildcard attributes. So we strip
124
200
  # any attributes from FormattedString instances (which can contain
125
201
  # xs:any markup, and are signalled with @format) before validation.
126
202
  def formattedstr_strip(doc)
127
- doc.xpath("//*[@format] | //stem | //bibdata//description | "\
128
- "//formattedref | //bibdata//note | //bibdata/abstract | "\
129
- "//bibitem/abstract | //bibitem/note | //misc-container")
130
- .each do |n|
203
+ doc.xpath(WILDCARD_ATTRS, "m" => SVG_NS).each do |n|
131
204
  n.elements.each do |e|
132
205
  e.traverse do |e1|
133
206
  e1.element? and e1.each { |k, _v| e1.delete(k) }
134
207
  end
135
208
  end
136
209
  end
210
+ doc.xpath("//m:svg", "m" => SVG_NS).each { |n| n.replace("<svg/>") }
137
211
  doc
138
212
  end
139
213
 
@@ -19,6 +19,6 @@ module Metanorma
19
19
  end
20
20
 
21
21
  module Standoc
22
- VERSION = "2.0.2".freeze
22
+ VERSION = "2.0.5".freeze
23
23
  end
24
24
  end
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_dependency "asciidoctor", "~> 2.0.0"
30
30
  spec.add_dependency "iev", "~> 0.3.0"
31
31
  spec.add_dependency "isodoc", "~> 2.0.0"
32
- spec.add_dependency "metanorma-plugin-datastruct"
32
+ spec.add_dependency "metanorma-plugin-datastruct", "~> 0.2.0"
33
33
  spec.add_dependency "metanorma-plugin-lutaml"
34
34
  spec.add_dependency "ruby-jing"
35
35
  # relaton-cli not just relaton, to avoid circular reference in metanorma
@@ -52,6 +52,6 @@ Gem::Specification.new do |spec|
52
52
  spec.add_development_dependency "sassc", "2.4.0"
53
53
  spec.add_development_dependency "simplecov", "~> 0.15"
54
54
  spec.add_development_dependency "timecop", "~> 0.9"
55
- spec.add_development_dependency "vcr", "~> 5.0.0"
55
+ spec.add_development_dependency "vcr", "~> 6.1.0"
56
56
  spec.add_development_dependency "webmock"
57
57
  end
data/spec/assets/a2.adoc CHANGED
@@ -1,8 +1,10 @@
1
- = X
2
- A
1
+ = A2
2
+ A2
3
3
 
4
4
  == Clause 2
5
5
 
6
6
  X
7
7
 
8
8
  embed::spec/assets/a3.adoc[]
9
+
10
+ embed::spec/assets/a3a.adoc[]
data/spec/assets/a3.adoc CHANGED
@@ -1,5 +1,5 @@
1
- = X
2
- A
1
+ = A3
2
+ A3
3
3
 
4
4
  == Clause 3
5
5
 
@@ -0,0 +1,7 @@
1
+ = A3a
2
+ A3a
3
+
4
+ == Clause 3a
5
+
6
+ X
7
+
@@ -1051,7 +1051,7 @@ QU1FOiB0ZXN0Cgo=
1051
1051
  .new(:standoc, header_footer: true)
1052
1052
  .doc_extract_attributes(node)
1053
1053
 
1054
- expect(options.dig(:mn2pdf, :font_manifest))
1054
+ expect(options[:font_manifest])
1055
1055
  .to eq(node[Metanorma::Standoc::Base::FONTS_MANIFEST])
1056
1056
  end
1057
1057
 
@@ -1039,4 +1039,140 @@ RSpec.describe Metanorma::Standoc do
1039
1039
  annotation_id = output.at("//xmlns:annotation/@id").text
1040
1040
  expect(callout_id).to eq(annotation_id)
1041
1041
  end
1042
+ it "deduplicates identifiers in inline SVGs" do
1043
+ input = <<~INPUT
1044
+ #{BLANK_HDR}
1045
+ <sections>
1046
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 256 256">
1047
+ <defs>
1048
+ <linearGradient id="gradient1">
1049
+ <stop class="stop1" offset="0%" xlink:href="#gradient1"/>
1050
+ <stop class="stop2" offset="100%"/>
1051
+ <style>url(#gradient1)</style>
1052
+ </linearGradient>
1053
+ </defs>
1054
+ <circle fill="url(#gradient1)" cx="128" cy="128" r="100" />
1055
+ </svg>
1056
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 256 256">
1057
+ <defs>
1058
+ <linearGradient id="gradient2">
1059
+ <stop class="stop1" offset="0%" xlink:href="#gradient2"/>
1060
+ <stop class="stop2" offset="100%"/>
1061
+ <style>url(#gradient2)</style>
1062
+ </linearGradient>
1063
+ </defs>
1064
+ <circle fill="url(#gradient2)" cx="128" cy="128" r="100" />
1065
+ </svg>
1066
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 256 256">
1067
+ <defs>
1068
+ <linearGradient id="gradient1">
1069
+ <stop class="stop1" offset="0%" xlink:href="#gradient1"/>
1070
+ <stop class="stop2" offset="100%"/>
1071
+ <style>url(#gradient1)</style>
1072
+ </linearGradient>
1073
+ </defs>
1074
+ <circle fill="url(#gradient1)" cx="128" cy="128" r="100" />
1075
+ </svg>
1076
+ </sections>
1077
+ </standard-document>
1078
+ INPUT
1079
+ output = <<~OUTPUT
1080
+ #{BLANK_HDR}
1081
+ <sections>
1082
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 256 256">
1083
+ <defs>
1084
+ <linearGradient id="gradient1_inject_0">
1085
+ <stop class="stop1" offset="0%" xlink:href="#gradient1_inject_0"/>
1086
+ <stop class="stop2" offset="100%"/>
1087
+ <style>url(#gradient1_inject_0)</style>
1088
+ </linearGradient>
1089
+ </defs>
1090
+ <circle fill="url(#gradient1_inject_0)" cx="128" cy="128" r="100"/>
1091
+ </svg>
1092
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 256 256">
1093
+ <defs>
1094
+ <linearGradient id="gradient2">
1095
+ <stop class="stop1" offset="0%" xlink:href="#gradient2"/>
1096
+ <stop class="stop2" offset="100%"/>
1097
+ <style>url(#gradient2)</style>
1098
+ </linearGradient>
1099
+ </defs>
1100
+ <circle fill="url(#gradient2)" cx="128" cy="128" r="100"/>
1101
+ </svg>
1102
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 256 256">
1103
+ <defs>
1104
+ <linearGradient id="gradient1_inject_2">
1105
+ <stop class="stop1" offset="0%" xlink:href="#gradient1_inject_2"/>
1106
+ <stop class="stop2" offset="100%"/>
1107
+ <style>url(#gradient1_inject_2)</style>
1108
+ </linearGradient>
1109
+ </defs>
1110
+ <circle fill="url(#gradient1_inject_2)" cx="128" cy="128" r="100"/>
1111
+ </svg>
1112
+ </sections>
1113
+ </standard-document>
1114
+ OUTPUT
1115
+ expect(Metanorma::Standoc::Converter.new(nil, *OPTIONS)
1116
+ .cleanup(Nokogiri::XML(input)).to_xml)
1117
+ .to be_equivalent_to xmlpp(output)
1118
+ end
1119
+
1120
+ it "deduplicates identifiers in embedded SVGs" do
1121
+ input = <<~INPUT
1122
+ #{ASCIIDOC_BLANK_HDR.sub(/:data-uri-image: false/, ':data-uri-image: true')}
1123
+
1124
+ image::spec/fixtures/action_schemaexpg1.svg[]
1125
+
1126
+ image::spec/examples/rice_images/rice_image1.png[]
1127
+
1128
+ image::spec/fixtures/action_schemaexpg1.svg[]
1129
+ INPUT
1130
+
1131
+ output = <<~OUTPUT
1132
+ #{BLANK_HDR}
1133
+ <sections>
1134
+ <figure id='_'>
1135
+ <svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='Layer_1' x='0px' y='0px' viewBox='0 0 595.28 841.89' style='enable-background:new 0 0 595.28 841.89;' xml:space='preserve'>
1136
+ <style/>
1137
+ <image/>
1138
+ <a xlink:href='mn://action_schema'>
1139
+ <rect x='123.28' y='273.93' class='st0' width='88.05' height='41.84'/>
1140
+ </a>
1141
+ <a xlink:href='mn://basic_attribute_schema'>
1142
+ <rect x='324.69' y='450.52' class='st0' width='132.62' height='40.75'/>
1143
+ </a>
1144
+ <a xlink:href='mn://support_resource_schema'>
1145
+ <rect x='324.69' y='528.36' class='st0' width='148.16' height='40.75'/>
1146
+ </a>
1147
+ </svg>
1148
+ </figure>
1149
+ <figure id='_'>
1150
+ <image/>
1151
+ </figure>
1152
+ <figure id='_'>
1153
+ <svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='Layer_1_inject_1' x='0px' y='0px' viewBox='0 0 595.28 841.89' style='enable-background:new 0 0 595.28 841.89;' xml:space='preserve'>
1154
+ <style/>
1155
+ <image/>
1156
+ <a xlink:href='mn://action_schema'>
1157
+ <rect x='123.28' y='273.93' class='st0' width='88.05' height='41.84'/>
1158
+ </a>
1159
+ <a xlink:href='mn://basic_attribute_schema'>
1160
+ <rect x='324.69' y='450.52' class='st0' width='132.62' height='40.75'/>
1161
+ </a>
1162
+ <a xlink:href='mn://support_resource_schema'>
1163
+ <rect x='324.69' y='528.36' class='st0' width='148.16' height='40.75'/>
1164
+ </a>
1165
+ </svg>
1166
+ </figure>
1167
+ </sections>
1168
+ </standard-document>
1169
+ OUTPUT
1170
+ xml = Nokogiri::XML(Asciidoctor.convert(input, *OPTIONS))
1171
+ xml.xpath("//*[local-name() = 'image']").each do |x|
1172
+ x.replace("<image/>")
1173
+ end
1174
+ expect(xmlpp(strip_guid(xml.to_xml)
1175
+ .gsub(%r{<style.*?</style>}m, "<style/>")))
1176
+ .to be_equivalent_to xmlpp(output)
1177
+ end
1042
1178
  end