metanorma-iso 3.1.3 → 3.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -94,17 +94,6 @@ module Metanorma
94
94
  style_problem_words(node, text)
95
95
  end
96
96
 
97
- # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-s-quantity
98
- def style_subscript(node)
99
- warning = "may contain nested subscripts (max 3 levels allowed)"
100
- node.xpath(".//sub[.//sub]").each do |x|
101
- style_warning(node, warning, x.to_xml)
102
- end
103
- node.xpath(".//m:msub[.//m:msub]", "m" => MATHML_NS).each do |x|
104
- style_warning(node, warning, x.to_xml)
105
- end
106
- end
107
-
108
97
  # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-s-need
109
98
  # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-s-might
110
99
  # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-s-family
@@ -120,44 +109,6 @@ module Metanorma
120
109
  style_regex(/\b(?<num>billions?)\b/i, "ambiguous number", node, text)
121
110
  end
122
111
 
123
- # ISO/IEC DIR 2, 9.1
124
- # ISO/IEC DIR 2, Table B.1
125
- # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-n-numbers
126
- def style_number(node, text)
127
- style_number_grouping(node, text)
128
- style_regex(/(?:^|\p{Zs})(?<num>[0-9]+\.[0-9]+)(?!\.[0-9])/i,
129
- "possible decimal point: mark up numbers with stem:[]", node, text)
130
- @lang == "en" and style_regex(/\b(?<num>billions?)\b/i,
131
- "ambiguous number", node, text)
132
- style_regex(/(?:^|\p{Zs})(?<num>-[0-9][0-9,.]*)/i,
133
- "hyphen instead of minus sign U+2212", node, text)
134
- @novalid_number = true
135
- end
136
-
137
- def style_number_grouping(node, text)
138
- if @validate_years
139
- style_two_regex_not_prev(
140
- node, text, /^(?<num>-?[0-9]{4,}[,0-9]*)\Z/,
141
- %r{\b(ISO|IEC|IEEE|(in|January|February|March|April|May|June|August|September|October|November|December)\b)\Z},
142
- "number not broken up in threes: mark up numbers with stem:[]"
143
- )
144
- else
145
- style_two_regex_not_prev(
146
- node, text, /^(?<num>-?(?:[0-9]{5,}[,0-9]*|[03-9]\d\d\d|1[0-8]\d\d|2[1-9]\d\d|20[5-9]\d))\Z/,
147
- %r{\b(ISO|IEC|IEEE|\b)\Z},
148
- "number not broken up in threes: mark up numbers with stem:[]"
149
- )
150
- end
151
- end
152
-
153
- # ISO/IEC DIR 2, 9.2.1
154
- def style_percent(node, text)
155
- style_regex(/\b(?<num>[0-9.,]+%)/,
156
- "no space before percent sign", node, text)
157
- style_regex(/\b(?<num>[0-9.,]+ \u00b1 [0-9,.]+ %)/,
158
- "unbracketed tolerance before percent sign", node, text)
159
- end
160
-
161
112
  # ISO/IEC DIR 2, 8.4
162
113
  # ISO/IEC DIR 2, 9.3
163
114
  def style_abbrev(node, text)
@@ -168,34 +119,6 @@ module Metanorma
168
119
  "language-specific abbreviation", node, text)
169
120
  end
170
121
 
171
- # leaving out as problematic: N J K C S T H h d B o E
172
- SI_UNIT = "(m|cm|mm|km|μm|nm|g|kg|mgmol|cd|rad|sr|Hz|Hz|MHz|Pa|hPa|kJ|" \
173
- "V|kV|W|MW|kW|F|μF|Ω|Wb|°C|lm|lx|Bq|Gy|Sv|kat|l|t|eV|u|Np|Bd|" \
174
- "bit|kB|MB|Hart|nat|Sh|var)".freeze
175
-
176
- # ISO/IEC DIR 2, 9.3
177
- def style_units(node, text)
178
- style_regex(/\b(?<num>[0-9][0-9,]*\p{Zs}+[\u00b0\u2032\u2033])/,
179
- "space between number and degrees/minutes/seconds",
180
- node, text)
181
- style_regex(/\b(?<num>[0-9][0-9,]*#{SI_UNIT})\b/o,
182
- "no space between number and SI unit", node, text)
183
- style_non_std_units(node, text)
184
- end
185
-
186
- NONSTD_UNITS = {
187
- sec: "s", mins: "min", hrs: "h", hr: "h", cc: "cm^3",
188
- lit: "l", amp: "A", amps: "A", rpm: "r/min"
189
- }.freeze
190
-
191
- # ISO/IEC DIR 2, 9.3
192
- def style_non_std_units(node, text)
193
- NONSTD_UNITS.each do |k, v|
194
- style_regex(/\b(?<num>[0-9][0-9,]*\p{Zs}+#{k})\b/,
195
- "non-standard unit (should be #{v})", node, text)
196
- end
197
- end
198
-
199
122
  # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-p-and
200
123
  # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-p-andor
201
124
  def style_punct(node, text)
@@ -0,0 +1,130 @@
1
+ module Metanorma
2
+ module Iso
3
+ class Converter < Standoc::Converter
4
+ # ISO/IEC DIR 2, 15.5.3, 20.2
5
+ # does not deal with preceding text marked up
6
+ def see_xrefs_validate(root)
7
+ @lang == "en" or return
8
+ anchors = extract_anchor_norm(root)
9
+ root.xpath("//xref").each do |t|
10
+ preceding = t.at("./preceding-sibling::text()[last()]")
11
+ !preceding.nil? &&
12
+ /\b(see| refer to)\p{Zs}*\Z/mi.match(preceding) or next
13
+ anchors[t["target"]] and
14
+ @log.add("Style", t,
15
+ "'see #{t['target']}' is pointing to a normative section")
16
+ end
17
+ end
18
+
19
+ def extract_anchor_norm(root)
20
+ nodes = root.xpath("//annex[@obligation = 'normative'] | " \
21
+ "//references[@obligation = 'normative']")
22
+ ret = nodes.each_with_object({}) do |n, m|
23
+ n["anchor"] and m[n["anchor"]] = true
24
+ end
25
+ nodes.each do |n|
26
+ n.xpath(".//*[@anchor]").each { |n1| ret[n1["anchor"]] = true }
27
+ end
28
+ ret
29
+ end
30
+
31
+ # ISO/IEC DIR 2, 15.5.3
32
+ def see_erefs_validate(root)
33
+ @lang == "en" or return
34
+ bibitemids = extract_bibitem_anchors(root)
35
+ root.xpath("//eref").each do |t|
36
+ prec = t.at("./preceding-sibling::text()[last()]")
37
+ !prec.nil? && /\b(see|refer to)\p{Zs}*\Z/mi.match(prec) or next
38
+ unless target = bibitemids[t["bibitemid"]]
39
+ # unless target = root.at("//bibitem[@anchor = '#{t['bibitemid']}']")
40
+ @log.add("Bibliography", t,
41
+ "'#{t} is not pointing to a real reference")
42
+ next
43
+ end
44
+ target[:norm] and
45
+ @log.add("Style", t,
46
+ "'see #{t}' is pointing to a normative reference")
47
+ end
48
+ end
49
+
50
+ def extract_bibitem_anchors(root)
51
+ ret = root.xpath("//references[@normative = 'true']//bibitem")
52
+ .each_with_object({}) do |b, m|
53
+ m[b["anchor"]] = { bib: b, norm: true }
54
+ end
55
+ root.xpath("//references[not(@normative = 'true')]//bibitem")
56
+ .each do |b|
57
+ ret[b["anchor"]] = { bib: b, norm: false }
58
+ end
59
+ ret
60
+ end
61
+
62
+ # ISO/IEC DIR 2, 10.4
63
+ def locality_erefs_validate(root)
64
+ root.xpath("//eref[descendant::locality]").each do |t|
65
+ if /^(ISO|IEC)/.match?(t["citeas"]) &&
66
+ !/: ?(\d+{4}|–)$/.match?(t["citeas"])
67
+ @log.add("Style", t,
68
+ "undated reference #{t['citeas']} should not contain " \
69
+ "specific elements")
70
+ end
71
+ end
72
+ end
73
+
74
+ # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-r-ref_clause3
75
+ def term_xrefs_validate(xmldoc)
76
+ termids = xmldoc
77
+ .xpath("//sections/terms | //sections/clause[.//terms] | " \
78
+ "//annex[.//terms]").each_with_object({}) do |t, m|
79
+ t.xpath(".//*/@anchor").each { |a| m[a.text] = true }
80
+ t.xpath(".//*/@id").each { |a| m[a.text] = true }
81
+ t.name == "terms" and m[t["anchor"] || t["id"]] = true
82
+ end
83
+ xmldoc.xpath(".//xref").each do |x|
84
+ term_xrefs_validate1(x, termids)
85
+ end
86
+ end
87
+
88
+ def term_xrefs_validate1(xref, termids)
89
+ closest_id = xref.xpath("./ancestor::*[@id]")&.last or return
90
+ termids[xref["target"]] && !termids[closest_id["id"]] and
91
+ @log.add("Style", xref,
92
+ "only terms clauses can cross-reference terms clause " \
93
+ "(#{xref['target']})")
94
+ !termids[xref["target"]] && termids[closest_id["id"]] and
95
+ @log.add("Style", xref,
96
+ "non-terms clauses cannot cross-reference terms clause " \
97
+ "(#{xref['target']})")
98
+ end
99
+
100
+ # require that all assets of a particular type be cross-referenced
101
+ # within the document
102
+ def xrefs_mandate_validate(xmldoc)
103
+ xrefs_mandate_validate1(xmldoc, "//annex", "Annex")
104
+ xrefs_mandate_validate1(xmldoc, "//table", "Table")
105
+ xrefs_mandate_validate1(xmldoc, "//figure", "Figure")
106
+ xrefs_mandate_validate1(xmldoc, "//formula", "Formula")
107
+ end
108
+
109
+ def xrefs_mandate_validate1(xmldoc, xpath, name)
110
+ exc = %w(table note example figure).map { |x| "//#{x}#{xpath}" }
111
+ .join(" | ")
112
+ (xmldoc.xpath(xpath) - xmldoc.xpath(exc)).each do |x|
113
+ x["unnumbered"] == "true" and next
114
+ @doc_xrefs[x["anchor"]] or
115
+ @log.add("Style", x, "#{name} #{x['anchor']} has not been " \
116
+ "cross-referenced within document",
117
+ severity: xpath == "//formula" ? 2 : 1)
118
+ end
119
+ end
120
+
121
+ def iso_xref_validate(doc)
122
+ see_xrefs_validate(doc)
123
+ term_xrefs_validate(doc)
124
+ xrefs_mandate_validate(doc)
125
+ see_erefs_validate(doc)
126
+ locality_erefs_validate(doc)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -1,5 +1,5 @@
1
1
  module Metanorma
2
2
  module Iso
3
- VERSION = "3.1.3".freeze
3
+ VERSION = "3.1.5".freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metanorma-iso
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.3
4
+ version: 3.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-18 00:00:00.000000000 Z
11
+ date: 2025-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: metanorma-standoc
@@ -376,12 +376,13 @@ files:
376
376
  - lib/metanorma/iso/reqt.rng
377
377
  - lib/metanorma/iso/section.rb
378
378
  - lib/metanorma/iso/validate.rb
379
- - lib/metanorma/iso/validate_image.rb
380
379
  - lib/metanorma/iso/validate_list.rb
380
+ - lib/metanorma/iso/validate_numeric.rb
381
381
  - lib/metanorma/iso/validate_requirements.rb
382
382
  - lib/metanorma/iso/validate_section.rb
383
383
  - lib/metanorma/iso/validate_style.rb
384
384
  - lib/metanorma/iso/validate_title.rb
385
+ - lib/metanorma/iso/validate_xref.rb
385
386
  - lib/metanorma/iso/version.rb
386
387
  - lib/metanorma/requirements/modspec.rb
387
388
  - lib/metanorma/requirements/requirements.rb
@@ -1,97 +0,0 @@
1
- module Metanorma
2
- module Iso
3
- class Converter < Standoc::Converter
4
- # DRG directives 3.7; but anticipated by standoc
5
- def subfigure_validate(xmldoc)
6
- elems = { footnote: "fn", note: "note", key: "dl" }
7
- xmldoc.xpath("//figure//figure").each do |f|
8
- elems.each do |k, v|
9
- f.xpath(".//#{v}").each do |n|
10
- @log.add("Style", n, "#{k} is not permitted in a subfigure")
11
- end
12
- end
13
- end
14
- end
15
-
16
- def image_name_prefix(xmldoc)
17
- std = xmldoc.at("//bibdata/ext/structuredidentifier/project-number") or
18
- return
19
- num = xmldoc.at("//bibdata/docnumber")&.text or return
20
- ed = xmldoc.at("//bibdata/edition")&.text || "1"
21
- prefix = num
22
- std["part"] and prefix += "-#{std['part']}"
23
- prefix += "_ed#{ed}"
24
- amd = std["amendment"] and prefix += "amd#{amd}"
25
- prefix
26
- end
27
-
28
- def image_name_suffix(xmldoc)
29
- case xmldoc.at("//bibdata/language")&.text
30
- when "fr" then "_f"
31
- when "de" then "_d"
32
- when "ru" then "_r"
33
- when "es" then "_s"
34
- when "ar" then "_a"
35
- # when "en" then "_e"
36
- else
37
- "_e"
38
- end
39
- end
40
-
41
- def disjunct_error(img, cond1, cond2, msg1, msg2)
42
- cond1 && !cond2 and
43
- @log.add("Style", img, "image name #{img['src']} #{msg1}")
44
- !cond1 && cond2 and
45
- @log.add("Style", img, "image name #{img['src']} #{msg2}")
46
- end
47
-
48
- def image_name_parse(img, prefix)
49
- m = %r[(SL)?#{prefix}fig(?<tab>Tab)?(?<annex>[A-Z])?(Text)?(?<num>\d+)
50
- (?<subfig>[a-z])?(?<key>_key\d+)?(?<lang>_[a-z])?$]x
51
- .match(File.basename(img["src"], ".*"))
52
- m.nil? and
53
- @log.add("Style", img,
54
- "image name #{img['src']} does not match DRG requirements")
55
- m
56
- end
57
-
58
- def image_name_validate1(i, prefix)
59
- m = image_name_parse(i, prefix) or return
60
- warn i["src"]
61
- disjunct_error(i, i.at("./ancestor::table"), !m[:tab].nil?,
62
- "is under a table but is not so labelled",
63
- "is labelled as under a table but is not")
64
- disjunct_error(i, i.at("./ancestor::annex"), !m[:annex].nil?,
65
- "is under an annex but is not so labelled",
66
- "is labelled as under an annex but is not")
67
- disjunct_error(i, i.xpath("./ancestor::figure").size > 1, !m[:subfig].nil?,
68
- "does not have a subfigure letter but is a subfigure",
69
- "has a subfigure letter but is not a subfigure")
70
- lang = image_name_suffix(i.document.root)
71
- (m[:lang] || "_e") == lang or
72
- @log.add("Style", i,
73
- "image name #{i['src']} expected to have suffix #{lang}")
74
- end
75
-
76
- # DRG directives 3.2
77
- def image_name_validate(xmldoc)
78
- prefix = image_name_prefix(xmldoc) or return
79
- xmldoc.xpath("//image").each do |i|
80
- i["src"].start_with?("data:") and next
81
- case File.basename(i["src"])
82
- when /^ISO_\d+_/
83
- when /^(SL)?#{prefix}fig/ then image_name_validate1(i, prefix)
84
- else
85
- @log.add("Style", i,
86
- "image name #{i['src']} does not match DRG requirements: expect #{prefix}fig")
87
- end
88
- end
89
- end
90
-
91
- def figure_validate(xmldoc)
92
- image_name_validate(xmldoc)
93
- subfigure_validate(xmldoc)
94
- end
95
- end
96
- end
97
- end