metanorma-iso 2.0.4 → 2.0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/isodoc/iso/html/header.html +42 -33
- data/lib/isodoc/iso/html/isodoc.css +9 -7
- data/lib/isodoc/iso/html/isodoc.scss +7 -5
- data/lib/isodoc/iso/html/style-human.css +0 -1
- data/lib/isodoc/iso/html/style-human.scss +0 -1
- data/lib/isodoc/iso/html/style-iso.css +0 -1
- data/lib/isodoc/iso/html/style-iso.scss +0 -1
- data/lib/isodoc/iso/html/word_iso_titlepage.html +12 -11
- data/lib/isodoc/iso/html/wordstyle.css +46 -19
- data/lib/isodoc/iso/html/wordstyle.scss +52 -25
- data/lib/isodoc/iso/i18n-en.yaml +7 -2
- data/lib/isodoc/iso/i18n-fr.yaml +5 -2
- data/lib/isodoc/iso/i18n-ru.yaml +5 -2
- data/lib/isodoc/iso/i18n-zh-Hans.yaml +6 -2
- data/lib/isodoc/iso/init.rb +1 -2
- data/lib/isodoc/iso/iso.amendment.xsl +704 -328
- data/lib/isodoc/iso/iso.international-standard.xsl +704 -328
- data/lib/isodoc/iso/presentation_xml_convert.rb +58 -21
- data/lib/metanorma/iso/boilerplate-fr.xml +5 -6
- data/lib/metanorma/iso/boilerplate-ru.xml +4 -6
- data/lib/metanorma/iso/boilerplate.xml +5 -6
- data/lib/metanorma/iso/cleanup.rb +43 -10
- data/lib/metanorma/iso/front_id.rb +1 -0
- data/lib/metanorma/iso/processor.rb +14 -7
- data/lib/metanorma/iso/validate.rb +29 -1
- data/lib/metanorma/iso/validate_image.rb +3 -3
- data/lib/metanorma/iso/validate_list.rb +122 -0
- data/lib/metanorma/iso/validate_section.rb +39 -32
- data/lib/metanorma/iso/validate_style.rb +32 -2
- data/lib/metanorma/iso/validate_title.rb +13 -1
- data/lib/metanorma/iso/version.rb +1 -1
- data/metanorma-iso.gemspec +1 -1
- data/spec/isodoc/inline_spec.rb +127 -10
- data/spec/isodoc/postproc_spec.rb +2 -4
- data/spec/isodoc/terms_spec.rb +4 -4
- data/spec/metanorma/cleanup_spec.rb +11 -11
- data/spec/metanorma/refs_spec.rb +273 -61
- data/spec/metanorma/section_spec.rb +18 -26
- data/spec/metanorma/validate_spec.rb +448 -17
- data/spec/spec_helper.rb +6 -4
- data/spec/vcr_cassettes/docrels.yml +393 -0
- data/spec/vcr_cassettes/withdrawn_iso.yml +301 -0
- metadata +8 -5
@@ -47,37 +47,74 @@ module IsoDoc
|
|
47
47
|
|
48
48
|
def eref_delim(delim, type)
|
49
49
|
if delim == ";" then ";"
|
50
|
-
else type == "list" ? "" : delim
|
50
|
+
else type == "list" ? " " : delim
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
54
|
+
def can_conflate_eref_rendering?(refs)
|
55
|
+
super or return false
|
56
|
+
|
57
|
+
first = subclause?(nil, refs.first.at(ns("./locality/@type"))&.text,
|
58
|
+
refs.first.at(ns("./locality/referenceFrom"))&.text)
|
59
|
+
refs.all? do |r|
|
60
|
+
subclause?(nil, r.at(ns("./locality/@type"))&.text,
|
61
|
+
r.at(ns("./locality/referenceFrom"))&.text) == first
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def locality_delimiter(loc)
|
66
|
+
loc&.next_element&.attribute("type")&.text == "list" and return " "
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
def eref_localities_conflated(refs, target, node)
|
71
|
+
droploc = node["droploc"]
|
72
|
+
node["droploc"] = true
|
73
|
+
ret = resolve_eref_connectives(eref_locality_stacks(refs, target,
|
74
|
+
node))
|
75
|
+
node["droploc"] = droploc
|
76
|
+
eref_localities1(target,
|
77
|
+
prefix_clause(target, refs.first.at(ns("./locality"))),
|
78
|
+
l10n(ret[1..-1].join), nil, node, @lang)
|
79
|
+
end
|
80
|
+
|
81
|
+
def prefix_clause(target, loc)
|
82
|
+
loc["type"] == "clause" or return loc["type"]
|
83
|
+
|
84
|
+
if subclause?(target, loc["type"], loc&.at(ns("./referenceFrom"))&.text)
|
85
|
+
""
|
86
|
+
else
|
87
|
+
"clause"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def subclause?(target, type, from)
|
92
|
+
(from&.match?(/\./) && type == "clause") ||
|
93
|
+
type == "list" || target&.match(/^IEV$|^IEC 60050-/)
|
94
|
+
end
|
95
|
+
|
96
|
+
def eref_localities1_zh(target, type, from, upto, node)
|
97
|
+
ret = " 第#{from}" if from
|
98
|
+
ret += "–#{upto}" if upto
|
99
|
+
if node["droploc"] != "true" && !subclause?(target, type, from)
|
100
|
+
ret += eref_locality_populate(type, node)
|
101
|
+
end
|
63
102
|
ret += ")" if type == "list"
|
64
103
|
ret
|
65
104
|
end
|
66
105
|
|
67
|
-
def eref_localities1(target, type, from, upto,
|
68
|
-
return
|
106
|
+
def eref_localities1(target, type, from, upto, node, lang = "en")
|
107
|
+
return nil if type == "anchor"
|
69
108
|
|
70
|
-
subsection = from&.text&.match(/\./)
|
71
109
|
type = type.downcase
|
72
110
|
lang == "zh" and
|
73
|
-
return l10n(eref_localities1_zh(target, type, from, upto, node
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
ret += "
|
80
|
-
ret += "–#{upto.text}" if upto
|
111
|
+
return l10n(eref_localities1_zh(target, type, from, upto, node))
|
112
|
+
ret = if node["droploc"] != "true" && !subclause?(target, type, from)
|
113
|
+
eref_locality_populate(type, node)
|
114
|
+
else ""
|
115
|
+
end
|
116
|
+
ret += " #{from}" if from
|
117
|
+
ret += "–#{upto}" if upto
|
81
118
|
ret += ")" if type == "list"
|
82
119
|
l10n(ret)
|
83
120
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
<boilerplate>
|
2
2
|
<copyright-statement>
|
3
3
|
<clause>
|
4
|
-
<title>DOCUMENT PROTÉGÉ PAR COPYRIGHT</title>
|
5
4
|
<p id="boilerplate-year">© {{ agency }} {{ docyear }}</p>
|
6
5
|
|
7
6
|
<p id="boilerplate-message">
|
8
|
-
Droits de reproduction réservés. Sauf indication contraire,
|
7
|
+
Droits de reproduction réservés. Sauf indication contraire, ou requise dans le cadre de sa mise en œuvre,
|
8
|
+
aucune partie de cette publication ne
|
9
9
|
peut être reproduite ni utilisée sous quelque forme que ce soit et par aucun procédé, électronique
|
10
10
|
ou mécanique, y compris la photocopie, l’affichage sur l’internet ou sur un Intranet, sans
|
11
11
|
autorisation écrite préalable. Les demandes d’autorisation peuvent être adressées à l’ISO à
|
@@ -14,10 +14,9 @@ l’adresse ci-après ou au comité membre de l’ISO dans le pays du demandeur.
|
|
14
14
|
|
15
15
|
<p id="boilerplate-address" align="left">
|
16
16
|
ISO copyright office<br/>
|
17
|
-
Ch. de Blandonnet 8
|
18
|
-
CH-1214 Vernier, Geneva
|
19
|
-
|
20
|
-
Fax  + 41 22 749 09 47<br/>
|
17
|
+
CP 401 • Ch. de Blandonnet 8<br/>
|
18
|
+
CH-1214 Vernier, Geneva<br/>
|
19
|
+
Tél.  + 41 22 749 01 11<br/>
|
21
20
|
Email: copyright@iso.org<br/>
|
22
21
|
Website: www.iso.org
|
23
22
|
</p>
|
@@ -1,21 +1,19 @@
|
|
1
1
|
<boilerplate>
|
2
2
|
<copyright-statement>
|
3
3
|
<clause>
|
4
|
-
<title>ДОКУМЕНТ, ОХРАНЯЕМЫЙ АВТОРСКИМ ПРАВОМ</title>
|
5
4
|
<p id="boilerplate-year">
|
6
5
|
© {{ agency }} {{ docyear }}
|
7
6
|
</p>
|
8
7
|
|
9
8
|
<p id="boilerplate-message">
|
10
|
-
Все права защищены. Если иначе не
|
9
|
+
Все права защищены. Если иначе не определено или не требуется в контексте его реализации, никакая часть этой публикации не может быть воспроизведена или использована иначе в любой форме или каким-либо образом, электронным или механическим, включая фотокопирование, или публикацию в Интернете или интранете, без предварительного письменного разрешения. Разрешение может быть запрошено ISO по адресу, указанному ниже, или у органа — члена ISO страны запрашивающего.
|
11
10
|
</p>
|
12
11
|
|
13
12
|
<p id="boilerplate-address" align="left">
|
14
13
|
Бюро ISO по охране авторских прав<br/>
|
15
|
-
Ch. de Blandonnet 8
|
16
|
-
CH-1214 Vernier, Geneva
|
17
|
-
|
18
|
-
Fax  + 41 22 749 09 47<br/>
|
14
|
+
CP 401 • Ch. de Blandonnet 8<br/>
|
15
|
+
CH-1214 Vernier, Geneva<br/>
|
16
|
+
Тел.  + 41 22 749 01 11<br/>
|
19
17
|
Электронная почта: copyright@iso.org<br/>
|
20
18
|
Сайт: www.iso.org
|
21
19
|
</p>
|
@@ -1,14 +1,14 @@
|
|
1
1
|
<boilerplate>
|
2
2
|
<copyright-statement>
|
3
3
|
<clause>
|
4
|
-
<title>COPYRIGHT PROTECTED DOCUMENT</title>
|
5
4
|
<p id="boilerplate-year">
|
6
5
|
© {{ agency }} {{ docyear }}
|
7
6
|
</p>
|
8
7
|
|
9
8
|
<p id="boilerplate-message">
|
10
9
|
All rights
|
11
|
-
reserved. Unless otherwise specified,
|
10
|
+
reserved. Unless otherwise specified, or required in the context of its implementation,
|
11
|
+
no part of this publication may be
|
12
12
|
reproduced or utilized otherwise in any form or by any means, electronic or
|
13
13
|
mechanical, including photocopying, or posting on the internet or an intranet,
|
14
14
|
without prior written permission. Permission can be requested from either ISO
|
@@ -17,10 +17,9 @@ at the address below or ISO's member body in the country of the requester.
|
|
17
17
|
|
18
18
|
<p id="boilerplate-address" align="left">
|
19
19
|
ISO copyright office<br/>
|
20
|
-
Ch. de Blandonnet 8
|
21
|
-
CH-1214 Vernier, Geneva
|
22
|
-
|
23
|
-
Fax  + 41 22 749 09 47<br/>
|
20
|
+
CP 401 • Ch. de Blandonnet 8<br/>
|
21
|
+
CH-1214 Vernier, Geneva<br/>
|
22
|
+
Phone:  + 41 22 749 01 11<br/>
|
24
23
|
Email: copyright@iso.org<br/>
|
25
24
|
Website: www.iso.org
|
26
25
|
</p>
|
@@ -37,13 +37,11 @@ module Metanorma
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def get_id_prefix(xmldoc)
|
40
|
-
prefix = []
|
41
40
|
xmldoc.xpath("//bibdata/contributor[role/@type = 'publisher']"\
|
42
|
-
"/organization").
|
41
|
+
"/organization").each_with_object([]) do |x, prefix|
|
43
42
|
x1 = x.at("abbreviation")&.text || x.at("name")&.text
|
44
43
|
(x1 == "ISO" and prefix.unshift("ISO")) or prefix << x1
|
45
44
|
end
|
46
|
-
prefix
|
47
45
|
end
|
48
46
|
|
49
47
|
# ISO as a prefix goes first
|
@@ -140,8 +138,7 @@ module Metanorma
|
|
140
138
|
|
141
139
|
def unpub_footnotes(xmldoc)
|
142
140
|
xmldoc.xpath("//bibitem/note[@type = 'Unpublished-Status']").each do |n|
|
143
|
-
|
144
|
-
e = xmldoc.at("//eref[@bibitemid = '#{id}']") or next
|
141
|
+
e = xmldoc.at("//eref[@bibitemid = '#{n.parent['id']}']") or next
|
145
142
|
fn = n.children.to_xml
|
146
143
|
n&.elements&.first&.name == "p" or fn = "<p>#{fn}</p>"
|
147
144
|
e.next = "<fn>#{fn}</fn>"
|
@@ -151,21 +148,52 @@ module Metanorma
|
|
151
148
|
def bibitem_cleanup(xmldoc)
|
152
149
|
super
|
153
150
|
unpublished_note(xmldoc)
|
151
|
+
withdrawn_note(xmldoc)
|
154
152
|
end
|
155
153
|
|
156
154
|
def unpublished_note(xmldoc)
|
157
|
-
xmldoc.xpath("//bibitem[not(
|
158
|
-
|
155
|
+
xmldoc.xpath("//bibitem[not(./ancestor::bibitem)]"\
|
156
|
+
"[not(note[@type = 'Unpublished-Status'])]").each do |b|
|
159
157
|
next if pub_class(b) > 2
|
160
158
|
next unless (s = b.at("./status/stage")) && (s.text.to_i < 60)
|
161
159
|
|
162
160
|
id = b.at("docidentifier").text
|
163
|
-
b.
|
164
|
-
.previous = %(<note type="Unpublished-Status">
|
165
|
-
<p>#{@i18n.under_preparation.sub(/%/, id)}</p></note>)
|
161
|
+
insert_unpub_note(b, @i18n.under_preparation.sub(/%/, id))
|
166
162
|
end
|
167
163
|
end
|
168
164
|
|
165
|
+
def withdrawn_note(xmldoc)
|
166
|
+
xmldoc.xpath("//bibitem[not(note[@type = 'Unpublished-Status'])]")
|
167
|
+
.each do |b|
|
168
|
+
next unless withdrawn_ref?(b)
|
169
|
+
|
170
|
+
if id = replacement_standard(b)
|
171
|
+
insert_unpub_note(b, @i18n.cancelled_and_replaced.sub(/%/, id))
|
172
|
+
else
|
173
|
+
insert_unpub_note(b, @i18n.withdrawn)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def withdrawn_ref?(biblio)
|
179
|
+
return false if pub_class(biblio) > 2
|
180
|
+
|
181
|
+
(s = biblio.at("./status/stage")) && (s.text.to_i == 95) &&
|
182
|
+
(t = biblio.at("./status/substage")) && (t.text.to_i == 99)
|
183
|
+
end
|
184
|
+
|
185
|
+
def replacement_standard(biblio)
|
186
|
+
r = biblio.at("./relation[@type = 'updates']/bibitem") or return nil
|
187
|
+
id = r.at("./formattedref | ./docidentifier[@primary = 'true'] | "\
|
188
|
+
"./docidentifier | ./formattedref") or return nil
|
189
|
+
id.text
|
190
|
+
end
|
191
|
+
|
192
|
+
def insert_unpub_note(biblio, msg)
|
193
|
+
biblio.at("./language | ./script | ./abstract | ./status")
|
194
|
+
.previous = %(<note type="Unpublished-Status"><p>#{msg}</p></note>)
|
195
|
+
end
|
196
|
+
|
169
197
|
def termdef_boilerplate_insert(xmldoc, isodoc, once = false)
|
170
198
|
once = true
|
171
199
|
super
|
@@ -175,6 +203,11 @@ module Metanorma
|
|
175
203
|
@vocab and src.empty? and return
|
176
204
|
super
|
177
205
|
end
|
206
|
+
|
207
|
+
def section_names_terms_cleanup(xml)
|
208
|
+
@vocab and return
|
209
|
+
super
|
210
|
+
end
|
178
211
|
end
|
179
212
|
end
|
180
213
|
end
|
@@ -39,6 +39,7 @@ module Metanorma
|
|
39
39
|
ret = STAGE_ABBRS[stage.to_sym]
|
40
40
|
ret = "PRF" if stage == "60" && substage == "00"
|
41
41
|
ret = "AWI" if stage == "10" && substage == "99"
|
42
|
+
ret = "AWI" if stage == "20" && substage == "00"
|
42
43
|
if %w(amendment technical-corrigendum technical-report
|
43
44
|
technical-specification).include?(doctype)
|
44
45
|
ret = "D" if stage == "40" && doctype == "amendment"
|
@@ -45,19 +45,26 @@ module Metanorma
|
|
45
45
|
def output(isodoc_node, inname, outname, format, options={})
|
46
46
|
case format
|
47
47
|
when :html
|
48
|
-
IsoDoc::Iso::HtmlConvert.new(options)
|
48
|
+
IsoDoc::Iso::HtmlConvert.new(options)
|
49
|
+
.convert(inname, isodoc_node, nil, outname)
|
49
50
|
when :html_alt
|
50
|
-
IsoDoc::Iso::HtmlConvert.new(options.merge(alt: true))
|
51
|
+
IsoDoc::Iso::HtmlConvert.new(options.merge(alt: true))
|
52
|
+
.convert(inname, isodoc_node, nil, outname)
|
51
53
|
when :doc
|
52
|
-
IsoDoc::Iso::WordConvert.new(options)
|
54
|
+
IsoDoc::Iso::WordConvert.new(options)
|
55
|
+
.convert(inname, isodoc_node, nil, outname)
|
53
56
|
when :pdf
|
54
|
-
IsoDoc::Iso::PdfConvert.new(options)
|
57
|
+
IsoDoc::Iso::PdfConvert.new(options)
|
58
|
+
.convert(inname, isodoc_node, nil, outname)
|
55
59
|
when :sts
|
56
|
-
IsoDoc::Iso::StsConvert.new(options)
|
60
|
+
IsoDoc::Iso::StsConvert.new(options)
|
61
|
+
.convert(inname, isodoc_node, nil, outname)
|
57
62
|
when :isosts
|
58
|
-
IsoDoc::Iso::IsoStsConvert.new(options)
|
63
|
+
IsoDoc::Iso::IsoStsConvert.new(options)
|
64
|
+
.convert(inname, isodoc_node, nil, outname)
|
59
65
|
when :presentation
|
60
|
-
IsoDoc::Iso::PresentationXMLConvert.new(options)
|
66
|
+
IsoDoc::Iso::PresentationXMLConvert.new(options)
|
67
|
+
.convert(inname, isodoc_node, nil, outname)
|
61
68
|
else
|
62
69
|
super
|
63
70
|
end
|
@@ -4,6 +4,7 @@ require_relative "./validate_requirements"
|
|
4
4
|
require_relative "./validate_section"
|
5
5
|
require_relative "./validate_title"
|
6
6
|
require_relative "./validate_image"
|
7
|
+
require_relative "./validate_list"
|
7
8
|
require "nokogiri"
|
8
9
|
require "jing"
|
9
10
|
require "iev"
|
@@ -35,7 +36,7 @@ module Metanorma
|
|
35
36
|
/\b(see| refer to)\s*\Z/mi.match(preceding)
|
36
37
|
|
37
38
|
(target = root.at("//*[@id = '#{t['target']}']")) || next
|
38
|
-
if target
|
39
|
+
if target.at("./ancestor-or-self::*[@obligation = 'normative']")
|
39
40
|
@log.add("Style", t,
|
40
41
|
"'see #{t['target']}' is pointing to a normative section")
|
41
42
|
end
|
@@ -75,6 +76,30 @@ module Metanorma
|
|
75
76
|
regex.match(text) && @log.add("Style", elem, "#{term}: #{msg}")
|
76
77
|
end
|
77
78
|
|
79
|
+
# https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-r-ref_clause3
|
80
|
+
def term_xrefs_validate(xmldoc)
|
81
|
+
termids = xmldoc
|
82
|
+
.xpath("//sections/terms | //sections/clause[.//terms] | "\
|
83
|
+
"//annex[.//terms]").each_with_object({}) do |t, m|
|
84
|
+
t.xpath(".//*/@id").each { |a| m[a.text] = true }
|
85
|
+
t.name == "terms" and m[t["id"]] = true
|
86
|
+
end
|
87
|
+
xmldoc.xpath(".//xref").each do |x|
|
88
|
+
term_xrefs_validate1(x, termids)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def term_xrefs_validate1(xref, termids)
|
93
|
+
(termids[xref["target"]] && !termids[xref.parent["id"]]) and
|
94
|
+
@log.add("Style", xref,
|
95
|
+
"only terms clauses can cross-reference terms clause "\
|
96
|
+
"(#{xref['target']})")
|
97
|
+
(!termids[xref["target"]] && termids[xref.parent["id"]]) and
|
98
|
+
@log.add("Style", xref,
|
99
|
+
"non-terms clauses cannot cross-reference terms clause "\
|
100
|
+
"(#{xref['target']})")
|
101
|
+
end
|
102
|
+
|
78
103
|
# ISO/IEC DIR 2, 16.5.6
|
79
104
|
def termdef_style(xmldoc)
|
80
105
|
xmldoc.xpath("//term").each do |t|
|
@@ -139,11 +164,14 @@ module Metanorma
|
|
139
164
|
onlychild_clause_validate(doc.root)
|
140
165
|
termdef_style(doc.root)
|
141
166
|
see_xrefs_validate(doc.root)
|
167
|
+
term_xrefs_validate(doc.root)
|
142
168
|
see_erefs_validate(doc.root)
|
143
169
|
locality_erefs_validate(doc.root)
|
144
170
|
bibdata_validate(doc.root)
|
145
171
|
bibitem_validate(doc.root)
|
146
172
|
figure_validate(doc.root)
|
173
|
+
listcount_validate(doc)
|
174
|
+
list_punctuation(doc)
|
147
175
|
end
|
148
176
|
|
149
177
|
def bibitem_validate(xmldoc)
|
@@ -78,9 +78,9 @@ module Metanorma
|
|
78
78
|
xmldoc.xpath("//image").each do |i|
|
79
79
|
next if i["src"].start_with?("data:")
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
case File.basename(i["src"])
|
82
|
+
when /^ISO_\d+_/
|
83
|
+
when /^(SL)?#{prefix}fig/ then image_name_validate1(i, prefix)
|
84
84
|
else
|
85
85
|
@log.add("Style", i,
|
86
86
|
"image name #{i['src']} does not match DRG requirements: expect #{prefix}fig")
|
@@ -0,0 +1,122 @@
|
|
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-p-lists
|
5
|
+
def listcount_validate(doc)
|
6
|
+
return if @novalid
|
7
|
+
|
8
|
+
ol_count_validate(doc)
|
9
|
+
li_depth_validate(doc)
|
10
|
+
end
|
11
|
+
|
12
|
+
def ol_count_validate(doc)
|
13
|
+
doc.xpath("//clause | //annex").each do |c|
|
14
|
+
next if c.xpath(".//ol").empty?
|
15
|
+
|
16
|
+
ols = c.xpath(".//ol") -
|
17
|
+
c.xpath(".//ul//ol | .//ol//ol | .//clause//ol")
|
18
|
+
ols.size > 1 and
|
19
|
+
style_warning(c, "More than 1 ordered list in a numbered clause",
|
20
|
+
nil)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def li_depth_validate(doc)
|
25
|
+
doc.xpath("//li//li//li//li").each do |l|
|
26
|
+
l.at(".//li") and
|
27
|
+
style_warning(l, "List more than four levels deep", nil)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-p-lists
|
32
|
+
def list_punctuation(doc)
|
33
|
+
return if @novalid
|
34
|
+
|
35
|
+
((doc.xpath("//ol") - doc.xpath("//ul//ol | //ol//ol")) +
|
36
|
+
(doc.xpath("//ul") - doc.xpath("//ul//ul | //ol//ul"))).each do |list|
|
37
|
+
next if skip_list_punctuation(list)
|
38
|
+
|
39
|
+
prec = list.at("./preceding::text()[normalize-space(.) != ''][1]")
|
40
|
+
list_punctuation1(list, prec&.text)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def skip_list_punctuation(list)
|
45
|
+
return true if list.at("./ancestor::table")
|
46
|
+
return true if list.at("./following-sibling::term") # terms boilerplate
|
47
|
+
|
48
|
+
list.xpath(".//li").each do |entry|
|
49
|
+
l = entry.dup
|
50
|
+
l.xpath(".//ol | .//ul").each(&:remove)
|
51
|
+
l.text.split.size > 2 and return false
|
52
|
+
end
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def list_punctuation1(list, prectext)
|
57
|
+
prectext ||= ""
|
58
|
+
entries = list.xpath(".//li")
|
59
|
+
case prectext.strip.chars.last
|
60
|
+
when ":", "" then list_after_colon_punctuation(list, entries)
|
61
|
+
when "." then entries.each { |li| list_full_sentence(li) }
|
62
|
+
else style_warning(list, "All lists must be preceded by "\
|
63
|
+
"colon or full stop", prectext)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# if first list entry starts lowercase, treat as sentence broken up
|
68
|
+
def list_after_colon_punctuation(list, entries)
|
69
|
+
lower = starts_lowercase?(list.at(".//li").text)
|
70
|
+
entries.each_with_index do |li, i|
|
71
|
+
if lower
|
72
|
+
list_semicolon_phrase(li, i == entries.size - 1)
|
73
|
+
else
|
74
|
+
list_full_sentence(li)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def list_semicolon_phrase(elem, last)
|
80
|
+
text = elem.text.strip
|
81
|
+
starts_lowercase?(text) or
|
82
|
+
style_warning(elem, "List entry of broken up sentence must start "\
|
83
|
+
"with lowercase letter", text)
|
84
|
+
list_semicolon_phrase_punct(elem, text, last)
|
85
|
+
end
|
86
|
+
|
87
|
+
def list_semicolon_phrase_punct(elem, text, last)
|
88
|
+
punct = text.strip.sub(/^.*?(\S)$/m, "\\1")
|
89
|
+
if last
|
90
|
+
punct == "." or
|
91
|
+
style_warning(elem, "Final list entry of broken up "\
|
92
|
+
"sentence must end with full stop", text)
|
93
|
+
else
|
94
|
+
punct == ";" or
|
95
|
+
style_warning(elem, "List entry of broken up sentence must "\
|
96
|
+
"end with semicolon", text)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def list_full_sentence(elem)
|
101
|
+
text = elem.text.strip
|
102
|
+
starts_uppercase?(text) or
|
103
|
+
style_warning(elem, "List entry of separate sentences must start "\
|
104
|
+
"with uppercase letter", text)
|
105
|
+
punct = text.strip.sub(/^.*?(\S)$/m, "\\1")
|
106
|
+
punct == "." or
|
107
|
+
style_warning(elem, "List entry of separate sentences must "\
|
108
|
+
"end with full stop", text)
|
109
|
+
end
|
110
|
+
|
111
|
+
# allow that all-caps word (acronym) is agnostic as to lowercase
|
112
|
+
def starts_lowercase?(text)
|
113
|
+
text.match?(/^[^[[:upper:]][[:lower:]]]*[[:lower:]]/) ||
|
114
|
+
text.match?(/^[^[[:upper:]][[:lower:]]]*[[:upper:]][[:upper:]]+[^[[:alpha:]]]/)
|
115
|
+
end
|
116
|
+
|
117
|
+
def starts_uppercase?(text)
|
118
|
+
text.match?(/^[^[[:upper:]][[:lower:]]]*[[:upper:]]/)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -14,6 +14,7 @@ module Metanorma
|
|
14
14
|
end
|
15
15
|
section_style(doc.root)
|
16
16
|
subclause_validate(doc.root)
|
17
|
+
@vocab and vocab_terms_titles_validate(doc.root)
|
17
18
|
super
|
18
19
|
end
|
19
20
|
|
@@ -33,7 +34,6 @@ module Metanorma
|
|
33
34
|
|
34
35
|
ONE_SYMBOLS_WARNING = "Only one Symbols and Abbreviated "\
|
35
36
|
"Terms section in the standard".freeze
|
36
|
-
|
37
37
|
NON_DL_SYMBOLS_WARNING = "Symbols and Abbreviated Terms can "\
|
38
38
|
"only contain a definition list".freeze
|
39
39
|
|
@@ -42,11 +42,12 @@ module Metanorma
|
|
42
42
|
f.empty? && return
|
43
43
|
(f.size == 1 || @vocab) or
|
44
44
|
@log.add("Style", f.first, ONE_SYMBOLS_WARNING)
|
45
|
-
f.first.elements.
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
f.first.elements.reject { |e| %w(title dl).include? e.name }.empty? or
|
46
|
+
@log.add("Style", f.first, NON_DL_SYMBOLS_WARNING)
|
47
|
+
@vocab and f.each do |f1|
|
48
|
+
f1.at("./ancestor::annex") or
|
49
|
+
@log.add("Style", f1, "In vocabulary documents, Symbols and "\
|
50
|
+
"Abbreviated Terms are only permitted in annexes")
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
@@ -72,26 +73,17 @@ module Metanorma
|
|
72
73
|
|
73
74
|
# spec of permissible section sequence
|
74
75
|
# we skip normative references, it goes to end of list
|
75
|
-
SEQ =
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
},
|
85
|
-
|
86
|
-
msg: "Prefatory material must be followed by (clause) Scope",
|
87
|
-
val: ["./self::clause[@type = 'scope']"],
|
88
|
-
},
|
89
|
-
{
|
90
|
-
msg: "Normative References must be followed by "\
|
91
|
-
"Terms and Definitions",
|
92
|
-
val: ["./self::terms | .//terms"],
|
93
|
-
},
|
94
|
-
].freeze
|
76
|
+
SEQ = [
|
77
|
+
{ msg: "Initial section must be (content) Foreword",
|
78
|
+
val: ["./self::foreword"] },
|
79
|
+
{ msg: "Prefatory material must be followed by (clause) Scope",
|
80
|
+
val: ["./self::introduction", "./self::clause[@type = 'scope']"] },
|
81
|
+
{ msg: "Prefatory material must be followed by (clause) Scope",
|
82
|
+
val: ["./self::clause[@type = 'scope']"] },
|
83
|
+
{ msg: "Normative References must be followed by "\
|
84
|
+
"Terms and Definitions",
|
85
|
+
val: ["./self::terms | .//terms"] },
|
86
|
+
].freeze
|
95
87
|
|
96
88
|
SECTIONS_XPATH =
|
97
89
|
"//foreword | //introduction | //sections/terms | .//annex | "\
|
@@ -198,11 +190,6 @@ module Metanorma
|
|
198
190
|
end
|
199
191
|
end
|
200
192
|
|
201
|
-
ASSETS_TO_STYLE =
|
202
|
-
"//termsource | //formula | //termnote | "\
|
203
|
-
"//p[not(ancestor::boilerplate)] | //li[not(p)] | //dt | "\
|
204
|
-
"//dd[not(p)] | //td[not(p)] | //th[not(p)]".freeze
|
205
|
-
|
206
193
|
NORM_BIBITEMS =
|
207
194
|
"//references[@normative = 'true']/bibitem".freeze
|
208
195
|
|
@@ -217,7 +204,9 @@ module Metanorma
|
|
217
204
|
|
218
205
|
def asset_style(root)
|
219
206
|
root.xpath("//example | //termexample").each { |e| example_style(e) }
|
220
|
-
root.xpath("//definition/verbal-definition").each
|
207
|
+
root.xpath("//definition/verbal-definition").each do |e|
|
208
|
+
definition_style(e)
|
209
|
+
end
|
221
210
|
root.xpath("//note").each { |e| note_style(e) }
|
222
211
|
root.xpath("//fn").each { |e| footnote_style(e) }
|
223
212
|
root.xpath(ASSETS_TO_STYLE).each { |e| style(e, extract_text(e)) }
|
@@ -243,6 +232,24 @@ module Metanorma
|
|
243
232
|
@log.add("Style", nil, "#{location}: subclause is only child")
|
244
233
|
end
|
245
234
|
end
|
235
|
+
|
236
|
+
# https://www.iso.org/ISO-house-style.html#iso-hs-s-formatting-r-vocabulary
|
237
|
+
def vocab_terms_titles_validate(root)
|
238
|
+
terms = root.xpath("//sections/terms | //sections/clause[.//terms]")
|
239
|
+
if terms.size == 1
|
240
|
+
((t = terms.first.at("./title")) && (t&.text == @i18n.termsdef)) or
|
241
|
+
@log.add("Style", terms.first,
|
242
|
+
"Single terms clause in vocabulary document "\
|
243
|
+
"should have normal Terms and definitions heading")
|
244
|
+
elsif terms.size > 1
|
245
|
+
terms.each do |x|
|
246
|
+
((t = x.at("./title")) && /^#{@i18n.termsrelated}/.match?(t&.text)) or
|
247
|
+
@log.add("Style", x,
|
248
|
+
"Multiple terms clauses in vocabulary document "\
|
249
|
+
"should have 'Terms related to' heading")
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
246
253
|
end
|
247
254
|
end
|
248
255
|
end
|