metanorma-iso 3.1.2 → 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 +867 -323
- data/lib/isodoc/iso/iso.international-standard.xsl +867 -323
- data/lib/isodoc/iso/metadata.rb +20 -32
- data/lib/isodoc/iso/presentation_bibdata.rb +0 -29
- data/lib/isodoc/iso/presentation_section.rb +2 -2
- data/lib/isodoc/iso/word_cleanup.rb +17 -2
- data/lib/metanorma/iso/biblio-standoc.rng +0 -49
- data/lib/metanorma/iso/biblio.rng +11 -1
- data/lib/metanorma/iso/cleanup.rb +20 -35
- data/lib/metanorma/iso/front_contributor.rb +35 -176
- data/lib/metanorma/iso/isodoc.rng +24 -1
- data/lib/metanorma/iso/relaton-iso.rng +0 -50
- data/lib/metanorma/iso/validate.rb +24 -129
- 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
@@ -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>
|
@@ -2233,6 +2251,11 @@ used in document amendments</a:documentation>
|
|
2233
2251
|
<ref name="RequiredId"/>
|
2234
2252
|
<ref name="NumberingAttributes"/>
|
2235
2253
|
<ref name="BlockAttributes"/>
|
2254
|
+
<optional>
|
2255
|
+
<attribute name="type">
|
2256
|
+
<a:documentation>Semantic classification of note</a:documentation>
|
2257
|
+
</attribute>
|
2258
|
+
</optional>
|
2236
2259
|
<oneOrMore>
|
2237
2260
|
<choice>
|
2238
2261
|
<a:documentation>Content of the term note</a:documentation>
|
@@ -67,14 +67,6 @@
|
|
67
67
|
</group>
|
68
68
|
</element>
|
69
69
|
</define>
|
70
|
-
<define name="editorialgroup">
|
71
|
-
<element name="editorialgroup">
|
72
|
-
<ref name="ISOProjectGroup"/>
|
73
|
-
</element>
|
74
|
-
<optional>
|
75
|
-
<ref name="approvalgroup"/>
|
76
|
-
</optional>
|
77
|
-
</define>
|
78
70
|
</include>
|
79
71
|
<define name="BibDataExtensionType" combine="interleave">
|
80
72
|
<optional>
|
@@ -112,33 +104,6 @@ Used for legacy ISO documents</a:documentation>
|
|
112
104
|
<ref name="DocumentType"/>
|
113
105
|
</element>
|
114
106
|
</define>
|
115
|
-
<define name="ISOProjectGroup">
|
116
|
-
<zeroOrMore>
|
117
|
-
<ref name="agency"/>
|
118
|
-
</zeroOrMore>
|
119
|
-
<oneOrMore>
|
120
|
-
<ref name="technical-committee"/>
|
121
|
-
</oneOrMore>
|
122
|
-
<zeroOrMore>
|
123
|
-
<ref name="subcommittee"/>
|
124
|
-
</zeroOrMore>
|
125
|
-
<zeroOrMore>
|
126
|
-
<ref name="workgroup"/>
|
127
|
-
</zeroOrMore>
|
128
|
-
<optional>
|
129
|
-
<ref name="secretariat"/>
|
130
|
-
</optional>
|
131
|
-
</define>
|
132
|
-
<define name="approvalgroup">
|
133
|
-
<element name="approvalgroup">
|
134
|
-
<ref name="ISOProjectGroup"/>
|
135
|
-
</element>
|
136
|
-
</define>
|
137
|
-
<define name="agency">
|
138
|
-
<element name="agency">
|
139
|
-
<text/>
|
140
|
-
</element>
|
141
|
-
</define>
|
142
107
|
<define name="horizontal">
|
143
108
|
<element name="horizontal">
|
144
109
|
<data type="boolean"/>
|
@@ -179,21 +144,6 @@ Used for legacy ISO documents</a:documentation>
|
|
179
144
|
<data type="int"/>
|
180
145
|
</element>
|
181
146
|
</define>
|
182
|
-
<define name="subcommittee">
|
183
|
-
<element name="subcommittee">
|
184
|
-
<ref name="IsoWorkgroup"/>
|
185
|
-
</element>
|
186
|
-
</define>
|
187
|
-
<define name="workgroup">
|
188
|
-
<element name="workgroup">
|
189
|
-
<ref name="IsoWorkgroup"/>
|
190
|
-
</element>
|
191
|
-
</define>
|
192
|
-
<define name="secretariat">
|
193
|
-
<element name="secretariat">
|
194
|
-
<text/>
|
195
|
-
</element>
|
196
|
-
</define>
|
197
147
|
<define name="stagename">
|
198
148
|
<element name="stagename">
|
199
149
|
<optional>
|
@@ -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"
|
@@ -12,141 +13,27 @@ require "iev"
|
|
12
13
|
module Metanorma
|
13
14
|
module Iso
|
14
15
|
class Converter < Standoc::Converter
|
16
|
+
COMMITTEE_XPATH = <<~XPATH.freeze
|
17
|
+
//contributor[role/description = 'committee']/organization/subdivision
|
18
|
+
XPATH
|
19
|
+
|
15
20
|
def isosubgroup_validate(root)
|
16
|
-
root.xpath("
|
21
|
+
root.xpath("#{COMMITTEE_XPATH}[@type = 'Technical committee']/@subtype").each do |t|
|
17
22
|
%w{TC PC JTC JPC}.include?(t.text) or
|
18
23
|
@log.add("Document Attributes", nil,
|
19
24
|
"invalid technical committee type #{t}")
|
20
25
|
end
|
21
|
-
root.xpath("
|
26
|
+
root.xpath("#{COMMITTEE_XPATH}[@type = 'Subcommittee']/@subtype").each do |t|
|
22
27
|
%w{SC JSC}.include?(t.text) or
|
23
28
|
@log.add("Document Attributes", nil,
|
24
29
|
"invalid subcommittee type #{t}")
|
25
30
|
end
|
26
31
|
end
|
27
32
|
|
28
|
-
# ISO/IEC DIR 2, 15.5.3, 20.2
|
29
|
-
# does not deal with preceding text marked up
|
30
|
-
def see_xrefs_validate(root)
|
31
|
-
@lang == "en" or return
|
32
|
-
anchors = extract_anchor_norm(root)
|
33
|
-
root.xpath("//xref").each do |t|
|
34
|
-
preceding = t.at("./preceding-sibling::text()[last()]")
|
35
|
-
!preceding.nil? &&
|
36
|
-
/\b(see| refer to)\p{Zs}*\Z/mi.match(preceding) or next
|
37
|
-
anchors[t["target"]] and
|
38
|
-
@log.add("Style", t,
|
39
|
-
"'see #{t['target']}' is pointing to a normative section")
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def extract_anchor_norm(root)
|
44
|
-
nodes = root.xpath("//annex[@obligation = 'normative'] | " \
|
45
|
-
"//references[@obligation = 'normative']")
|
46
|
-
ret = nodes.each_with_object({}) do |n, m|
|
47
|
-
n["anchor"] and m[n["anchor"]] = true
|
48
|
-
end
|
49
|
-
nodes.each do |n|
|
50
|
-
n.xpath(".//*[@anchor]").each { |n1| ret[n1["anchor"]] = true }
|
51
|
-
end
|
52
|
-
ret
|
53
|
-
end
|
54
|
-
|
55
|
-
# ISO/IEC DIR 2, 15.5.3
|
56
|
-
def see_erefs_validate(root)
|
57
|
-
@lang == "en" or return
|
58
|
-
bibitemids = extract_bibitem_anchors(root)
|
59
|
-
root.xpath("//eref").each do |t|
|
60
|
-
prec = t.at("./preceding-sibling::text()[last()]")
|
61
|
-
!prec.nil? && /\b(see|refer to)\p{Zs}*\Z/mi.match(prec) or next
|
62
|
-
unless target = bibitemids[t["bibitemid"]]
|
63
|
-
#unless target = root.at("//bibitem[@anchor = '#{t['bibitemid']}']")
|
64
|
-
@log.add("Bibliography", t,
|
65
|
-
"'#{t} is not pointing to a real reference")
|
66
|
-
next
|
67
|
-
end
|
68
|
-
#target.at("./ancestor::references[@normative = 'true']") and
|
69
|
-
target[:norm] and
|
70
|
-
@log.add("Style", t,
|
71
|
-
"'see #{t}' is pointing to a normative reference")
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def extract_bibitem_anchors(root)
|
76
|
-
ret = root.xpath("//references[@normative = 'true']//bibitem")
|
77
|
-
.each_with_object({}) do |b, m|
|
78
|
-
m[b["anchor"]] = { bib: b, norm: true }
|
79
|
-
end
|
80
|
-
root.xpath("//references[not(@normative = 'true')]//bibitem")
|
81
|
-
.each do |b|
|
82
|
-
ret[b["anchor"]] = { bib: b, norm: false }
|
83
|
-
end
|
84
|
-
ret
|
85
|
-
end
|
86
|
-
|
87
|
-
# ISO/IEC DIR 2, 10.4
|
88
|
-
def locality_erefs_validate(root)
|
89
|
-
root.xpath("//eref[descendant::locality]").each do |t|
|
90
|
-
if /^(ISO|IEC)/.match?(t["citeas"]) &&
|
91
|
-
!/: ?(\d+{4}|–)$/.match?(t["citeas"])
|
92
|
-
@log.add("Style", t,
|
93
|
-
"undated reference #{t['citeas']} should not contain " \
|
94
|
-
"specific elements")
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
33
|
def termdef_warn(text, regex, elem, term, msg)
|
100
34
|
regex.match(text) && @log.add("Style", elem, "#{term}: #{msg}")
|
101
35
|
end
|
102
36
|
|
103
|
-
# https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-r-ref_clause3
|
104
|
-
def term_xrefs_validate(xmldoc)
|
105
|
-
termids = xmldoc
|
106
|
-
.xpath("//sections/terms | //sections/clause[.//terms] | " \
|
107
|
-
"//annex[.//terms]").each_with_object({}) do |t, m|
|
108
|
-
t.xpath(".//*/@anchor").each { |a| m[a.text] = true }
|
109
|
-
t.xpath(".//*/@id").each { |a| m[a.text] = true }
|
110
|
-
t.name == "terms" and m[t["anchor"] || t["id"]] = true
|
111
|
-
end
|
112
|
-
xmldoc.xpath(".//xref").each do |x|
|
113
|
-
term_xrefs_validate1(x, termids)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def term_xrefs_validate1(xref, termids)
|
118
|
-
closest_id = xref.xpath("./ancestor::*[@id]")&.last or return
|
119
|
-
termids[xref["target"]] && !termids[closest_id["id"]] and
|
120
|
-
@log.add("Style", xref,
|
121
|
-
"only terms clauses can cross-reference terms clause " \
|
122
|
-
"(#{xref['target']})")
|
123
|
-
!termids[xref["target"]] && termids[closest_id["id"]] and
|
124
|
-
@log.add("Style", xref,
|
125
|
-
"non-terms clauses cannot cross-reference terms clause " \
|
126
|
-
"(#{xref['target']})")
|
127
|
-
end
|
128
|
-
|
129
|
-
# require that all assets of a particular type be cross-referenced
|
130
|
-
# within the document
|
131
|
-
def xrefs_mandate_validate(xmldoc)
|
132
|
-
xrefs_mandate_validate1(xmldoc, "//annex", "Annex")
|
133
|
-
xrefs_mandate_validate1(xmldoc, "//table", "Table")
|
134
|
-
xrefs_mandate_validate1(xmldoc, "//figure", "Figure")
|
135
|
-
xrefs_mandate_validate1(xmldoc, "//formula", "Formula")
|
136
|
-
end
|
137
|
-
|
138
|
-
def xrefs_mandate_validate1(xmldoc, xpath, name)
|
139
|
-
exc = %w(table note example figure).map { |x| "//#{x}#{xpath}" }
|
140
|
-
.join(" | ")
|
141
|
-
(xmldoc.xpath(xpath) - xmldoc.xpath(exc)).each do |x|
|
142
|
-
x["unnumbered"] == "true" and next
|
143
|
-
@doc_xrefs[x["anchor"]] or
|
144
|
-
@log.add("Style", x, "#{name} #{x['anchor']} has not been " \
|
145
|
-
"cross-referenced within document",
|
146
|
-
severity: xpath == "//formula" ? 2 : 1)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
37
|
# ISO/IEC DIR 2, 16.5.6
|
151
38
|
def termdef_style(xmldoc)
|
152
39
|
xmldoc.xpath("//term").each do |t|
|
@@ -182,6 +69,22 @@ module Metanorma
|
|
182
69
|
iteration_validate(doc)
|
183
70
|
end
|
184
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
|
+
|
185
88
|
def content_validate(doc)
|
186
89
|
super
|
187
90
|
root = doc.root
|
@@ -200,14 +103,6 @@ module Metanorma
|
|
200
103
|
list_punctuation(doc)
|
201
104
|
end
|
202
105
|
|
203
|
-
def iso_xref_validate(doc)
|
204
|
-
see_xrefs_validate(doc)
|
205
|
-
term_xrefs_validate(doc)
|
206
|
-
xrefs_mandate_validate(doc)
|
207
|
-
see_erefs_validate(doc)
|
208
|
-
locality_erefs_validate(doc)
|
209
|
-
end
|
210
|
-
|
211
106
|
def bibitem_validate(xmldoc)
|
212
107
|
xmldoc.xpath("//bibitem[date/on = '–']").each do |b|
|
213
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
|