metanorma-standoc 2.8.11 → 2.9.1

Sign up to get free protection for your applications and to get access to all the features.
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),