metanorma-iso 3.1.3 → 3.1.4
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.
- checksums.yaml +4 -4
- data/lib/isodoc/iso/iso.amendment.xsl +591 -166
- data/lib/isodoc/iso/iso.international-standard.xsl +591 -166
- data/lib/isodoc/iso/word_cleanup.rb +17 -2
- data/lib/metanorma/iso/front_contributor.rb +3 -5
- data/lib/metanorma/iso/isodoc.rng +19 -1
- data/lib/metanorma/iso/validate.rb +18 -126
- data/lib/metanorma/iso/validate_numeric.rb +128 -0
- data/lib/metanorma/iso/validate_style.rb +0 -72
- data/lib/metanorma/iso/validate_xref.rb +130 -0
- data/lib/metanorma/iso/version.rb +1 -1
- metadata +4 -3
- data/lib/metanorma/iso/validate_image.rb +0 -97
@@ -123,7 +123,9 @@ module IsoDoc
|
|
123
123
|
|
124
124
|
def authority_copyright_style(auth)
|
125
125
|
auth.xpath(".//p[not(@class)]").each { |p| p["class"] = "zzCopyright" }
|
126
|
-
auth.xpath(".//p[@class = 'Tablebody']").each
|
126
|
+
auth.xpath(".//p[@class = 'Tablebody']").each do |p|
|
127
|
+
p["class"] = "zzCopyright"
|
128
|
+
end
|
127
129
|
auth.xpath(".//p[@id = 'boilerplate-message']").each do |p|
|
128
130
|
p["class"] = "zzCopyright1"
|
129
131
|
end
|
@@ -163,14 +165,27 @@ module IsoDoc
|
|
163
165
|
toc = ""
|
164
166
|
s = docxml.at("//div[@class = 'TOC']") and toc = to_xml(s.children)
|
165
167
|
xpath = (1..level).each.map { |i| "//h#{i}" }.join (" | ")
|
168
|
+
annexid = 0
|
166
169
|
docxml.xpath(xpath).each do |h|
|
167
170
|
x = ""
|
168
|
-
|
171
|
+
if %w(ANNEX Annex).include?(h["class"])
|
172
|
+
x, annexid = annex_toc(annexid)
|
173
|
+
end
|
169
174
|
toc += word_toc_entry(h.name[1].to_i, x + header_strip(h))
|
170
175
|
end
|
171
176
|
toc.sub(/(<p class="MsoToc1">)/,
|
172
177
|
%{\\1#{word_toc_preface(level)}}) + WORD_TOC_SUFFIX1
|
173
178
|
end
|
179
|
+
|
180
|
+
# do not use in IEC, BSI, where Word does not use list to generate
|
181
|
+
# "Annex A"
|
182
|
+
def annex_toc(annexid)
|
183
|
+
instance_of?(IsoDoc::Iso::WordConvert) ||
|
184
|
+
instance_of?(WordDISConvert) or return ""
|
185
|
+
annexid += 1
|
186
|
+
x = "#{@i18n.annex} #{('@'.ord + annexid).chr} "
|
187
|
+
[x, annexid]
|
188
|
+
end
|
174
189
|
end
|
175
190
|
end
|
176
191
|
end
|
@@ -12,14 +12,12 @@ module Metanorma
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def metadata_author(node, xml)
|
15
|
-
|
16
|
-
{ source: ["publisher", "pub"], role: "author",
|
17
|
-
default: default_publisher })
|
18
|
-
committee_contributors(node, xml, default_publisher,
|
19
|
-
{ approval: false })
|
15
|
+
super
|
20
16
|
secretariat_contributor(node, xml, default_publisher)
|
21
17
|
end
|
22
18
|
|
19
|
+
def personal_author(node, xml); end
|
20
|
+
|
23
21
|
def org_organization(node, xml, org)
|
24
22
|
if org[:committee]
|
25
23
|
contrib_committee_build(xml, org[:agency], org)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
2
|
<grammar xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
3
|
-
<!-- VERSION v2.1.
|
3
|
+
<!-- VERSION v2.1.2 -->
|
4
4
|
|
5
5
|
<!--
|
6
6
|
ALERT: cannot have root comments, because of https://github.com/metanorma/metanorma/issues/437
|
@@ -123,6 +123,10 @@ the type attribute defaults to `review` for reviews</a:documentation>
|
|
123
123
|
<a:documentation>Notes whose scope is the current block</a:documentation>
|
124
124
|
</ref>
|
125
125
|
</zeroOrMore>
|
126
|
+
<ref name="BlockSource">
|
127
|
+
<a:documentation>Bibliographic source for the information in the list.
|
128
|
+
Sources are currently only rendered in metanorma-plateau</a:documentation>
|
129
|
+
</ref>
|
126
130
|
</define>
|
127
131
|
<define name="OlBody">
|
128
132
|
<optional>
|
@@ -140,6 +144,10 @@ the type attribute defaults to `review` for reviews</a:documentation>
|
|
140
144
|
<a:documentation>Notes whose scope is the current block</a:documentation>
|
141
145
|
</ref>
|
142
146
|
</zeroOrMore>
|
147
|
+
<ref name="BlockSource">
|
148
|
+
<a:documentation>Bibliographic source for the information in the list.
|
149
|
+
Sources are currently only rendered in metanorma-plateau</a:documentation>
|
150
|
+
</ref>
|
143
151
|
</define>
|
144
152
|
<define name="OlAttributes">
|
145
153
|
<a:documentation>NOTE: `start` attribute is not included by default, because of problems it raises with DOC output</a:documentation>
|
@@ -188,6 +196,10 @@ the type attribute defaults to `review` for reviews</a:documentation>
|
|
188
196
|
<a:documentation>Notes whose scope is the current block</a:documentation>
|
189
197
|
</ref>
|
190
198
|
</zeroOrMore>
|
199
|
+
<ref name="BlockSource">
|
200
|
+
<a:documentation>Bibliographic source for the information in the list.
|
201
|
+
Sources are currently only rendered in metanorma-plateau</a:documentation>
|
202
|
+
</ref>
|
191
203
|
</define>
|
192
204
|
<define name="dt">
|
193
205
|
<element name="dt">
|
@@ -871,6 +883,12 @@ titlecase, or lowercase</a:documentation>
|
|
871
883
|
</oneOrMore>
|
872
884
|
</element>
|
873
885
|
</define>
|
886
|
+
<define name="ParagraphFnBody" combine="interleave">
|
887
|
+
<ref name="BlockSource">
|
888
|
+
<a:documentation>Bibliographic source for the information in the paragraph
|
889
|
+
parargaph sources are currently only rendered in metanorma-plateau</a:documentation>
|
890
|
+
</ref>
|
891
|
+
</define>
|
874
892
|
<define name="BasicBlock" combine="choice">
|
875
893
|
<ref name="columnbreak"/>
|
876
894
|
</define>
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require "metanorma-standoc"
|
2
2
|
require_relative "validate_style"
|
3
|
+
require_relative "validate_numeric"
|
3
4
|
require_relative "validate_requirements"
|
4
5
|
require_relative "validate_section"
|
5
6
|
require_relative "validate_title"
|
6
|
-
require_relative "validate_image"
|
7
7
|
require_relative "validate_list"
|
8
|
+
require_relative "validate_xref"
|
8
9
|
require "nokogiri"
|
9
10
|
require "jing"
|
10
11
|
require "iev"
|
@@ -29,127 +30,10 @@ module Metanorma
|
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
|
-
# ISO/IEC DIR 2, 15.5.3, 20.2
|
33
|
-
# does not deal with preceding text marked up
|
34
|
-
def see_xrefs_validate(root)
|
35
|
-
@lang == "en" or return
|
36
|
-
anchors = extract_anchor_norm(root)
|
37
|
-
root.xpath("//xref").each do |t|
|
38
|
-
preceding = t.at("./preceding-sibling::text()[last()]")
|
39
|
-
!preceding.nil? &&
|
40
|
-
/\b(see| refer to)\p{Zs}*\Z/mi.match(preceding) or next
|
41
|
-
anchors[t["target"]] and
|
42
|
-
@log.add("Style", t,
|
43
|
-
"'see #{t['target']}' is pointing to a normative section")
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def extract_anchor_norm(root)
|
48
|
-
nodes = root.xpath("//annex[@obligation = 'normative'] | " \
|
49
|
-
"//references[@obligation = 'normative']")
|
50
|
-
ret = nodes.each_with_object({}) do |n, m|
|
51
|
-
n["anchor"] and m[n["anchor"]] = true
|
52
|
-
end
|
53
|
-
nodes.each do |n|
|
54
|
-
n.xpath(".//*[@anchor]").each { |n1| ret[n1["anchor"]] = true }
|
55
|
-
end
|
56
|
-
ret
|
57
|
-
end
|
58
|
-
|
59
|
-
# ISO/IEC DIR 2, 15.5.3
|
60
|
-
def see_erefs_validate(root)
|
61
|
-
@lang == "en" or return
|
62
|
-
bibitemids = extract_bibitem_anchors(root)
|
63
|
-
root.xpath("//eref").each do |t|
|
64
|
-
prec = t.at("./preceding-sibling::text()[last()]")
|
65
|
-
!prec.nil? && /\b(see|refer to)\p{Zs}*\Z/mi.match(prec) or next
|
66
|
-
unless target = bibitemids[t["bibitemid"]]
|
67
|
-
# unless target = root.at("//bibitem[@anchor = '#{t['bibitemid']}']")
|
68
|
-
@log.add("Bibliography", t,
|
69
|
-
"'#{t} is not pointing to a real reference")
|
70
|
-
next
|
71
|
-
end
|
72
|
-
target[:norm] and
|
73
|
-
@log.add("Style", t,
|
74
|
-
"'see #{t}' is pointing to a normative reference")
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def extract_bibitem_anchors(root)
|
79
|
-
ret = root.xpath("//references[@normative = 'true']//bibitem")
|
80
|
-
.each_with_object({}) do |b, m|
|
81
|
-
m[b["anchor"]] = { bib: b, norm: true }
|
82
|
-
end
|
83
|
-
root.xpath("//references[not(@normative = 'true')]//bibitem")
|
84
|
-
.each do |b|
|
85
|
-
ret[b["anchor"]] = { bib: b, norm: false }
|
86
|
-
end
|
87
|
-
ret
|
88
|
-
end
|
89
|
-
|
90
|
-
# ISO/IEC DIR 2, 10.4
|
91
|
-
def locality_erefs_validate(root)
|
92
|
-
root.xpath("//eref[descendant::locality]").each do |t|
|
93
|
-
if /^(ISO|IEC)/.match?(t["citeas"]) &&
|
94
|
-
!/: ?(\d+{4}|–)$/.match?(t["citeas"])
|
95
|
-
@log.add("Style", t,
|
96
|
-
"undated reference #{t['citeas']} should not contain " \
|
97
|
-
"specific elements")
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
33
|
def termdef_warn(text, regex, elem, term, msg)
|
103
34
|
regex.match(text) && @log.add("Style", elem, "#{term}: #{msg}")
|
104
35
|
end
|
105
36
|
|
106
|
-
# https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-r-ref_clause3
|
107
|
-
def term_xrefs_validate(xmldoc)
|
108
|
-
termids = xmldoc
|
109
|
-
.xpath("//sections/terms | //sections/clause[.//terms] | " \
|
110
|
-
"//annex[.//terms]").each_with_object({}) do |t, m|
|
111
|
-
t.xpath(".//*/@anchor").each { |a| m[a.text] = true }
|
112
|
-
t.xpath(".//*/@id").each { |a| m[a.text] = true }
|
113
|
-
t.name == "terms" and m[t["anchor"] || t["id"]] = true
|
114
|
-
end
|
115
|
-
xmldoc.xpath(".//xref").each do |x|
|
116
|
-
term_xrefs_validate1(x, termids)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def term_xrefs_validate1(xref, termids)
|
121
|
-
closest_id = xref.xpath("./ancestor::*[@id]")&.last or return
|
122
|
-
termids[xref["target"]] && !termids[closest_id["id"]] and
|
123
|
-
@log.add("Style", xref,
|
124
|
-
"only terms clauses can cross-reference terms clause " \
|
125
|
-
"(#{xref['target']})")
|
126
|
-
!termids[xref["target"]] && termids[closest_id["id"]] and
|
127
|
-
@log.add("Style", xref,
|
128
|
-
"non-terms clauses cannot cross-reference terms clause " \
|
129
|
-
"(#{xref['target']})")
|
130
|
-
end
|
131
|
-
|
132
|
-
# require that all assets of a particular type be cross-referenced
|
133
|
-
# within the document
|
134
|
-
def xrefs_mandate_validate(xmldoc)
|
135
|
-
xrefs_mandate_validate1(xmldoc, "//annex", "Annex")
|
136
|
-
xrefs_mandate_validate1(xmldoc, "//table", "Table")
|
137
|
-
xrefs_mandate_validate1(xmldoc, "//figure", "Figure")
|
138
|
-
xrefs_mandate_validate1(xmldoc, "//formula", "Formula")
|
139
|
-
end
|
140
|
-
|
141
|
-
def xrefs_mandate_validate1(xmldoc, xpath, name)
|
142
|
-
exc = %w(table note example figure).map { |x| "//#{x}#{xpath}" }
|
143
|
-
.join(" | ")
|
144
|
-
(xmldoc.xpath(xpath) - xmldoc.xpath(exc)).each do |x|
|
145
|
-
x["unnumbered"] == "true" and next
|
146
|
-
@doc_xrefs[x["anchor"]] or
|
147
|
-
@log.add("Style", x, "#{name} #{x['anchor']} has not been " \
|
148
|
-
"cross-referenced within document",
|
149
|
-
severity: xpath == "//formula" ? 2 : 1)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
37
|
# ISO/IEC DIR 2, 16.5.6
|
154
38
|
def termdef_style(xmldoc)
|
155
39
|
xmldoc.xpath("//term").each do |t|
|
@@ -185,6 +69,22 @@ module Metanorma
|
|
185
69
|
iteration_validate(doc)
|
186
70
|
end
|
187
71
|
|
72
|
+
# DRG directives 3.7; but anticipated by standoc
|
73
|
+
def subfigure_validate(xmldoc)
|
74
|
+
elems = { footnote: "fn", note: "note", key: "dl" }
|
75
|
+
xmldoc.xpath("//figure//figure").each do |f|
|
76
|
+
elems.each do |k, v|
|
77
|
+
f.xpath(".//#{v}").each do |n|
|
78
|
+
@log.add("Style", n, "#{k} is not permitted in a subfigure")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def figure_validate(xmldoc)
|
85
|
+
subfigure_validate(xmldoc)
|
86
|
+
end
|
87
|
+
|
188
88
|
def content_validate(doc)
|
189
89
|
super
|
190
90
|
root = doc.root
|
@@ -203,14 +103,6 @@ module Metanorma
|
|
203
103
|
list_punctuation(doc)
|
204
104
|
end
|
205
105
|
|
206
|
-
def iso_xref_validate(doc)
|
207
|
-
see_xrefs_validate(doc)
|
208
|
-
term_xrefs_validate(doc)
|
209
|
-
xrefs_mandate_validate(doc)
|
210
|
-
see_erefs_validate(doc)
|
211
|
-
locality_erefs_validate(doc)
|
212
|
-
end
|
213
|
-
|
214
106
|
def bibitem_validate(xmldoc)
|
215
107
|
xmldoc.xpath("//bibitem[date/on = '–']").each do |b|
|
216
108
|
b.at("./note[@type = 'Unpublished-Status']") or
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Metanorma
|
2
|
+
module Iso
|
3
|
+
class Converter < Standoc::Converter
|
4
|
+
# https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-s-quantity
|
5
|
+
def style_subscript(node)
|
6
|
+
style_subscript_proper(node)
|
7
|
+
style_subscript_mathml(node)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Check HTML subscripts - only topmost level subs (no sub ancestors)
|
11
|
+
def style_subscript_proper(node)
|
12
|
+
node.xpath(".//sub[not(ancestor::sub)]").each do |x|
|
13
|
+
depth = calculate_subscript_depth(x)
|
14
|
+
depth < 2 and next # No warning for single level subscripts
|
15
|
+
if [2, 3].include?(depth)
|
16
|
+
style_warning(node, "may contain nested subscripts", x.to_xml)
|
17
|
+
else # depth >= 3
|
18
|
+
style_warning(node, "no more than 3 levels of subscript nesting allowed",
|
19
|
+
x.to_xml)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Check MathML subscripts - only topmost level msubs (no msub ancestors)
|
25
|
+
def style_subscript_mathml(node)
|
26
|
+
node.xpath(".//m:msub[not(ancestor::m:msub)]",
|
27
|
+
"m" => MATHML_NS).each do |x|
|
28
|
+
depth = calculate_mathml_subscript_depth(x)
|
29
|
+
depth < 2 and next # No warning for single level subscripts
|
30
|
+
if [2, 3].include?(depth)
|
31
|
+
style_warning(node, "may contain nested subscripts", x.to_xml)
|
32
|
+
else # depth > 3
|
33
|
+
style_warning(node, "no more than 3 levels of subscript nesting allowed",
|
34
|
+
x.to_xml)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# ISO/IEC DIR 2, 9.1
|
40
|
+
# ISO/IEC DIR 2, Table B.1
|
41
|
+
# https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-n-numbers
|
42
|
+
def style_number(node, text)
|
43
|
+
style_number_grouping(node, text)
|
44
|
+
style_regex(/(?:^|\p{Zs})(?<num>[0-9]+\.[0-9]+)(?!\.[0-9])/i,
|
45
|
+
"possible decimal point: mark up numbers with stem:[]", node, text)
|
46
|
+
@lang == "en" and style_regex(/\b(?<num>billions?)\b/i,
|
47
|
+
"ambiguous number", node, text)
|
48
|
+
style_regex(/(?:^|\p{Zs})(?<num>-[0-9][0-9,.]*)/i,
|
49
|
+
"hyphen instead of minus sign U+2212", node, text)
|
50
|
+
@novalid_number = true
|
51
|
+
end
|
52
|
+
|
53
|
+
def style_number_grouping(node, text)
|
54
|
+
if @validate_years
|
55
|
+
style_two_regex_not_prev(
|
56
|
+
node, text, /^(?<num>-?[0-9]{4,}[,0-9]*)\Z/,
|
57
|
+
%r{\b(ISO|IEC|IEEE|(in|January|February|March|April|May|June|August|September|October|November|December)\b)\Z},
|
58
|
+
"number not broken up in threes: mark up numbers with stem:[]"
|
59
|
+
)
|
60
|
+
else
|
61
|
+
style_two_regex_not_prev(
|
62
|
+
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/,
|
63
|
+
%r{\b(ISO|IEC|IEEE|\b)\Z},
|
64
|
+
"number not broken up in threes: mark up numbers with stem:[]"
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# ISO/IEC DIR 2, 9.2.1
|
70
|
+
def style_percent(node, text)
|
71
|
+
style_regex(/\b(?<num>[0-9.,]+%)/,
|
72
|
+
"no space before percent sign", node, text)
|
73
|
+
style_regex(/\b(?<num>[0-9.,]+ \u00b1 [0-9,.]+ %)/,
|
74
|
+
"unbracketed tolerance before percent sign", node, text)
|
75
|
+
end
|
76
|
+
|
77
|
+
# leaving out as problematic: N J K C S T H h d B o E
|
78
|
+
SI_UNIT = "(m|cm|mm|km|μm|nm|g|kg|mgmol|cd|rad|sr|Hz|Hz|MHz|Pa|hPa|kJ|" \
|
79
|
+
"V|kV|W|MW|kW|F|μF|Ω|Wb|°C|lm|lx|Bq|Gy|Sv|kat|l|t|eV|u|Np|Bd|" \
|
80
|
+
"bit|kB|MB|Hart|nat|Sh|var)".freeze
|
81
|
+
|
82
|
+
# ISO/IEC DIR 2, 9.3
|
83
|
+
def style_units(node, text)
|
84
|
+
style_regex(/\b(?<num>[0-9][0-9,]*\p{Zs}+[\u00b0\u2032\u2033])/,
|
85
|
+
"space between number and degrees/minutes/seconds",
|
86
|
+
node, text)
|
87
|
+
style_regex(/\b(?<num>[0-9][0-9,]*#{SI_UNIT})\b/o,
|
88
|
+
"no space between number and SI unit", node, text)
|
89
|
+
style_non_std_units(node, text)
|
90
|
+
end
|
91
|
+
|
92
|
+
NONSTD_UNITS = {
|
93
|
+
sec: "s", mins: "min", hrs: "h", hr: "h", cc: "cm^3",
|
94
|
+
lit: "l", amp: "A", amps: "A", rpm: "r/min"
|
95
|
+
}.freeze
|
96
|
+
|
97
|
+
# ISO/IEC DIR 2, 9.3
|
98
|
+
def style_non_std_units(node, text)
|
99
|
+
NONSTD_UNITS.each do |k, v|
|
100
|
+
style_regex(/\b(?<num>[0-9][0-9,]*\p{Zs}+#{k})\b/,
|
101
|
+
"non-standard unit (should be #{v})", node, text)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def calculate_subscript_depth(sub_element)
|
108
|
+
sub_element.xpath(".//sub").empty? and return 1
|
109
|
+
max_depth = 1
|
110
|
+
sub_element.xpath(".//sub").each do |nested_sub|
|
111
|
+
depth = 1 + calculate_subscript_depth(nested_sub)
|
112
|
+
max_depth = [max_depth, depth].max
|
113
|
+
end
|
114
|
+
max_depth
|
115
|
+
end
|
116
|
+
|
117
|
+
def calculate_mathml_subscript_depth(msub_element)
|
118
|
+
msub_element.xpath(".//m:msub", "m" => MATHML_NS).empty? and return 1
|
119
|
+
max_depth = 1
|
120
|
+
msub_element.xpath(".//m:msub", "m" => MATHML_NS).each do |nested_msub|
|
121
|
+
depth = 1 + calculate_mathml_subscript_depth(nested_msub)
|
122
|
+
max_depth = [max_depth, depth].max
|
123
|
+
end
|
124
|
+
max_depth
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -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)
|
@@ -173,29 +124,6 @@ module Metanorma
|
|
173
124
|
"V|kV|W|MW|kW|F|μF|Ω|Wb|°C|lm|lx|Bq|Gy|Sv|kat|l|t|eV|u|Np|Bd|" \
|
174
125
|
"bit|kB|MB|Hart|nat|Sh|var)".freeze
|
175
126
|
|
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
127
|
# https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-p-and
|
200
128
|
# https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-p-andor
|
201
129
|
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
|
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.
|
4
|
+
version: 3.1.4
|
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-
|
11
|
+
date: 2025-09-01 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
|