metanorma-standoc 1.4.3 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ubuntu.yml +1 -1
  3. data/.rubocop.yml +1 -1
  4. data/Rakefile +2 -0
  5. data/lib/asciidoctor/standoc/base.rb +5 -4
  6. data/lib/asciidoctor/standoc/base_structured_text_preprocessor.rb +123 -0
  7. data/lib/asciidoctor/standoc/basicdoc.rng +31 -1
  8. data/lib/asciidoctor/standoc/cleanup.rb +2 -0
  9. data/lib/asciidoctor/standoc/cleanup_amend.rb +54 -0
  10. data/lib/asciidoctor/standoc/cleanup_block.rb +0 -2
  11. data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +11 -24
  12. data/lib/asciidoctor/standoc/cleanup_footnotes.rb +0 -3
  13. data/lib/asciidoctor/standoc/cleanup_inline.rb +62 -1
  14. data/lib/asciidoctor/standoc/cleanup_ref.rb +8 -4
  15. data/lib/asciidoctor/standoc/cleanup_section.rb +91 -8
  16. data/lib/asciidoctor/standoc/cleanup_terms.rb +12 -2
  17. data/lib/asciidoctor/standoc/converter.rb +3 -3
  18. data/lib/asciidoctor/standoc/front.rb +0 -12
  19. data/lib/asciidoctor/standoc/front_contributor.rb +43 -10
  20. data/lib/asciidoctor/standoc/inline.rb +13 -1
  21. data/lib/asciidoctor/standoc/isodoc.rng +124 -15
  22. data/lib/asciidoctor/standoc/json2_text_preprocessor.rb +43 -0
  23. data/lib/asciidoctor/standoc/log.rb +10 -1
  24. data/lib/asciidoctor/standoc/macros.rb +45 -33
  25. data/lib/asciidoctor/standoc/ref.rb +24 -25
  26. data/lib/asciidoctor/standoc/section.rb +39 -30
  27. data/lib/asciidoctor/standoc/table.rb +3 -2
  28. data/lib/asciidoctor/standoc/utils.rb +18 -1
  29. data/lib/asciidoctor/standoc/validate.rb +30 -18
  30. data/lib/asciidoctor/standoc/validate_section.rb +1 -1
  31. data/lib/asciidoctor/standoc/views/datamodel/model_representation.adoc.erb +10 -10
  32. data/lib/asciidoctor/standoc/yaml2_text_preprocessor.rb +43 -0
  33. data/lib/liquid/custom_blocks/key_iterator.rb +21 -0
  34. data/lib/liquid/custom_filters/values.rb +7 -0
  35. data/lib/metanorma/standoc/version.rb +1 -1
  36. data/metanorma-standoc.gemspec +4 -5
  37. data/spec/asciidoctor-standoc/base_spec.rb +21 -6
  38. data/spec/asciidoctor-standoc/blocks_spec.rb +274 -148
  39. data/spec/asciidoctor-standoc/cleanup_spec.rb +1367 -53
  40. data/spec/asciidoctor-standoc/inline_spec.rb +5 -2
  41. data/spec/asciidoctor-standoc/macros_json2text_spec.rb +10 -0
  42. data/spec/asciidoctor-standoc/macros_spec.rb +43 -23
  43. data/spec/asciidoctor-standoc/macros_yaml2text_spec.rb +5 -560
  44. data/spec/asciidoctor-standoc/refs_dl_spec.rb +9 -7
  45. data/spec/asciidoctor-standoc/refs_spec.rb +16 -16
  46. data/spec/asciidoctor-standoc/section_spec.rb +42 -39
  47. data/spec/asciidoctor-standoc/table_spec.rb +119 -113
  48. data/spec/asciidoctor-standoc/validate_spec.rb +45 -1
  49. data/spec/assets/{html.css → html.scss} +0 -0
  50. data/spec/assets/i18n.yaml +17 -2
  51. data/spec/assets/{word.css → word.scss} +0 -0
  52. data/spec/examples/codes_table.html +1365 -1365
  53. data/spec/fixtures/macros_datamodel/address_class_profile.xml +46 -46
  54. data/spec/fixtures/macros_datamodel/address_component_profile.xml +21 -21
  55. data/spec/fixtures/macros_datamodel/blank_definition_profile.xml +21 -21
  56. data/spec/metanorma/processor_spec.rb +1 -2
  57. data/spec/spec_helper.rb +110 -109
  58. data/spec/support/shared_examples/structured_data_2_text_preprocessor.rb +583 -0
  59. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +66 -66
  60. data/spec/vcr_cassettes/isobib_get_123.yml +16 -16
  61. data/spec/vcr_cassettes/isobib_get_123_1.yml +33 -33
  62. data/spec/vcr_cassettes/isobib_get_123_2001.yml +19 -19
  63. data/spec/vcr_cassettes/isobib_get_124.yml +19 -19
  64. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +8 -8
  65. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +28 -28
  66. metadata +44 -13
  67. data/lib/asciidoctor-yaml/i18n-en.yaml +0 -20
  68. data/lib/asciidoctor-yaml/i18n-fr.yaml +0 -13
  69. data/lib/asciidoctor-yaml/i18n-zh-Hans.yaml +0 -15
  70. data/lib/asciidoctor/standoc/i18n.rb +0 -39
  71. data/lib/asciidoctor/standoc/macros_yaml2text.rb +0 -165
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac225a257c1a5c42ce296ee87ef1d95e378111a82875211969771f7d1756bcf2
4
- data.tar.gz: 30ff55c905559089be61b2752cb84c8274a121b4e5359a8cdf547528296a09be
3
+ metadata.gz: 7503bfc7dd68c2d6a6b2ded8f08611e92067a15aa4088c02f1e5408ff8e027e6
4
+ data.tar.gz: 65cd5d4d3648788113fd30437fe8d649c6e498b9ebdab8d5b41d2fadb7439056
5
5
  SHA512:
6
- metadata.gz: e66997f17221a04e57e213a66e16ce0061060c5ffd0708bc9196b2b75ec8081673475890b8745086341196bb30a7832ca7f3c1d5615bd80063e31f6f9e243534
7
- data.tar.gz: 1a1e23d189dbb0ef46552e5953cd8568c83b6380d239a68cfab95e8a1cb975583e7697bc6d11babb509ca5a21f9f3c7d4d06e225f8faef39f2c7f3d9d8087f9c
6
+ metadata.gz: 73510f7d926c6acf395377794a753cef6bb0f0658fb66c606df07e23548c17f010c7120b77765b388fb363a4309051fcf03a601d6ea95cf20aca6e4645b1c674
7
+ data.tar.gz: bae732755cc47dcae17e0d8ef01bcd35173c8941807b2d5844cf45f008f57c199977756d4fd15b0e3c8c558e73432dd9ba88be2b01f61b32cff180308adcd83d
@@ -43,7 +43,7 @@ jobs:
43
43
  polling_interval_seconds: 5
44
44
  timeout_minutes: 5
45
45
  max_attempts: 3
46
- command: sudo bash -c "curl -L https://github.com/metanorma/plantuml-install/raw/master/ubuntu.sh | bash"
46
+ command: sudo apt-get update -y && sudo bash -c "curl -L https://github.com/metanorma/plantuml-install/raw/master/ubuntu.sh | bash"
47
47
  - name: Run specs
48
48
  run: |
49
49
  bundle exec rake
@@ -5,6 +5,6 @@
5
5
  inherit_from:
6
6
  - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
7
7
  AllCops:
8
- TargetRubyVersion: 2.3
8
+ TargetRubyVersion: 2.6
9
9
  Rails:
10
10
  Enabled: true
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require 'isodoc/gem_tasks'
3
4
 
5
+ IsoDoc::GemTasks.install
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
8
  task default: :spec
@@ -111,9 +111,10 @@ module Asciidoctor
111
111
  @log = Asciidoctor::Standoc::Log.new
112
112
  init_bib_caches(node)
113
113
  init_iev_caches(node)
114
- lang = (node.attr("language") || "en")
115
- script = (node.attr("script") || "en")
116
- i18n_init(lang, script)
114
+ @lang = (node.attr("language") || "en")
115
+ @script = (node.attr("script") || "Latn")
116
+ @isodoc = isodoc(@lang, @script, node.attr("i18nyaml"))
117
+ @i18n = @isodoc.i18n
117
118
  end
118
119
 
119
120
  def default_fonts(node)
@@ -169,7 +170,7 @@ module Asciidoctor
169
170
  end
170
171
 
171
172
  def doctype(node)
172
- node.attr("doctype")
173
+ node.attr("doctype")&.gsub(/\s+/, "-")&.downcase
173
174
  end
174
175
 
175
176
  def front(node, xml)
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "liquid/custom_blocks/key_iterator"
4
+ require "liquid/custom_filters/values"
5
+
6
+ Liquid::Template.register_tag("keyiterator", Liquid::CustomBlocks::KeyIterator)
7
+ Liquid::Template.register_filter(Liquid::CustomFilters)
8
+
9
+ module Asciidoctor
10
+ module Standoc
11
+ # Base class for processing structured data blocks(yaml, json)
12
+ class BaseStructuredTextPreprocessor < Asciidoctor::Extensions::Preprocessor
13
+ BLOCK_START_REGEXP = /\{(.+?)\.\*,(.+),(.+)\}/
14
+ BLOCK_END_REGEXP = /\A\{[A-Z]+\}\z/
15
+
16
+ def process(document, reader)
17
+ input_lines = reader.readlines.to_enum
18
+ Reader.new(processed_lines(document, input_lines))
19
+ end
20
+
21
+ protected
22
+
23
+ def content_from_file(_document, _file_path)
24
+ raise ArgumentError, "Implement `content_from_file` in your class"
25
+ end
26
+
27
+ private
28
+
29
+ def processed_lines(document, input_lines)
30
+ result = []
31
+ loop do
32
+ result.push(*process_text_blocks(document, input_lines))
33
+ end
34
+ result
35
+ end
36
+
37
+ def relative_file_path(document, file_path)
38
+ docfile_directory = File.dirname(document.attributes["docfile"] || ".")
39
+ document
40
+ .path_resolver
41
+ .system_path(file_path, docfile_directory)
42
+ end
43
+
44
+ def process_text_blocks(document, input_lines)
45
+ line = input_lines.next
46
+ block_match = line.match(/^\[#{config[:block_name]},(.+?),(.+?)\]/)
47
+ return [line] if block_match.nil?
48
+
49
+ mark = input_lines.next
50
+ current_block = []
51
+ while (block_line = input_lines.next) != mark
52
+ current_block.push(block_line)
53
+ end
54
+ read_content_and_parse_template(document,
55
+ current_block,
56
+ block_match)
57
+ end
58
+
59
+ def read_content_and_parse_template(document, current_block, block_match)
60
+ transformed_liquid_lines = current_block
61
+ .map(&method(:transform_line_liquid))
62
+ context_items = content_from_file(document, block_match[1])
63
+ parse_context_block(document: document,
64
+ context_lines: transformed_liquid_lines,
65
+ context_items: context_items,
66
+ context_name: block_match[2])
67
+ rescue StandardError => exception
68
+ document.logger
69
+ .warn("Failed to parse #{config[:block_name]} \
70
+ block: #{exception.message}")
71
+ []
72
+ end
73
+
74
+ def transform_line_liquid(line)
75
+ if line.match?(BLOCK_START_REGEXP)
76
+ line.gsub!(BLOCK_START_REGEXP,
77
+ '{% keyiterator \1, \2 %}')
78
+ end
79
+
80
+ if line.strip.match?(BLOCK_END_REGEXP)
81
+ line.gsub!(BLOCK_END_REGEXP, "{% endkeyiterator %}")
82
+ end
83
+ line
84
+ .gsub(/(?<!{){(?!%)([^{}]+)(?<!%)}(?!})/, '{{\1}}')
85
+ .gsub(/[a-z\.]+\#/, "index")
86
+ .gsub(/{{(.+)\s+\+\s+(\d+)\s*?}}/, '{{ \1 | plus: \2 }}')
87
+ .gsub(/{{(.+)\s+\-\s+(\d+)\s*?}}/, '{{ \1 | minus: \2 }}')
88
+ .gsub(/{{(.+).values(.*?)}}/,
89
+ '{% assign custom_value = \1 | values %}{{custom_value\2}}')
90
+ end
91
+
92
+ def parse_context_block(context_lines:,
93
+ context_items:,
94
+ context_name:,
95
+ document:)
96
+ render_result, errors = render_liquid_string(
97
+ template_string: context_lines.join("\n"),
98
+ context_items: context_items,
99
+ context_name: context_name
100
+ )
101
+ notify_render_errors(document, errors)
102
+ render_result.split("\n")
103
+ end
104
+
105
+ def render_liquid_string(template_string:, context_items:, context_name:)
106
+ liquid_template = Liquid::Template.parse(template_string)
107
+ rendered_string = liquid_template
108
+ .render(context_name => context_items,
109
+ strict_variables: true,
110
+ error_mode: :warn)
111
+ [rendered_string, liquid_template.errors]
112
+ end
113
+
114
+ def notify_render_errors(document, errors)
115
+ errors.each do |error_obj|
116
+ document
117
+ .logger
118
+ .warn("Liquid render error: #{error_obj.message}")
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -158,7 +158,17 @@
158
158
  <data type="ID"/>
159
159
  </attribute>
160
160
  <oneOrMore>
161
- <ref name="paragraph"/>
161
+ <choice>
162
+ <ref name="formula"/>
163
+ <ref name="ul"/>
164
+ <ref name="ol"/>
165
+ <ref name="dl"/>
166
+ <ref name="quote"/>
167
+ <ref name="sourcecode"/>
168
+ <ref name="paragraph"/>
169
+ <ref name="table"/>
170
+ <ref name="figure"/>
171
+ </choice>
162
172
  </oneOrMore>
163
173
  </element>
164
174
  </define>
@@ -402,6 +412,16 @@
402
412
  </choice>
403
413
  </attribute>
404
414
  </optional>
415
+ <optional>
416
+ <attribute name="valign">
417
+ <choice>
418
+ <value>top</value>
419
+ <value>middle</value>
420
+ <value>bottom</value>
421
+ <value>baseline</value>
422
+ </choice>
423
+ </attribute>
424
+ </optional>
405
425
  <choice>
406
426
  <zeroOrMore>
407
427
  <ref name="TextElement"/>
@@ -429,6 +449,16 @@
429
449
  </choice>
430
450
  </attribute>
431
451
  </optional>
452
+ <optional>
453
+ <attribute name="valign">
454
+ <choice>
455
+ <value>top</value>
456
+ <value>middle</value>
457
+ <value>bottom</value>
458
+ <value>baseline</value>
459
+ </choice>
460
+ </attribute>
461
+ </optional>
432
462
  <choice>
433
463
  <zeroOrMore>
434
464
  <ref name="TextElement"/>
@@ -9,6 +9,7 @@ require_relative "./cleanup_boilerplate.rb"
9
9
  require_relative "./cleanup_section.rb"
10
10
  require_relative "./cleanup_terms.rb"
11
11
  require_relative "./cleanup_inline.rb"
12
+ require_relative "./cleanup_amend.rb"
12
13
  require "relaton_iev"
13
14
 
14
15
  module Asciidoctor
@@ -71,6 +72,7 @@ module Asciidoctor
71
72
  para_cleanup(xmldoc)
72
73
  empty_element_cleanup(xmldoc)
73
74
  img_cleanup(xmldoc)
75
+ anchor_cleanup(xmldoc)
74
76
  xmldoc
75
77
  end
76
78
 
@@ -0,0 +1,54 @@
1
+ module Asciidoctor
2
+ module Standoc
3
+ module Cleanup
4
+ def change_clauses(x)
5
+ x.xpath("//clause[@change]").each do |c|
6
+ a = create_amend(c)
7
+ end
8
+ end
9
+
10
+ def create_amend(c)
11
+ a = c.add_child("<amend id='_#{UUIDTools::UUID.random_create}'/>").first
12
+ c.elements.each do |e|
13
+ e.parent = a unless %w(amend title).include? e.name
14
+ end
15
+ create_amend1(c, a)
16
+ end
17
+
18
+ def create_amend1(c, a)
19
+ create_amend2(c, a)
20
+ d = a.at("./description")
21
+ d.xpath(".//autonumber").each { |e| d.previous = e }
22
+ d.xpath(".//p[normalize-space(.)='']").each { |e| e.remove }
23
+ move_attrs_to_amend(c, a)
24
+ a
25
+ end
26
+
27
+ def create_amend2(c, a)
28
+ q = a.at("./quote") and q.name = "newcontent"
29
+ if q.nil?
30
+ a.children = "<description>#{a.children.to_xml}</description>"
31
+ else
32
+ pre = q&.xpath("./preceding-sibling::*")&.remove
33
+ post = q&.xpath("./following-sibling::*")&.remove
34
+ pre.empty? or a << "<description>#{pre.to_xml}</description>"
35
+ a << q.remove
36
+ post.empty? or a << "<description>#{post.to_xml}</description>"
37
+ end
38
+ end
39
+
40
+ def move_attrs_to_amend(c, a)
41
+ %w(change path path_end title).each do |e|
42
+ next unless c[e]
43
+ a[e] = c[e]
44
+ c.delete(e)
45
+ end
46
+ return unless a["locality"]
47
+ loc = a.children.add_previous_sibling("<location/>")
48
+ extract_localities1(loc, a["locality"])
49
+ loc1 = loc.at("./localityStack") and loc.replace(loc1.elements)
50
+ a.delete("locality")
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,8 +1,6 @@
1
1
  require "date"
2
- require "nokogiri"
3
2
  require "htmlentities"
4
3
  require "json"
5
- require "pathname"
6
4
  require "open-uri"
7
5
 
8
6
  module Asciidoctor
@@ -2,25 +2,25 @@ module Asciidoctor
2
2
  module Standoc
3
3
  module Cleanup
4
4
  def external_terms_boilerplate(sources)
5
- IsoDoc::Function::I18n::l10n(
6
- @external_terms_boilerplate.gsub(/%/, sources || "???"),
5
+ @i18n.l10n(
6
+ @i18n.external_terms_boilerplate.gsub(/%/, sources || "???"),
7
7
  @lang, @script)
8
8
  end
9
9
 
10
10
  def internal_external_terms_boilerplate(sources)
11
- IsoDoc::Function::I18n::l10n(
12
- @internal_external_terms_boilerplate.gsub(/%/, sources || "??"),
11
+ @i18n.l10n(
12
+ @i18n.internal_external_terms_boilerplate.gsub(/%/, sources || "??"),
13
13
  @lang, @script)
14
14
  end
15
15
 
16
16
  def term_defs_boilerplate(div, source, term, preface, isodoc)
17
- div.next = @term_def_boilerplate
17
+ a = @i18n.term_def_boilerplate and div.next = a
18
18
  source.each do |s|
19
19
  @anchors[s["bibitemid"]] or
20
20
  @log.add("Crossreferences", nil, "term source #{s['bibitemid']} not referenced")
21
21
  end
22
22
  if source.empty? && term.nil?
23
- div.next = @no_terms_boilerplate
23
+ div.next = @i18n.no_terms_boilerplate
24
24
  else
25
25
  div.next = term_defs_boilerplate_cont(source, term, isodoc)
26
26
  end
@@ -30,7 +30,7 @@ module Asciidoctor
30
30
  sources = isodoc.sentence_join(src.map do |s|
31
31
  %{<eref bibitemid="#{s['bibitemid']}"/>}
32
32
  end)
33
- if src.empty? then @internal_terms_boilerplate
33
+ if src.empty? then @i18n.internal_terms_boilerplate
34
34
  elsif term.nil? then external_terms_boilerplate(sources)
35
35
  else
36
36
  internal_external_terms_boilerplate(sources)
@@ -42,7 +42,7 @@ module Asciidoctor
42
42
  ["reference", "bibitem"].include? e.name
43
43
  end
44
44
  f.at("./title").next =
45
- "<p>#{(refs.empty? ? @norm_empty_pref : @norm_with_refs_pref)}</p>"
45
+ "<p>#{(refs.empty? ? @i18n.norm_empty_pref : @i18n.norm_with_refs_pref)}</p>"
46
46
  end
47
47
 
48
48
  TERM_CLAUSE = "//sections/terms | "\
@@ -54,13 +54,9 @@ module Asciidoctor
54
54
  x = xmldoc.dup
55
55
  x.root.add_namespace(nil, self.class::XML_NAMESPACE)
56
56
  xml = Nokogiri::XML(x.to_xml)
57
- conv = html_converter(EmptyAttr.new)
58
- @lang = xmldoc&.at("//bibdata/language")&.text
59
- @script = xmldoc&.at("//bibdata/script")&.text
60
- conv.i18n_init(@lang, @script)
61
- conv.metadata_init(@lang, @script, conv.labels)
62
- conv.info(xml, nil)
63
- conv
57
+ @isodoc ||= isodoc(@lang, @script)
58
+ @isodoc.info(xml, nil)
59
+ @isodoc
64
60
  end
65
61
 
66
62
  def boilerplate_cleanup(xmldoc)
@@ -83,15 +79,6 @@ module Asciidoctor
83
79
  preface.previous = b
84
80
  end
85
81
 
86
- class EmptyAttr
87
- def attr(_x)
88
- nil
89
- end
90
- def attributes
91
- {}
92
- end
93
- end
94
-
95
82
  def boilerplate_file(xmldoc)
96
83
  File.join(@libdir, "boilerplate.xml")
97
84
  end
@@ -1,10 +1,7 @@
1
1
  require "date"
2
- require "nokogiri"
3
2
  require "htmlentities"
4
3
  require "json"
5
- require "pathname"
6
4
  require "open-uri"
7
- require "pp"
8
5
 
9
6
  module Asciidoctor
10
7
  module Standoc
@@ -50,6 +50,10 @@ module Asciidoctor
50
50
 
51
51
  def extract_localities(x)
52
52
  text = x&.children&.first&.remove&.text
53
+ extract_localities1(x, text)
54
+ end
55
+
56
+ def extract_localities1(x, text)
53
57
  b = x.add_child("<localityStack/>").first if LOCALITY_RE.match text
54
58
  while (m = LOCALITY_RE.match text)
55
59
  ref = m[:ref] ? "<referenceFrom>#{tq m[:ref]}</referenceFrom>" : ""
@@ -117,7 +121,8 @@ module Asciidoctor
117
121
  def concept_termbase_cleanup(x)
118
122
  text = x&.children&.first&.remove&.text
119
123
  termbase, key = x["key"].split(/:/, 2)
120
- x.add_child(%(<termref base="#{termbase}" target="#{key}">#{text}</termref>))
124
+ x.add_child(%(<termref base="#{termbase}" target="#{key}">) +
125
+ "#{text}</termref>")
121
126
  end
122
127
 
123
128
  def concept_xref_cleanup(x)
@@ -129,6 +134,62 @@ module Asciidoctor
129
134
  x.children = "<eref>#{x.children.to_xml}</eref>"
130
135
  extract_localities(x.first_element_child)
131
136
  end
137
+
138
+ NAMECHAR = "\u0000-\u0022\u0024\u002c\u002f\u003a-\u0040\\u005b-\u005e"\
139
+ "\u0060\u007b-\u00b6\u00b8-\u00bf\u00d7\u00f7\u037e\u2000-\u200b"\
140
+ "\u200e-\u203e\u2041-\u206f\u2190-\u2bff\u2ff0-\u3000".freeze
141
+ #"\ud800-\uf8ff\ufdd0-\ufdef\ufffe-\uffff".freeze
142
+ NAMESTARTCHAR = "\\u002d\u002e\u0030-\u0039\u00b7\u0300-\u036f"\
143
+ "\u203f-\u2040".freeze
144
+
145
+ def to_ncname(s)
146
+ start = s[0]
147
+ ret1 = %r([#{NAMECHAR}#]).match(start) ? "_" :
148
+ (%r([#{NAMESTARTCHAR}#]).match(start) ? "_#{start}" : start)
149
+ ret2 = s[1..-1] || ""
150
+ ret = (ret1 || "") + ret2.gsub(%r([#{NAMECHAR}#]), "_")
151
+ ret
152
+ end
153
+
154
+ def to_xreftarget(s)
155
+ return to_ncname(s) unless /^[^#]+#.+$/.match(s)
156
+ /^(?<pref>[^#]+)#(?<suff>.+)$/ =~ s
157
+ pref = pref.gsub(%r([#{NAMECHAR}]), "_")
158
+ suff = suff.gsub(%r([#{NAMECHAR}]), "_")
159
+ "#{pref}##{suff}"
160
+ end
161
+
162
+ IDREF = "//*/@id | //review/@from | //review/@to | "\
163
+ "//callout/@target | //citation/@bibitemid | //eref/@bibitemid".freeze
164
+
165
+ def anchor_cleanup(x)
166
+ anchor_cleanup1(x)
167
+ xreftarget_cleanup(x)
168
+ end
169
+
170
+ def anchor_cleanup1(x)
171
+ x.xpath(IDREF).each do |s|
172
+ if (ret = to_ncname(s.value)) != (orig = s.value)
173
+ s.value = ret
174
+ output = s.parent.dup
175
+ output.children.remove
176
+ @log.add("Anchors", s.parent, "normalised identifier in #{output} "\
177
+ "from #{orig}")
178
+ end
179
+ end
180
+ end
181
+
182
+ def xreftarget_cleanup(x)
183
+ x.xpath("//xref/@target").each do |s|
184
+ if (ret = to_xreftarget(s.value)) != (orig = s.value)
185
+ s.value = ret
186
+ output = s.parent.dup
187
+ output.children.remove
188
+ @log.add("Anchors", s.parent, "normalised identifier in #{output} "\
189
+ "from #{orig}")
190
+ end
191
+ end
192
+ end
132
193
  end
133
194
  end
134
195
  end