metanorma-standoc 2.8.11 → 2.9.1

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/lib/metanorma/standoc/anchor.rb +3 -3
  4. data/lib/metanorma/standoc/base.rb +1 -1
  5. data/lib/metanorma/standoc/blocks.rb +5 -5
  6. data/lib/metanorma/standoc/blocks_image.rb +1 -1
  7. data/lib/metanorma/standoc/blocks_notes.rb +6 -6
  8. data/lib/metanorma/standoc/cleanup.rb +15 -0
  9. data/lib/metanorma/standoc/cleanup_block.rb +1 -1
  10. data/lib/metanorma/standoc/cleanup_boilerplate.rb +67 -31
  11. data/lib/metanorma/standoc/cleanup_maths.rb +24 -1
  12. data/lib/metanorma/standoc/cleanup_ref.rb +0 -1
  13. data/lib/metanorma/standoc/cleanup_section.rb +5 -9
  14. data/lib/metanorma/standoc/cleanup_section_names.rb +5 -5
  15. data/lib/metanorma/standoc/cleanup_terms.rb +6 -5
  16. data/lib/metanorma/standoc/cleanup_xref.rb +6 -10
  17. data/lib/metanorma/standoc/converter.rb +1 -0
  18. data/lib/metanorma/standoc/init.rb +23 -2
  19. data/lib/metanorma/standoc/inline.rb +5 -5
  20. data/lib/metanorma/standoc/isodoc.rng +5 -5
  21. data/lib/metanorma/standoc/lists.rb +4 -4
  22. data/lib/metanorma/standoc/macros.rb +16 -44
  23. data/lib/metanorma/standoc/macros_embed.rb +14 -12
  24. data/lib/metanorma/standoc/macros_inline.rb +38 -75
  25. data/lib/metanorma/standoc/macros_link.rb +81 -0
  26. data/lib/metanorma/standoc/ref.rb +9 -12
  27. data/lib/metanorma/standoc/ref_queue.rb +2 -4
  28. data/lib/metanorma/standoc/ref_utility.rb +2 -2
  29. data/lib/metanorma/standoc/section.rb +3 -3
  30. data/lib/metanorma/standoc/terms.rb +2 -2
  31. data/lib/metanorma/standoc/utils.rb +22 -2
  32. data/lib/metanorma/standoc/validate.rb +14 -2
  33. data/lib/metanorma/standoc/validate_table.rb +7 -0
  34. data/lib/metanorma/standoc/version.rb +1 -1
  35. data/metanorma-standoc.gemspec +5 -4
  36. metadata +24 -9
@@ -41,7 +41,7 @@ module Metanorma
41
41
  list_caption(node, xml_ul)
42
42
  node.items.each { |item| ul_li(xml_ul, item) }
43
43
  end
44
- end.join("\n")
44
+ end.join("")
45
45
  end
46
46
 
47
47
  def olist_style(style)
@@ -67,7 +67,7 @@ module Metanorma
67
67
  list_caption(node, xml_ol)
68
68
  node.items.each { |item| li(xml_ol, item) }
69
69
  end
70
- end.join("\n")
70
+ end.join("")
71
71
  end
72
72
 
73
73
  def dt(terms, xml_dl)
@@ -107,7 +107,7 @@ module Metanorma
107
107
  dd(dd, xml_dl)
108
108
  end
109
109
  end
110
- end.join("\n")
110
+ end.join("")
111
111
  end
112
112
 
113
113
  def colist(node)
@@ -117,7 +117,7 @@ module Metanorma
117
117
  xml_li.p { |p| p << item.text }
118
118
  end
119
119
  end
120
- end.join("\n")
120
+ end.join("")
121
121
  end
122
122
 
123
123
  def list_caption(node, out)
@@ -7,6 +7,7 @@ require_relative "macros_terms"
7
7
  require_relative "macros_form"
8
8
  require_relative "macros_note"
9
9
  require_relative "macros_embed"
10
+ require_relative "macros_link"
10
11
  require_relative "datamodel/attributes_table_preprocessor"
11
12
  require_relative "datamodel/diagram_preprocessor"
12
13
  require "metanorma-plugin-datastruct"
@@ -52,13 +53,19 @@ module Metanorma
52
53
  class NamedEscapePreprocessor < Asciidoctor::Extensions::Preprocessor
53
54
  def process(document, reader)
54
55
  c = HTMLEntities.new
56
+ p = Metanorma::Utils::LineStatus.new
55
57
  lines = reader.lines.map do |l|
56
- l.split(/(&[A-Za-z][^&;]*;)/).map do |s|
57
- /^&[A-Za-z]/.match?(s) ? c.encode(c.decode(s), :hexadecimal) : s
58
- end.join
58
+ p.process(l)
59
+ p.pass ? l : convert(l, c)
59
60
  end
60
61
  ::Asciidoctor::PreprocessorReader.new document, lines
61
62
  end
63
+
64
+ def convert(line, esc)
65
+ line.split(/(&[A-Za-z][^&;]*;)/).map do |s|
66
+ /^&[A-Za-z]/.match?(s) ? esc.encode(esc.decode(s), :hexadecimal) : s
67
+ end.join
68
+ end
62
69
  end
63
70
 
64
71
  class ColumnBreakBlockMacro < Asciidoctor::Extensions::BlockMacroProcessor
@@ -74,51 +81,16 @@ module Metanorma
74
81
  # Not using TreeProcessor because that is still too close to
75
82
  # inline expressions being processed on access (e.g. titles)
76
83
  class LinkProtectPreprocessor < Asciidoctor::Extensions::Preprocessor
77
- def init
78
- pass = true # process as passthrough: init = true until
79
- # hit end of doc header
80
- is_delim = false # current line is a no-substititon block delimiter
81
- pass_delim = false # current line is a passthrough delimiter
82
- delimln = "" # delimiter line of current block(s);
83
- # init value looks for end of doc header
84
- { pass: pass, is_delim: is_delim, pass_delim: pass_delim,
85
- delimln: delimln }
86
- end
87
-
88
84
  def process(document, reader)
89
- p = init
85
+ p = Metanorma::Utils::LineStatus.new
90
86
  lines = reader.lines.map do |t|
91
- p = pass_status(p, t.rstrip)
92
- !p[:pass] && t.include?(":") and t = inlinelinkmacro(inlinelink(t))
87
+ p.process(t)
88
+ !p.pass && t.include?(":") and t = inlinelinkmacro(inlinelink(t))
93
89
  t
94
90
  end
95
91
  ::Asciidoctor::PreprocessorReader.new document, lines
96
92
  end
97
93
 
98
- def pass_status(status, text)
99
- text == "++++" && !status[:delimln] and status[:pass] = !status[:pass]
100
- status[:midline_docattr] && !/^:[^ :]+: /.match?(text) and
101
- status[:midline_docattr] = false
102
- if (status[:is_delim] && /^(-+|\*+|=+|_+)$/.match?(text)) ||
103
- (!status[:is_delim] && !status[:delimln] && /^-----*$|^\.\.\.\.\.*$/.match?(text))
104
- status[:delimln] = text
105
- status[:pass] = true
106
- elsif status[:pass_delim]
107
- status[:delimln] = "" # end of paragraph for paragraph with [pass]
108
- elsif status[:delimln] && text == status[:delimln]
109
- status[:pass] = false
110
- status[:delimln] = nil
111
- elsif /^:[^ :]+: /.match?(text) &&
112
- (status[:prev_line].empty? || status[:midline_docattr])
113
- status[:pass] = true
114
- status[:midline_docattr] = true
115
- end
116
- status[:is_delim] = /^\[(source|listing|literal|pass)\b/.match?(text)
117
- status[:pass_delim] = /^\[(pass)\b/.match?(text)
118
- status[:prev_line] = text.strip
119
- status
120
- end
121
-
122
94
  PASS_INLINE_MACROS = %w(pass pass-format identifier std-link stem)
123
95
  .join("|").freeze
124
96
 
@@ -132,7 +104,7 @@ module Metanorma
132
104
  \\[.*?(?<!\\\\)\\] # [ ... ] not preceded by \\
133
105
  )
134
106
  REGEX
135
- PASS_INLINE_MACRO_RX = /#{PASS_INLINE_MACRO_STR}/xo.freeze
107
+ PASS_INLINE_MACRO_RX = /#{PASS_INLINE_MACRO_STR}/xo
136
108
 
137
109
  def pass_inline_split(text)
138
110
  text.split(PASS_INLINE_MACRO_RX).each.map do |x|
@@ -142,7 +114,7 @@ module Metanorma
142
114
 
143
115
  # InlineLinkRx = %r((^|link:|#{CG_BLANK}|&lt;|[>\(\)\[\];"'])(\\?(?:https?|file|ftp|irc)://)(?:([^\s\[\]]+)\[(|#{CC_ALL}*?[^\\])\]|([^\s\[\]<]*([^\s,.?!\[\]<\)]))))m
144
116
  #
145
- InlineLinkRx = %r((^|(?<![-\\])\blink:(?!\+)|\p{Blank}|&lt;|[<>\(\)\[\];"'])((?:https?|file|ftp|irc)://)(?:([^\s\[\]]+)(?:(\[(|.*?[^\\])\])|([^\s\[\]<]*([^\s,.?!\[\]<\)])))))m.freeze
117
+ InlineLinkRx = %r((^|(?<![-\\])\blink:(?!\+)|\p{Blank}|&lt;|[<>\(\)\[\];"'])((?:https?|file|ftp|irc)://)(?:([^\s\[\]]+)(?:(\[(|.*?[^\\])\])|([^\s\[\]<]*([^\s,.?!\[\]<\)])))))m
146
118
 
147
119
  def inlinelink(text)
148
120
  text.include?("://") or return text
@@ -172,7 +144,7 @@ module Metanorma
172
144
  (|[^:\\s\\[][^\\s\\[]*) # link: ... up to [
173
145
  (\\[(|.*?[^\\\\])\\]) # [ ... ], no ]
174
146
  REGEX
175
- InlineLinkMacroRx = /#{InlineLinkMacroRx1}/x.freeze
147
+ InlineLinkMacroRx = /#{InlineLinkMacroRx1}/x
176
148
 
177
149
  def inlinelinkmacro(text)
178
150
  (text.include?("[") &&
@@ -37,18 +37,19 @@ module Metanorma
37
37
  def process(doc, reader)
38
38
  reader.eof? and return reader
39
39
  r = ::Asciidoctor::PreprocessorNoIfdefsReader.new doc, reader.lines
40
+ p = Metanorma::Utils::LineStatus.new
40
41
  lines = r.readlines
41
42
  headings = lines.grep(/^== /).map(&:strip)
42
43
  ret = lines.each_with_object(embed_acc(doc, r)) do |line, m|
43
- process_line(line, m, headings)
44
+ process_line(line, m, headings, p)
44
45
  end
45
46
  return_to_document(doc, ret)
46
47
  end
47
48
 
48
49
  def embed_acc(doc, reader)
49
50
  { lines: [], hdr: [], id: [],
50
- orig: doc, doc: doc, file: nil, path: nil,
51
- reader: reader, prev: nil }
51
+ orig: doc, doc:, file: nil, path: nil,
52
+ reader:, prev: nil }
52
53
  end
53
54
 
54
55
  # presupposes single embed
@@ -93,13 +94,14 @@ module Metanorma
93
94
 
94
95
  def update_embeds(lines, acc, emb)
95
96
  lines.empty? or
96
- acc << { file: emb[:file], path: emb[:path], lines: lines }
97
+ acc << { file: emb[:file], path: emb[:path], lines: }
97
98
  [[], acc]
98
99
  end
99
100
 
100
- def process_line(line, acc, headings)
101
- if /^embed::/.match?(line)
102
- e = embed(line, acc, headings)
101
+ def process_line(line, acc, headings, status)
102
+ status.process(line)
103
+ if !status.pass && /^embed::/.match?(line)
104
+ e = embed(line, acc, headings, status)
103
105
  acc = process_embed(acc, e, acc[:prev])
104
106
  else
105
107
  acc[:lines] << line
@@ -148,7 +150,7 @@ module Metanorma
148
150
  end
149
151
  end
150
152
 
151
- def embed(line, acc, headings)
153
+ def embed(line, acc, headings, status)
152
154
  fname, inc_path = filename(line, acc)
153
155
  lines = filter_sections(read(inc_path), headings)
154
156
  n = Asciidoctor::Document
@@ -158,12 +160,12 @@ module Metanorma
158
160
  .merge(file: fname, path: inc_path, orig: acc[:orig])
159
161
  ret[:hdr] or
160
162
  raise "Embedding an incomplete document with no header: #{ret[:path]}"
161
- embed_recurse(ret, n, r, headings)
163
+ embed_recurse(ret, n, r, headings, status)
162
164
  end
163
165
 
164
- def embed_recurse(ret, doc, reader, headings)
166
+ def embed_recurse(ret, doc, reader, headings, status)
165
167
  ret1 = ret[:lines].each_with_object(embed_acc(doc, reader)) do |line, m|
166
- process_line(line, m, headings)
168
+ process_line(line, m, headings, status)
167
169
  end
168
170
  ret.merge(
169
171
  { lines: ret1[:lines], id: ret[:id] + ret1[:id],
@@ -172,7 +174,7 @@ module Metanorma
172
174
  end
173
175
 
174
176
  def strip_header(lines)
175
- return { lines: lines, hdr: nil } unless !lines.empty? &&
177
+ return { lines:, hdr: nil } unless !lines.empty? &&
176
178
  lines.first.start_with?("= ")
177
179
 
178
180
  skip = true
@@ -1,3 +1,5 @@
1
+ require_relative "utils"
2
+
1
3
  module Metanorma
2
4
  module Standoc
3
5
  class InheritInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
@@ -12,49 +14,6 @@ module Metanorma
12
14
  end
13
15
  end
14
16
 
15
- class IndexXrefInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
16
- use_dsl
17
- named :index
18
-
19
- def preprocess_attrs(attrs)
20
- ret = { primary: attrs[1], target: attrs[attrs.size] }
21
- ret[:secondary] = attrs[2] if attrs.size > 2
22
- ret[:tertiary] = attrs[3] if attrs.size > 3
23
- ret
24
- end
25
-
26
- def validate(parent, target, attrs)
27
- attrs.size > 1 && attrs.size < 5 and return true
28
- e = "invalid index \"#{target}\" cross-reference: wrong number of " \
29
- "attributes in `index:#{target}[#{attrs.values.join(',')}]`"
30
- parent.converter.log.add("Crossreferences", parent, e, severity: 0)
31
- false
32
- end
33
-
34
- def process(parent, target, attr)
35
- validate(parent, target, attr) or return
36
- args = preprocess_attrs(attr)
37
- ret = "<index-xref also='#{target == 'also'}'>" \
38
- "<primary>#{args[:primary]}</primary>"
39
- ret += "<secondary>#{args[:secondary]}</secondary>" if args[:secondary]
40
- ret += "<tertiary>#{args[:tertiary]}</tertiary>" if args[:tertiary]
41
- ret + "<target>#{args[:target]}</target></index-xref>"
42
- end
43
- end
44
-
45
- class IndexRangeInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
46
- use_dsl
47
- named :"index-range"
48
- parse_content_as :text
49
-
50
- def process(parent, target, attr)
51
- text = attr["text"]
52
- text = "((#{text}))" unless /^\(\(.+\)\)$/.match?(text)
53
- out = parent.sub_macros(text)
54
- out.sub("<index>", "<index to='#{target}'>")
55
- end
56
- end
57
-
58
17
  class HTML5RubyMacro < Asciidoctor::Extensions::InlineMacroProcessor
59
18
  use_dsl
60
19
  named :ruby
@@ -151,23 +110,6 @@ module Metanorma
151
110
  end
152
111
  end
153
112
 
154
- class ToCInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
155
- use_dsl
156
- named :toc
157
- parse_content_as :text
158
- using_format :short
159
-
160
- def process(parent, _target, attrs)
161
- out = Asciidoctor::Inline.new(parent, :quoted, attrs["text"]).convert
162
- content = CSV.parse_line(out).map do |x|
163
- x.sub!(/^(["'])(.+)\1/, "\\2")
164
- m = /^(.*?)(:\d+)?$/.match(x)
165
- %{<toc-xpath depth='#{m[2]&.sub(':', '') || 1}'>#{m[1]}</toc-xpath>}
166
- end.join
167
- "<toc>#{content}</toc>"
168
- end
169
- end
170
-
171
113
  # inject ZWNJ to prevent Asciidoctor from attempting regex substitutions
172
114
  class PassInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
173
115
  use_dsl
@@ -194,32 +136,53 @@ module Metanorma
194
136
  end
195
137
  end
196
138
 
197
- class StdLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
139
+ class SpanInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
198
140
  use_dsl
199
- named :"std-link"
141
+ named :span
200
142
  parse_content_as :text
201
- using_format :short
202
143
 
203
- def process(parent, _target, attrs)
204
- t = attrs["text"]
205
- t = if /,/.match?(t)
206
- t.sub(/,/, "%")
207
- else
208
- "#{t}%"
209
- end
210
- create_anchor(parent, "hidden=#{t}",
211
- type: :xref, target: "_#{UUIDTools::UUID.random_create}")
144
+ def process(parent, target, attrs)
145
+ out = Asciidoctor::Inline.new(parent, :quoted, attrs["text"]).convert
146
+ %{<span class="#{target}">#{out}</span>}
212
147
  end
213
148
  end
214
149
 
215
- class SpanInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
150
+ class NumberInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
151
+ include ::Metanorma::Standoc::Utils
152
+
216
153
  use_dsl
217
- named :span
154
+ named :number
218
155
  parse_content_as :text
219
156
 
157
+ MATHML_NS = "http://www.w3.org/1998/Math/MathML".freeze
158
+
159
+ def unquote(str)
160
+ str.sub(/^(["'])(.+)\1$/, "\\2")
161
+ end
162
+
163
+ def format(attrs)
164
+ # a="," => "a=,"
165
+ quoted_csv_split(attrs || "", ",").map do |x|
166
+ m = /^(.+?)=(.+)?$/.match(x) or next
167
+ "#{m[1]}='#{m[2]}'"
168
+ end.join(",")
169
+ end
170
+
171
+ def number(text)
172
+ n = BigDecimal(text)
173
+ trailing_zeroes = 0
174
+ m = /\.[1-9]*(0+)/.match(text) and trailing_zeroes += m[1].size
175
+ n.to_s("E").sub("e", "0" * trailing_zeroes + "e")
176
+ end
177
+
220
178
  def process(parent, target, attrs)
221
179
  out = Asciidoctor::Inline.new(parent, :quoted, attrs["text"]).convert
222
- %{<span class="#{target}">#{out}</span>}
180
+ fmt = format(out)
181
+ fmt.empty? and fmt = "notation='basic'"
182
+ fmt = %( data-metanorma-numberformat="#{fmt}")
183
+ <<~OUTPUT
184
+ <stem type="MathML"><math xmlns='#{MATHML_NS}'><mn#{fmt}>#{number(target)}</mn></math></stem>
185
+ OUTPUT
223
186
  end
224
187
  end
225
188
  end
@@ -0,0 +1,81 @@
1
+ module Metanorma
2
+ module Standoc
3
+ class IndexXrefInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
4
+ use_dsl
5
+ named :index
6
+
7
+ def preprocess_attrs(attrs)
8
+ ret = { primary: attrs[1], target: attrs[attrs.size] }
9
+ ret[:secondary] = attrs[2] if attrs.size > 2
10
+ ret[:tertiary] = attrs[3] if attrs.size > 3
11
+ ret
12
+ end
13
+
14
+ def validate(parent, target, attrs)
15
+ attrs.size > 1 && attrs.size < 5 and return true
16
+ e = "invalid index \"#{target}\" cross-reference: wrong number of " \
17
+ "attributes in `index:#{target}[#{attrs.values.join(',')}]`"
18
+ parent.converter.log.add("Crossreferences", parent, e, severity: 0)
19
+ false
20
+ end
21
+
22
+ def process(parent, target, attr)
23
+ validate(parent, target, attr) or return
24
+ args = preprocess_attrs(attr)
25
+ ret = "<index-xref also='#{target == 'also'}'>" \
26
+ "<primary>#{args[:primary]}</primary>"
27
+ ret += "<secondary>#{args[:secondary]}</secondary>" if args[:secondary]
28
+ ret += "<tertiary>#{args[:tertiary]}</tertiary>" if args[:tertiary]
29
+ ret + "<target>#{args[:target]}</target></index-xref>"
30
+ end
31
+ end
32
+
33
+ class IndexRangeInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
34
+ use_dsl
35
+ named :"index-range"
36
+ parse_content_as :text
37
+
38
+ def process(parent, target, attr)
39
+ text = attr["text"]
40
+ text = "((#{text}))" unless /^\(\(.+\)\)$/.match?(text)
41
+ out = parent.sub_macros(text)
42
+ out.sub("<index>", "<index to='#{target}'>")
43
+ end
44
+ end
45
+
46
+ class ToCInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
47
+ use_dsl
48
+ named :toc
49
+ parse_content_as :text
50
+ using_format :short
51
+
52
+ def process(parent, _target, attrs)
53
+ out = Asciidoctor::Inline.new(parent, :quoted, attrs["text"]).convert
54
+ content = CSV.parse_line(out).map do |x|
55
+ x.sub!(/^(["'])(.+)\1/, "\\2")
56
+ m = /^(.*?)(:\d+)?$/.match(x)
57
+ %{<toc-xpath depth='#{m[2]&.sub(':', '') || 1}'>#{m[1]}</toc-xpath>}
58
+ end.join
59
+ "<toc>#{content}</toc>"
60
+ end
61
+ end
62
+
63
+ class StdLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
64
+ use_dsl
65
+ named :"std-link"
66
+ parse_content_as :text
67
+ using_format :short
68
+
69
+ def process(parent, _target, attrs)
70
+ t = attrs["text"]
71
+ t = if /,/.match?(t)
72
+ t.sub(/,/, "%")
73
+ else
74
+ "#{t}%"
75
+ end
76
+ create_anchor(parent, "hidden=#{t}",
77
+ type: :xref, target: "_#{UUIDTools::UUID.random_create}")
78
+ end
79
+ end
80
+ end
81
+ end
@@ -27,7 +27,7 @@ module Metanorma
27
27
  def isorefmatchescode(match, _item)
28
28
  code = analyse_ref_code(match[:code])
29
29
  yr = norm_year(match[:year])
30
- { code: match[:code], year: yr, match: match,
30
+ { code: match[:code], year: yr, match:,
31
31
  title: match[:text], usrlbl: match[:usrlbl] || code[:usrlabel],
32
32
  analyse_code: code, lang: @lang || :all }
33
33
  end
@@ -47,7 +47,7 @@ module Metanorma
47
47
  def isorefmatches2code(match, _item)
48
48
  code = analyse_ref_code(match[:code])
49
49
  { code: match[:code], no_year: true, lang: @lang || :all,
50
- note: match[:fn], year: nil, match: match, analyse_code: code,
50
+ note: match[:fn], year: nil, match:, analyse_code: code,
51
51
  title: match[:text], usrlbl: match[:usrlbl] || code[:usrlabel] }
52
52
  end
53
53
 
@@ -77,7 +77,7 @@ module Metanorma
77
77
  code = analyse_ref_code(match[:code])
78
78
  yr = norm_year(match[:year])
79
79
  hasyr = !yr.nil? && yr != "--"
80
- { code: match[:code], match: match, yr: yr, hasyr: hasyr,
80
+ { code: match[:code], match:, yr:, hasyr:,
81
81
  year: hasyr ? yr : nil, lang: @lang || :all,
82
82
  all_parts: true, no_year: yr == "--",
83
83
  title: match[:text], usrlbl: match[:usrlbl] || code[:usrlabel] }
@@ -162,11 +162,11 @@ module Metanorma
162
162
  def refitem1code(_item, match)
163
163
  code = analyse_ref_code(match[:code])
164
164
  ((code[:id] && code[:numeric]) || code[:nofetch]) and
165
- return { code: nil, match: match, analyse_code: code,
165
+ return { code: nil, match:, analyse_code: code,
166
166
  hidden: code[:hidden] }
167
167
  { code: code[:id], analyse_code: code, localfile: code[:localfile],
168
168
  year: (m = refitem1yr(code[:id])) ? m[:year] : nil,
169
- title: match[:text], match: match, hidden: code[:hidden],
169
+ title: match[:text], match:, hidden: code[:hidden],
170
170
  usrlbl: match[:usrlbl] || code[:usrlabel], lang: @lang || :all }
171
171
  end
172
172
 
@@ -187,29 +187,26 @@ module Metanorma
187
187
  ISO_REF =
188
188
  %r{^<ref\sid="(?<anchor>[^"]+)">
189
189
  \[(?<usrlbl>\([^)]+\))?(?<code>(?:ISO|IEC)[^0-9]*\s[0-9-]+|IEV)
190
- (?::(?<year>[0-9][0-9-]+))?\]</ref>,?\s*(?<text>.*)$}xm.freeze
190
+ (?::(?<year>[0-9][0-9-]+))?\]</ref>,?\s*(?<text>.*)$}xm
191
191
 
192
192
  ISO_REF_NO_YEAR =
193
193
  %r{^<ref\sid="(?<anchor>[^"]+)">
194
194
  \[(?<usrlbl>\([^)]+\))?(?<code>(?:ISO|IEC)[^0-9]*\s[0-9-]+):
195
- (?:--|&\#821[12];)\]</ref>,?\s*
195
+ (?:--|–|—|&\#821[12];)\]</ref>,?\s*
196
196
  (?:<fn[^>]*>\s*<p>(?<fn>[^\]]+)</p>\s*</fn>)?,?\s?(?<text>.*)$}xm
197
- .freeze
198
197
 
199
198
  ISO_REF_ALL_PARTS =
200
199
  %r{^<ref\sid="(?<anchor>[^"]+)">
201
200
  \[(?<usrlbl>\([^)]+\))?(?<code>(?:ISO|IEC)[^0-9]*\s[0-9]+)
202
- (?::(?<year>--|&\#821[12];|[0-9][0-9-]+))?\s
201
+ (?::(?<year>--|–|—|&\#821[12];|[0-9][0-9-]+))?\s
203
202
  \(all\sparts\)\]</ref>,?\s*
204
- (?:<fn[^>]*>\s*<p>(?<fn>[^\]]+)</p>\s*</fn>,?\s?)?(?<text>.*)$}xm.freeze
203
+ (?:<fn[^>]*>\s*<p>(?<fn>[^\]]+)</p>\s*</fn>,?\s?)?(?<text>.*)$}xm
205
204
 
206
205
  NON_ISO_REF = %r{^<ref\sid="(?<anchor>[^"]+)">
207
206
  \[(?<usrlbl>\([^)]+\))?(?<code>.+?)\]</ref>,?\s*(?<text>.*)$}xm
208
- .freeze
209
207
 
210
208
  NON_ISO_REF1 = %r{^<ref\sid="(?<anchor>[^"]+)">
211
209
  (?<usrlbl>\([^)]+\))?(?<code>.+?)</ref>,?\s*(?<text>.*)$}xm
212
- .freeze
213
210
 
214
211
  def reference1_matches(item)
215
212
  matched = ISO_REF.match item
@@ -122,16 +122,14 @@ module Metanorma
122
122
  end
123
123
 
124
124
  def local_ievcache_name(cachename)
125
- return nil if cachename.nil?
126
-
125
+ cachename.nil? and return nil
127
126
  cachename += "_iev" unless cachename.empty?
128
127
  cachename = "iev" if cachename.empty?
129
128
  "#{cachename}/cache"
130
129
  end
131
130
 
132
131
  def fetch_ref(xml, code, year, **opts)
133
- return nil if opts[:no_year]
134
-
132
+ opts[:no_year] and return nil
135
133
  code = code.sub(/^\([^)]+\)/, "")
136
134
  hit = fetch_ref1(code, year, opts) or return nil
137
135
  xml.parent.add_child(smart_render_xml(hit, code, opts))
@@ -200,8 +200,8 @@ module Metanorma
200
200
 
201
201
  MALFORMED_REF = <<~REF.freeze
202
202
  no anchor on reference, markup may be malformed: see
203
- https://www.metanorma.com/author/topics/document-format/bibliography/ ,
204
- https://www.metanorma.com/author/iso/topics/markup/#bibliographies
203
+ https://www.metanorma.org/author/topics/sections/bibliography/ ,
204
+ https://www.metanorma.org/author/iso/topics/markup/#bibliographies
205
205
  REF
206
206
 
207
207
  def ref_normalise(ref)
@@ -16,8 +16,7 @@ module Metanorma
16
16
  .gsub(%r{<index>.*?</index>}m, "")
17
17
  .gsub(%r{<fn[^>]*>.*?</fn>}m, "")
18
18
  .gsub(/<[^>]+>/, "")
19
- .strip.downcase
20
- .sub(/\.$/, "")
19
+ .strip.downcase.sub(/\.$/, "")
21
20
  end
22
21
 
23
22
  def sectiontype(node, level = true)
@@ -41,7 +40,8 @@ module Metanorma
41
40
  "terms, definitions and abbreviated terms"
42
41
  "terms and definitions"
43
42
  when "symbols and abbreviated terms",
44
- "symbols", "abbreviated terms", "abbreviations"
43
+ "symbols", "abbreviated terms", "abbreviations",
44
+ "symbols and abbreviations"
45
45
  "symbols and abbreviated terms"
46
46
  when "acknowledgements", "acknowledgments"
47
47
  "acknowledgements"
@@ -170,7 +170,7 @@ module Metanorma
170
170
  seen_xref = Nokogiri::XML.fragment(matched[:xref])
171
171
  add_term_source(node, xml_t, seen_xref, matched)
172
172
  end
173
- end.join("\n")
173
+ end.join("")
174
174
  end
175
175
 
176
176
  def termdefinition(node)
@@ -178,7 +178,7 @@ module Metanorma
178
178
  xml.definition **attr_code(type: node.attr("type")) do |d|
179
179
  d << node.content
180
180
  end
181
- end.join("\n")
181
+ end.join("")
182
182
  end
183
183
  end
184
184
  end
@@ -17,8 +17,8 @@ module Metanorma
17
17
  nil
18
18
  end
19
19
 
20
- def noko(&block)
21
- Metanorma::Utils::noko(@script, &block)
20
+ def noko(&)
21
+ Metanorma::Utils::noko(@script, &)
22
22
  end
23
23
 
24
24
  def attr_code(attributes)
@@ -30,6 +30,26 @@ module Metanorma
30
30
  .map { |x| @c.encode(x, :basic, :hexadecimal) }
31
31
  end
32
32
 
33
+ def quoted_csv_split(text, delim = ",", eql = "=")
34
+ # quoted strings: key="va,lue",
35
+ c = HTMLEntities.new
36
+ text = c.decode(text).gsub(/([a-zA-Z_]+)#{eql}(["'])(.+?)\2/,
37
+ %("\\1#{eql}\\3"))
38
+ Metanorma::Utils::csv_split(text, delim)
39
+ .map do |x|
40
+ c.encode(x.sub(/^(["'])(.+)\1$/, "\\2"), :basic, :hexadecimal)
41
+ end
42
+ end
43
+
44
+ def kv_parse(text, delim = ",", eql = "=")
45
+ text or return {}
46
+ c = HTMLEntities.new
47
+ quoted_csv_split(text, delim).each_with_object({}) do |k, m|
48
+ x = k.split(eql, 2)
49
+ m[x[0]] = c.decode(x[1])
50
+ end
51
+ end
52
+
33
53
  def wrap_in_para(node, out)
34
54
  Metanorma::Utils::wrap_in_para(node, out)
35
55
  end
@@ -48,7 +48,6 @@ module Metanorma
48
48
  def mathml_sanitise(math)
49
49
  math.to_xml(encoding: "US-ASCII").gsub(/ xmlns=["'][^"']+["']/, "")
50
50
  .gsub(%r{<[^:/>]+:}, "<").gsub(%r{</[^:/>]+:}, "</")
51
- # .gsub(/&#([^;]+);/) { |x| "&#x#{$1.to_i.to_s(16)};" }
52
51
  end
53
52
 
54
53
  def math_validate_error(math, elem, error)
@@ -133,7 +132,7 @@ module Metanorma
133
132
  doc.xpath(WILDCARD_ATTRS, "m" => SVG_NS).each do |n|
134
133
  n.elements.each do |e|
135
134
  e.traverse do |e1|
136
- e1.element? and e1.each { |k, _v| e1.delete(k) }
135
+ e1.element? and e1.each { |k, _v| e1.delete(k) } # rubocop:disable Style/HashEachMethods
137
136
  end
138
137
  end
139
138
  end
@@ -143,6 +142,7 @@ module Metanorma
143
142
 
144
143
  def image_validate(doc)
145
144
  image_exists(doc)
145
+ image_toobig(doc)
146
146
  png_validate(doc)
147
147
  end
148
148
 
@@ -183,6 +183,18 @@ module Metanorma
183
183
  "Corrupt PNG image detected: #{e.message}")
184
184
  end
185
185
 
186
+ TOO_BIG_IMG_ERR = <<~ERR.freeze
187
+ Image too large for Data URI encoding: disable Data URI encoding (`:data-uri-image: false`), or set `:data-uri-maxsize: 0`
188
+ ERR
189
+
190
+ def image_toobig(doc)
191
+ @dataurimaxsize.zero? and return
192
+ doc.xpath("//image").each do |i|
193
+ i["src"].size > @dataurimaxsize and
194
+ @log.add("Images", i.parent, TOO_BIG_IMG_ERR, severity: 0)
195
+ end
196
+ end
197
+
186
198
  def validate(doc)
187
199
  content_validate(doc)
188
200
  schema_validate(formattedstr_strip(doc.dup),