metanorma-standoc 3.0.7 → 3.0.9

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/isodoc/html/htmlstyle.css +0 -2
  3. data/lib/metanorma/standoc/anchor.rb +1 -1
  4. data/lib/metanorma/standoc/base.rb +1 -1
  5. data/lib/metanorma/standoc/basicdoc.rng +9 -5
  6. data/lib/metanorma/standoc/blocks.rb +8 -5
  7. data/lib/metanorma/standoc/blocks_image.rb +2 -6
  8. data/lib/metanorma/standoc/blocks_notes.rb +2 -6
  9. data/lib/metanorma/standoc/cleanup.rb +5 -1
  10. data/lib/metanorma/standoc/cleanup_amend.rb +6 -8
  11. data/lib/metanorma/standoc/cleanup_asciibib.rb +17 -13
  12. data/lib/metanorma/standoc/cleanup_bibdata.rb +19 -13
  13. data/lib/metanorma/standoc/cleanup_bibitem.rb +9 -6
  14. data/lib/metanorma/standoc/cleanup_block.rb +6 -6
  15. data/lib/metanorma/standoc/cleanup_boilerplate.rb +5 -4
  16. data/lib/metanorma/standoc/cleanup_footnotes.rb +2 -4
  17. data/lib/metanorma/standoc/cleanup_image.rb +3 -3
  18. data/lib/metanorma/standoc/cleanup_inline.rb +22 -47
  19. data/lib/metanorma/standoc/cleanup_review.rb +7 -5
  20. data/lib/metanorma/standoc/cleanup_section.rb +30 -5
  21. data/lib/metanorma/standoc/cleanup_table.rb +1 -2
  22. data/lib/metanorma/standoc/cleanup_terms.rb +1 -1
  23. data/lib/metanorma/standoc/cleanup_terms_designations.rb +1 -1
  24. data/lib/metanorma/standoc/cleanup_text.rb +5 -3
  25. data/lib/metanorma/standoc/cleanup_toc.rb +1 -1
  26. data/lib/metanorma/standoc/cleanup_xref.rb +1 -1
  27. data/lib/metanorma/standoc/converter.rb +15 -7
  28. data/lib/metanorma/standoc/init.rb +15 -7
  29. data/lib/metanorma/standoc/inline.rb +12 -7
  30. data/lib/metanorma/standoc/isodoc.rng +145 -5
  31. data/lib/metanorma/standoc/localbib.rb +1 -2
  32. data/lib/metanorma/standoc/macros.rb +0 -1
  33. data/lib/metanorma/standoc/macros_form.rb +21 -3
  34. data/lib/metanorma/standoc/macros_inline.rb +24 -1
  35. data/lib/metanorma/standoc/macros_link.rb +13 -5
  36. data/lib/metanorma/standoc/macros_plantuml.rb +28 -14
  37. data/lib/metanorma/standoc/ref.rb +2 -2
  38. data/lib/metanorma/standoc/ref_sect.rb +1 -1
  39. data/lib/metanorma/standoc/ref_utility.rb +4 -3
  40. data/lib/metanorma/standoc/section.rb +9 -10
  41. data/lib/metanorma/standoc/sectiontype.rb +28 -20
  42. data/lib/metanorma/standoc/table.rb +9 -13
  43. data/lib/metanorma/standoc/term_lookup_cleanup.rb +17 -9
  44. data/lib/metanorma/standoc/terms.rb +1 -1
  45. data/lib/metanorma/standoc/utils.rb +4 -0
  46. data/lib/metanorma/standoc/validate.rb +50 -23
  47. data/lib/metanorma/standoc/validate_schema.rb +2 -0
  48. data/lib/metanorma/standoc/validate_term.rb +8 -7
  49. data/lib/metanorma/standoc/version.rb +1 -1
  50. data/metanorma-standoc.gemspec +2 -3
  51. metadata +6 -20
@@ -4,9 +4,15 @@ module Metanorma
4
4
  use_dsl
5
5
  named :input
6
6
 
7
+ def map_attr_name(attr)
8
+ attr == "id" ? "anchor" : attr
9
+ end
10
+
7
11
  def process(_parent, target, attr)
8
12
  m = %w(id name value disabled readonly checked maxlength minlength)
9
- .map { |a| attr[a] ? " #{a}='#{attr[a]}'" : nil }.compact
13
+ .map { |a| attr[a] ? " #{map_attr_name(a)}='#{attr[a]}'" : nil }
14
+ .compact
15
+ m << " id='_#{UUIDTools::UUID.random_create}'"
10
16
  %{<input type='#{target}' #{m.join}/>}
11
17
  end
12
18
  end
@@ -27,9 +33,15 @@ module Metanorma
27
33
  named :textarea
28
34
  using_format :short
29
35
 
36
+ def map_attr_name(attr)
37
+ attr == "id" ? "anchor" : attr
38
+ end
39
+
30
40
  def process(_parent, _target, attr)
31
41
  m = %w(id name rows cols value)
32
- .map { |a| attr[a] ? " #{a}='#{attr[a]}'" : nil }.compact
42
+ .map { |a| attr[a] ? " #{map_attr_name(a)}='#{attr[a]}'" : nil }
43
+ .compact
44
+ m << " id='_#{UUIDTools::UUID.random_create}'"
33
45
  %{<textarea #{m.join}/>}
34
46
  end
35
47
  end
@@ -39,9 +51,15 @@ module Metanorma
39
51
  named :select
40
52
  using_format :short
41
53
 
54
+ def map_attr_name(attr)
55
+ attr == "id" ? "anchor" : attr
56
+ end
57
+
42
58
  def process(parent, _target, attr)
43
59
  m = %w(id name size disabled multiple value)
44
- .map { |a| attr[a] ? " #{a}='#{attr[a]}'" : nil }.compact
60
+ .map { |a| attr[a] ? " #{map_attr_name(a)}='#{attr[a]}'" : nil }
61
+ .compact
62
+ m << " id='_#{UUIDTools::UUID.random_create}'"
45
63
  out = Asciidoctor::Inline.new(parent, :quoted, attr["text"]).convert
46
64
  %{<select #{m.join}>#{out}</select>}
47
65
  end
@@ -58,6 +58,30 @@ module Metanorma
58
58
  end
59
59
  end
60
60
 
61
+ class AnchorInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
62
+ use_dsl
63
+ named :anchor
64
+ parse_content_as :text
65
+
66
+ def process(parent, target, attrs)
67
+ out = Asciidoctor::Inline.new(parent, :quoted, attrs["text"]).convert
68
+ id = "_#{UUIDTools::UUID.random_create}"
69
+ %{<span id='#{id}' anchor='#{target}'>#{out}</span>}
70
+ end
71
+ end
72
+
73
+ class SourceIdInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
74
+ use_dsl
75
+ named :"source-id"
76
+ parse_content_as :text
77
+
78
+ def process(parent, target, attrs)
79
+ out = Asciidoctor::Inline.new(parent, :quoted, attrs["text"]).convert
80
+ id = "_#{UUIDTools::UUID.random_create}"
81
+ %{<span id='#{id}' source='#{target}'>#{out}</span>}
82
+ end
83
+ end
84
+
61
85
  class VariantInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
62
86
  use_dsl
63
87
  named :lang
@@ -117,7 +141,6 @@ module Metanorma
117
141
  parse_content_as :text
118
142
 
119
143
  def process(parent, target, attrs)
120
- #require "debug"; binding.b
121
144
  format = target || "metanorma"
122
145
  out = Asciidoctor::Inline.new(parent, :quoted, attrs["text"]).convert
123
146
  .gsub(/((?![<>&])[[:punct:]])/, "\\1&#x200c;")
@@ -68,13 +68,21 @@ module Metanorma
68
68
 
69
69
  def process(parent, _target, attrs)
70
70
  t = attrs["text"]
71
- t = if /,/.match?(t)
71
+ t = if t.include?(",")
72
72
  t.sub(/,/, "%")
73
- else
74
- "#{t}%"
73
+ else "#{t}%"
75
74
  end
76
- create_anchor(parent, "hidden=#{t}",
77
- type: :xref, target: "_#{UUIDTools::UUID.random_create}")
75
+ target = attrs["text"].sub(/,.*$/, "").gsub(":", "_") # special char
76
+ create_anchor(parent, "hidden=#{t}", type: :xref, target: target)
77
+ end
78
+ end
79
+
80
+ class SourceIncludeInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
81
+ use_dsl
82
+ named :source_include
83
+
84
+ def process(_parent, target, _attrs)
85
+ "<source-include path='#{target}'/>"
78
86
  end
79
87
  end
80
88
  end
@@ -15,8 +15,13 @@ module Metanorma
15
15
  end
16
16
  end
17
17
 
18
- def self.run(umlfile, outfile)
19
- system "#{plantuml_bin} #{umlfile.path}" or (warn $? and return false)
18
+ def self.run(umlfile, outfile, fmt)
19
+ valid_formats = %w[png svg eps]
20
+ unless valid_formats.include?(fmt)
21
+ raise ArgumentError, "Invalid format: #{fmt}. Allowed formats are: #{valid_formats.join(', ')}"
22
+ end
23
+ system(plantuml_bin, umlfile.path, "-t#{fmt}") or
24
+ (warn $? and return false)
20
25
  i = 0
21
26
  until !Gem.win_platform? || File.exist?(outfile) || i == 15
22
27
  sleep(1)
@@ -34,16 +39,24 @@ module Metanorma
34
39
  # Windows Ruby 2.4 will crash if a Tempfile is "mv"ed.
35
40
  # This is why we need to copy and then unlink.
36
41
  def self.generate_file(parent, reader)
37
- ldir = localdir(parent)
38
- imagesdir = parent.document.attr("imagesdir")
39
- umlfile, outfile = save_plantuml parent, reader, ldir
40
- run(umlfile, outfile) or
42
+ ldir, imagesdir, fmt = generate_file_prep(parent)
43
+ umlfile, outfile = save_plantuml parent, reader, ldir, fmt
44
+ run(umlfile, outfile, fmt) or
41
45
  raise "No image output from PlantUML (#{umlfile}, #{outfile})!"
42
46
  umlfile.unlink
43
- path = path_prep(ldir, imagesdir)
47
+ absolute_path, relative_path = path_prep(ldir, imagesdir)
44
48
  filename = File.basename(outfile.to_s)
45
- FileUtils.cp(outfile, path) and outfile.unlink
46
- imagesdir ? filename : File.join(path, filename)
49
+ FileUtils.cp(outfile, absolute_path) and outfile.unlink
50
+ #imagesdir ? filename : File.join(path, filename)
51
+ File.join(relative_path, filename)
52
+ end
53
+
54
+ def self.generate_file_prep(parent)
55
+ ldir = localdir(parent)
56
+ imagesdir = parent.document.attr("imagesdir")
57
+ fmt = parent.document.attr("plantuml-image-format")&.strip&.downcase ||
58
+ "png"
59
+ [ldir, imagesdir, fmt]
47
60
  end
48
61
 
49
62
  def self.localdir(parent)
@@ -54,21 +67,22 @@ module Metanorma
54
67
  end
55
68
 
56
69
  def self.path_prep(localdir, imagesdir)
57
- path = Pathname.new(localdir) + (imagesdir || "plantuml")
70
+ #path = Pathname.new(localdir) + (imagesdir || "plantuml")
71
+ path = Pathname.new(File.join(localdir, "_plantuml_images"))
72
+ sourcepath = imagesdir ? File.join(localdir, imagesdir) : localdir
58
73
  path.mkpath
59
74
  File.writable?(path) or
60
75
  raise "Destination path #{path} not writable for PlantUML!"
61
- # File.exist?(path) or raise "Destination path #{path} already exists for PlantUML!"
62
- path
76
+ [path, Pathname.new(path).relative_path_from(Pathname.new(sourcepath)).to_s]
63
77
  end
64
78
 
65
- def self.save_plantuml(_parent, reader, _localdir)
79
+ def self.save_plantuml(_parent, reader, _localdir, fmt)
66
80
  src = prep_source(reader)
67
81
  /^@startuml (?<fn>[^\n]+)\n/ =~ src
68
82
  Tempfile.open(["plantuml", ".pml"], encoding: "utf-8") do |f|
69
83
  f.write(src)
70
84
  [f, File.join(File.dirname(f.path),
71
- "#{fn || File.basename(f.path, '.pml')}.png")]
85
+ "#{fn || File.basename(f.path, '.pml')}.#{fmt}")]
72
86
  end
73
87
  end
74
88
 
@@ -131,8 +131,8 @@ module Metanorma
131
131
 
132
132
  def refitem_render(xml, match, code)
133
133
  xml.bibitem **attr_code(
134
- id: match[:anchor], suppress_identifier: code[:dropid],
135
- hidden: code[:hidden]
134
+ anchor: match[:anchor], suppress_identifier: code[:dropid],
135
+ hidden: code[:hidden], id: "_#{UUIDTools::UUID.random_create}"
136
136
  ) do |t|
137
137
  refitem_render_formattedref(t, match[:text])
138
138
  yr_match = refitem1yr(code[:id])
@@ -76,7 +76,7 @@ module Metanorma
76
76
  @log.add("Bibliography", nil,
77
77
  "ERROR: No title retrieved for #{code}")
78
78
  !fmt and
79
- xml.root << "<title>#{title || '(MISSING TITLE)'}</title>"
79
+ xml.root << "<title>#{title || '(MISSING TITLE)'}</title>"
80
80
  end
81
81
  fmt and xml.root << "<formattedref>#{title}</formattedref>"
82
82
  end
@@ -32,7 +32,8 @@ module Metanorma
32
32
  end
33
33
 
34
34
  def use_my_anchor(ref, id, opt)
35
- ref.parent.elements.last["id"] = id
35
+ ref.parent.elements.last["anchor"] = id
36
+ add_id(ref.parent.elements.last)
36
37
  a = opt[:hidden] and ref.parent.elements.last["hidden"] = a
37
38
  a = opt[:dropid] and
38
39
  ref.parent.elements.last["suppress_identifier"] = a
@@ -195,8 +196,8 @@ module Metanorma
195
196
 
196
197
  def ref_attributes(match)
197
198
  code = analyse_ref_code(match[:code])
198
-
199
- { id: match[:anchor], type: "standard",
199
+ { anchor: match[:anchor], id: "_#{UUIDTools::UUID.random_create}",
200
+ type: "standard",
200
201
  suppress_identifier: code[:dropid] || nil }
201
202
  end
202
203
 
@@ -11,13 +11,12 @@ module Metanorma
11
11
  @norm_ref = false
12
12
 
13
13
  def section_attributes(node)
14
- ret =
15
- { id: Metanorma::Utils::anchor_or_uuid(node),
16
- unnumbered: node.option?("unnumbered") ? "true" : nil,
17
- annex: role_style(node, "appendix") && node.level == 1 ? true : nil,
14
+ ret = id_unnum_attrs(node).merge(
15
+ { annex: role_style(node, "appendix") && node.level == 1 ? true : nil,
18
16
  colophon: role_style(node, "colophon") ? true : nil,
19
- preface: role_style(node, "preface") ? true : nil }
20
- %w(language script number branch-number type tag keeptitle
17
+ preface: role_style(node, "preface") ? true : nil },
18
+ )
19
+ %w(language script branch-number type tag keeptitle
21
20
  multilingual-rendering).each do |k|
22
21
  a = node.attr(k) and ret[k.to_sym] = a
23
22
  end
@@ -36,8 +35,8 @@ module Metanorma
36
35
  a = section_attributes(node)
37
36
  noko do |xml|
38
37
  case sectiontype(node)
39
- when "misc-container", "metanorma-extension"
40
- misccontainer_parse(a, xml, node)
38
+ when "metanorma-extension"
39
+ metanorma_extension_parse(a, xml, node)
41
40
  when "introduction" then introduction_parse(a, xml, node)
42
41
  when "foreword" then foreword_parse(a, xml, node)
43
42
  when "scope" then scope_parse(a, xml, node)
@@ -97,8 +96,8 @@ module Metanorma
97
96
  end
98
97
  end
99
98
 
100
- def misccontainer_parse(_attrs, xml, node)
101
- xml.send :"misc-container-clause" do |xml_section|
99
+ def metanorma_extension_parse(_attrs, xml, node)
100
+ xml.send :"metanorma-extension-clause" do |xml_section|
102
101
  xml_section << node.content
103
102
  end
104
103
  end
@@ -14,7 +14,9 @@ module Metanorma
14
14
  def sectiontype(node, level = true)
15
15
  ret = sectiontype1(node)
16
16
  ret1 = preface_main_filter(sectiontype_streamline(ret), node)
17
- ret1 == "symbols and abbreviated terms" and return ret1
17
+ # permit multiple instances
18
+ ["symbols and abbreviated terms",
19
+ "metanorma-extension"].include?(ret1) and return ret1
18
20
  !level || node.level == 1 || node.attr("heading") or return nil
19
21
  !node.attr("heading") && @seen_headers.include?(ret) and return nil
20
22
  @seen_headers << ret unless ret1.nil?
@@ -22,31 +24,37 @@ module Metanorma
22
24
  ret1
23
25
  end
24
26
 
27
+ SECTIONTYPE_STREAMLINE =
28
+ { "terms and definitions":
29
+ ["terms, definitions, symbols and abbreviated terms",
30
+ "terms, definitions, symbols and abbreviations",
31
+ "terms, definitions and symbols",
32
+ "terms, definitions and abbreviations",
33
+ "terms, definitions and abbreviated terms",
34
+ "terms and definitions"],
35
+ "symbols and abbreviated terms":
36
+ ["symbols and abbreviated terms",
37
+ "symbols", "abbreviated terms", "abbreviations",
38
+ "symbols and abbreviations",
39
+ "symbols and abbreviated terms"],
40
+ "acknowledgements":
41
+ ["acknowledgements", "acknowledgments"],
42
+ "executivesummary":
43
+ ["executive summary", "executive-summary",
44
+ "executive_summary"],
45
+ "metanorma-extension":
46
+ ["misc-container", "metanorma-extension"] }.freeze
47
+
25
48
  def sectiontype_streamline(ret)
26
- case ret
27
- when "terms and definitions",
28
- "terms, definitions, symbols and abbreviated terms",
29
- "terms, definitions, symbols and abbreviations",
30
- "terms, definitions and symbols",
31
- "terms, definitions and abbreviations",
32
- "terms, definitions and abbreviated terms"
33
- "terms and definitions"
34
- when "symbols and abbreviated terms",
35
- "symbols", "abbreviated terms", "abbreviations",
36
- "symbols and abbreviations"
37
- "symbols and abbreviated terms"
38
- when "acknowledgements", "acknowledgments"
39
- "acknowledgements"
40
- when "executive summary", "executive-summary", "executive_summary"
41
- "executivesummary"
42
- else
43
- ret
49
+ SECTIONTYPE_STREAMLINE.each do |k, v|
50
+ v.include?(ret) and return k.to_s
44
51
  end
52
+ ret
45
53
  end
46
54
 
47
55
  PREFACE_CLAUSE_NAMES =
48
56
  %w(abstract foreword introduction metanorma-extension termdocsource
49
- misc-container metanorma-extension acknowledgements executivesummary)
57
+ metanorma-extension acknowledgements executivesummary)
50
58
  .freeze
51
59
 
52
60
  MAIN_CLAUSE_NAMES =
@@ -3,11 +3,8 @@ module Metanorma
3
3
  module Table
4
4
  def table_attrs(node)
5
5
  keep_attrs(node)
6
- .merge(id: Metanorma::Utils::anchor_or_uuid(node),
7
- headerrows: node.attr("headerrows"),
8
- unnumbered: node.option?("unnumbered") ? "true" : nil,
9
- number: node.attr("number"),
10
- subsequence: node.attr("subsequence"),
6
+ .merge(id_unnum_attrs(node))
7
+ .merge(headerrows: node.attr("headerrows"),
11
8
  alt: node.attr("alt"),
12
9
  summary: node.attr("summary"),
13
10
  width: node.attr("width"))
@@ -28,11 +25,9 @@ module Metanorma
28
25
  private
29
26
 
30
27
  def colgroup(node, xml_table)
31
- return if node.option? "autowidth"
32
-
33
- cols = node&.attr("cols")&.split(/,/) or return
34
- return unless (cols.size > 1) && cols.all? { |c| /\d/.match(c) }
35
-
28
+ node.option? "autowidth" and return
29
+ cols = node.attr("cols")&.split(",") or return
30
+ (cols.size > 1) && cols.all? { |c| /\d/.match(c) } or return
36
31
  xml_table.colgroup do |cg|
37
32
  node.columns.each do |col|
38
33
  cg.col width: "#{col.attr 'colpcwidth'}%"
@@ -57,9 +52,10 @@ module Metanorma
57
52
  end
58
53
 
59
54
  def table_cell(node, xml_tr, tblsec)
60
- cell_attributes =
61
- { id: node.id, colspan: node.colspan, valign: node.attr("valign"),
62
- rowspan: node.rowspan, align: node.attr("halign") }
55
+ cell_attributes = id_attr(node).merge(
56
+ { colspan: node.colspan, valign: node.attr("valign"),
57
+ rowspan: node.rowspan, align: node.attr("halign") },
58
+ )
63
59
  cell_tag = "td"
64
60
  cell_tag = "th" if tblsec == :head || node.style == :header
65
61
  xml_tr.send cell_tag, **attr_code(cell_attributes) do |thd|
@@ -16,7 +16,7 @@ module Metanorma
16
16
  @unique_designs = {}
17
17
  @c = HTMLEntities.new
18
18
  @terms_tags = xmldoc.xpath("//terms").each_with_object({}) do |t, m|
19
- m[t["id"]] = true
19
+ m[t["anchor"]] = true
20
20
  end
21
21
  end
22
22
 
@@ -29,6 +29,7 @@ module Metanorma
29
29
  related_cleanup
30
30
  remove_missing_refs
31
31
  concept_cleanup2
32
+ anchor_to_id
32
33
  end
33
34
 
34
35
  private
@@ -36,7 +37,7 @@ module Metanorma
36
37
  def unique_designators
37
38
  ret = xmldoc
38
39
  .xpath("//preferred/expression/name | //admitted/expression/name | " \
39
- "//deprecated/expression/name").each_with_object({}) do |n, m|
40
+ "//deprecates/expression/name").each_with_object({}) do |n, m|
40
41
  m[n.text] ||= 0
41
42
  m[n.text] += 1
42
43
  end
@@ -69,9 +70,9 @@ module Metanorma
69
70
  end
70
71
 
71
72
  def populate_idhash
72
- xmldoc.xpath("//*[@id]").each_with_object({}) do |n, mem|
73
- /^(term|symbol)-/.match?(n["id"]) or next
74
- mem[n["id"]] = true
73
+ xmldoc.xpath("//*[@anchor]").each_with_object({}) do |n, mem|
74
+ /^(term|symbol)-/.match?(n["anchor"]) or next
75
+ mem[n["anchor"]] = true
75
76
  end
76
77
  end
77
78
 
@@ -207,18 +208,18 @@ module Metanorma
207
208
 
208
209
  def norm_id_memorize_init(node, res_table, selector, prefix, use_domain)
209
210
  term_text = norm_ref_id(node, selector, use_domain) or return
210
- unless AUTO_GEN_ID_REGEXP.match(node["id"]).nil? && !node["id"].nil?
211
+ unless AUTO_GEN_ID_REGEXP.match(node["anchor"]).nil? && !node["anchor"].nil?
211
212
  id = unique_text_id(term_text, prefix)
212
- node["id"] = id
213
+ node["anchor"] = id
213
214
  @idhash[id] = true
214
215
  end
215
- res_table[term_text] = node["id"]
216
+ res_table[term_text] = node["anchor"]
216
217
  end
217
218
 
218
219
  def memorize_other_pref_terms(node, res_table, text_selector, use_domain)
219
220
  node.xpath(text_selector).each_with_index do |p, i|
220
221
  i.positive? or next
221
- res_table[norm_ref_id1(p, use_domain ? node : nil)] = node["id"]
222
+ res_table[norm_ref_id1(p, use_domain ? node : nil)] = node["anchor"]
222
223
  end
223
224
  end
224
225
 
@@ -255,6 +256,13 @@ module Metanorma
255
256
  end
256
257
  end
257
258
 
259
+ def anchor_to_id
260
+ xmldoc.xpath("//*[@anchor]").each do |n|
261
+ /^(term|symbol)-/.match?(n["anchor"]) or next
262
+ n["id"] or add_id(n)
263
+ end
264
+ end
265
+
258
266
  include ::Metanorma::Standoc::Utils
259
267
  end
260
268
  end
@@ -157,7 +157,7 @@ module Metanorma
157
157
  def termsource(node)
158
158
  matched = extract_termsource_refs(node.content, node) or return
159
159
  noko do |xml|
160
- xml.termsource **termsource_attrs(node, matched) do |xml_t|
160
+ xml.source **termsource_attrs(node, matched) do |xml_t|
161
161
  seen_xref = Nokogiri::XML.fragment(matched[:xref])
162
162
  add_term_source(node, xml_t, seen_xref, matched)
163
163
  end
@@ -35,6 +35,10 @@ module Metanorma
35
35
  Metanorma::Utils::attr_code(attributes)
36
36
  end
37
37
 
38
+ def add_id(node)
39
+ node["id"] = "_#{UUIDTools::UUID.random_create}"
40
+ end
41
+
38
42
  def csv_split(text, delim = ";")
39
43
  Metanorma::Utils::csv_split(@c.decode(text), delim)
40
44
  .map { |x| @c.encode(x, :basic, :hexadecimal) }
@@ -12,7 +12,7 @@ module Metanorma
12
12
  module Validate
13
13
  def content_validate(doc)
14
14
  @doctype = doc.at("//bibdata/ext/doctype")&.text
15
- repeat_id_validate(doc.root) # feeds xref_validate
15
+ repeat_id_validate(doc.root) # feeds xref_validate, termsect_validate
16
16
  xref_validate(doc) # feeds nested_asset_validate
17
17
  nested_asset_validate(doc)
18
18
  section_validate(doc)
@@ -92,7 +92,7 @@ module Metanorma
92
92
  end
93
93
 
94
94
  def nested_asset_xref_report(outer, inner, _doc)
95
- i = @doc_xrefs[inner["id"]] or return
95
+ i = @doc_xrefs[inner["anchor"]] or return
96
96
  err2 = "There is a crossreference to an instance of #{inner.name} " \
97
97
  "nested within #{outer.name}: #{i.to_xml}"
98
98
  @log.add("Style", i, err2)
@@ -158,37 +158,63 @@ module Metanorma
158
158
  schema_validate(formattedstr_strip(doc.dup), schema_location)
159
159
  end
160
160
 
161
+ # Check should never happen with content ids, but will check it anyway
162
+ # since consequences are so catastrophic
161
163
  def repeat_id_validate1(elem)
162
164
  if @doc_ids[elem["id"]]
163
165
  @log.add("Anchors", elem,
164
- "Anchor #{elem['id']} has already been " \
165
- "used at line #{@doc_ids[elem['id']]}", severity: 0)
166
+ "ID #{elem['id']} has already been " \
167
+ "used at line #{@doc_ids[elem['id']][:line]}", severity: 0)
166
168
  else
167
- @doc_ids[elem["id"]] = elem.line
169
+ @doc_ids[elem["id"]] =
170
+ { line: elem.line, anchor: elem["anchor"] }.compact
168
171
  end
169
172
  end
170
173
 
174
+ def repeat_anchor_validate1(elem)
175
+ if @doc_anchors[elem["anchor"]]
176
+ @log.add("Anchors", elem,
177
+ "Anchor #{elem['anchor']} has already been used at line " \
178
+ "#{@doc_anchors[elem['anchor']][:line]}", severity: 0)
179
+ else
180
+ @doc_anchors[elem["anchor"]] = { line: elem.line, id: elem["id"] }
181
+ @doc_anchor_seq << elem["anchor"]
182
+ end
183
+ end
184
+
185
+ # Check should never happen with content ids, but will check it anyway
171
186
  def repeat_id_validate(doc)
172
- @doc_ids = {} # hash of all ids in document to line number
173
- @doc_id_seq = [] # ordered list of all ids in document
187
+ repeat_id_validate_prep
174
188
  doc.xpath("//*[@id]").each do |x|
175
189
  @doc_id_seq << x["id"]
176
190
  repeat_id_validate1(x)
191
+ x["anchor"] and repeat_anchor_validate1(x)
177
192
  end
178
- @doc_id_seq.sort!
193
+ @doc_id_seq_hash = @doc_id_seq.each_with_index
194
+ .with_object({}) do |(x, i), m|
195
+ m[x] = i
196
+ end
197
+ @doc_anchor_seq_hash = @doc_anchor_seq.each_with_index
198
+ .with_object({}) do |(x, i), m|
199
+ m[x] = i
200
+ end
201
+ end
202
+
203
+ def repeat_id_validate_prep
204
+ @doc_ids = {} # hash of all ids in document to line number, anchor
205
+ @doc_anchors = {} # hash of all anchors in document to line number, id
206
+ @doc_id_seq = [] # ordered list of all ids in document
207
+ @doc_anchor_seq = [] # ordered list of all anchors in document
179
208
  end
180
209
 
181
- # Retrieve IDs between two nominated values
210
+ # Retrieve anchors between two nominated values
182
211
  # (exclusive of start_id AND exclusive of end_id)
183
- def get_ids_between(start_id, end_id)
184
- start_index = @doc_id_seq.bsearch_index { |id| id > start_id }
185
- end_index = @doc_id_seq.bsearch_index { |id| id >= end_id }
186
- # start_id is greater than or equal to all elements
187
- start_index.nil? and return []
188
- # end_id is greater than all elements
189
- end_index.nil? and end_index = @doc_id_seq.length
212
+ def get_anchors_between(start_id, end_id)
213
+ start_index = @doc_anchor_seq_hash[start_id]
214
+ end_index = @doc_anchor_seq_hash[end_id]
215
+ start_index.nil? || end_index.nil? and return []
190
216
  start_index >= end_index and return []
191
- @doc_id_seq[start_index...end_index]
217
+ @doc_anchor_seq[start_index...end_index]
192
218
  end
193
219
 
194
220
  # manually check for xref/@target et sim. integrity
@@ -198,14 +224,15 @@ module Metanorma
198
224
  end
199
225
 
200
226
  def xref_validate_exists(doc)
201
- @doc_xrefs =
202
- doc.xpath("//xref/@target | //xref//location/@target | //index/@to")
203
- .each_with_object({}) do |x, m|
204
- m[x.text] = x.parent
205
- @doc_ids[x.text] and next
227
+ @doc_xrefs = {}
228
+ Metanorma::Utils::anchor_attributes.each do |a|
229
+ doc.xpath("//#{a[0]}/@#{a[1]}").each do |x|
230
+ @doc_xrefs[x.text] = x.parent
231
+ @doc_anchors[x.text] and next
206
232
  @log.add("Anchors", x.parent,
207
233
  "Crossreference target #{x} is undefined", severity: 1)
208
234
  end
235
+ end
209
236
  end
210
237
 
211
238
  # If there is an xref range, record the IDs between the two targets
@@ -220,7 +247,7 @@ module Metanorma
220
247
  from = to_location.previous_element
221
248
  from && from.name == "location" or return
222
249
  from["target"] && to_location["target"] or return
223
- get_ids_between(from["target"], to_location["target"])
250
+ get_anchors_between(from["target"], to_location["target"])
224
251
  .each { |id| @doc_xrefs[id] = from }
225
252
  end
226
253
  end
@@ -99,6 +99,8 @@ module Metanorma
99
99
  doc.xpath("//m:svg", "m" => SVG_NS).each { |n| n.replace("<svg/>") }
100
100
  doc
101
101
  end
102
+
103
+ include ::Metanorma::Standoc::Utils
102
104
  end
103
105
  end
104
106
  end