metanorma-iso 2.0.2 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/lib/isodoc/iso/html/html_iso_titlepage.html +1 -1
  4. data/lib/isodoc/iso/html/isodoc.css +1 -2
  5. data/lib/isodoc/iso/html/isodoc.scss +0 -1
  6. data/lib/isodoc/iso/html/word_iso_titlepage.html +1 -1
  7. data/lib/isodoc/iso/html/wordstyle.css +20 -0
  8. data/lib/isodoc/iso/html/wordstyle.scss +20 -0
  9. data/lib/isodoc/iso/i18n-en.yaml +29 -2
  10. data/lib/isodoc/iso/i18n-fr.yaml +27 -3
  11. data/lib/isodoc/iso/i18n-ru.yaml +45 -0
  12. data/lib/isodoc/iso/i18n-zh-Hans.yaml +27 -3
  13. data/lib/isodoc/iso/i18n.rb +1 -0
  14. data/lib/isodoc/iso/init.rb +1 -2
  15. data/lib/isodoc/iso/iso.amendment.xsl +1752 -807
  16. data/lib/isodoc/iso/iso.international-standard.xsl +1752 -807
  17. data/lib/isodoc/iso/metadata.rb +12 -2
  18. data/lib/isodoc/iso/presentation_xml_convert.rb +58 -21
  19. data/lib/metanorma/iso/basicdoc.rng +5 -3
  20. data/lib/metanorma/iso/biblio.rng +5 -3
  21. data/lib/metanorma/iso/boilerplate-fr.xml +3 -3
  22. data/lib/metanorma/iso/boilerplate-ru.xml +39 -0
  23. data/lib/metanorma/iso/boilerplate.xml +3 -3
  24. data/lib/metanorma/iso/cleanup.rb +42 -11
  25. data/lib/metanorma/iso/front.rb +1 -1
  26. data/lib/metanorma/iso/front_id.rb +1 -0
  27. data/lib/metanorma/iso/isodoc.rng +73 -3
  28. data/lib/metanorma/iso/processor.rb +14 -7
  29. data/lib/metanorma/iso/validate.rb +30 -2
  30. data/lib/metanorma/iso/validate_image.rb +3 -3
  31. data/lib/metanorma/iso/validate_list.rb +107 -0
  32. data/lib/metanorma/iso/validate_section.rb +42 -34
  33. data/lib/metanorma/iso/validate_style.rb +32 -2
  34. data/lib/metanorma/iso/validate_title.rb +13 -1
  35. data/lib/metanorma/iso/version.rb +1 -1
  36. data/spec/isodoc/amd_spec.rb +10 -5
  37. data/spec/isodoc/i18n_spec.rb +350 -9
  38. data/spec/isodoc/inline_spec.rb +127 -10
  39. data/spec/isodoc/metadata_spec.rb +153 -3
  40. data/spec/isodoc/postproc_spec.rb +3 -4
  41. data/spec/isodoc/section_spec.rb +1 -0
  42. data/spec/isodoc/terms_spec.rb +4 -4
  43. data/spec/isodoc/xref_spec.rb +18 -18
  44. data/spec/metanorma/base_spec.rb +434 -375
  45. data/spec/metanorma/cleanup_spec.rb +11 -11
  46. data/spec/metanorma/refs_spec.rb +333 -65
  47. data/spec/metanorma/section_spec.rb +15 -20
  48. data/spec/metanorma/validate_spec.rb +449 -14
  49. data/spec/spec_helper.rb +6 -4
  50. data/spec/vcr_cassettes/docrels.yml +211 -16
  51. data/spec/vcr_cassettes/withdrawn_iso.yml +301 -0
  52. metadata +6 -3
  53. data/spec/vcr_cassettes/sortrefs.yml +0 -599
@@ -0,0 +1,107 @@
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
+
47
+ list.xpath(".//li").each do |entry|
48
+ l = entry.dup
49
+ l.xpath(".//ol | .//ul").each(&:remove)
50
+ l.text.split.size > 2 and return false
51
+ end
52
+ true
53
+ end
54
+
55
+ def list_punctuation1(list, prectext)
56
+ prectext ||= ""
57
+ entries = list.xpath(".//li")
58
+ case prectext.strip.chars.last
59
+ when ":", "" then list_after_colon_punctuation(list, entries)
60
+ when "." then entries.each { |li| list_full_sentence(li) }
61
+ else style_warning(list, "All lists must be preceded by "\
62
+ "colon or full stop", prectext)
63
+ end
64
+ end
65
+
66
+ # if first list entry starts lowercase, treat as sentence broken up
67
+ def list_after_colon_punctuation(list, entries)
68
+ lower = list.at(".//li").text.match?(/^[^A-Za-z]*[a-z]/)
69
+ entries.each_with_index do |li, i|
70
+ if lower
71
+ list_semicolon_phrase(li, i == entries.size - 1)
72
+ else
73
+ list_full_sentence(li)
74
+ end
75
+ end
76
+ end
77
+
78
+ def list_semicolon_phrase(elem, last)
79
+ text = elem.text.strip
80
+ text.match?(/^[^A-Za-z]*[a-z]/) or
81
+ style_warning(elem, "List entry of broken up sentence must start "\
82
+ "with lowercase letter", text)
83
+ punct = text.sub(/^.*?(\S)\s*$/, "\\1")
84
+ if last
85
+ punct == "." or
86
+ style_warning(elem, "Final list entry of broken up "\
87
+ "sentence must end with full stop", text)
88
+ else
89
+ punct == ";" or
90
+ style_warning(elem, "List entry of broken up sentence must "\
91
+ "end with semicolon", text)
92
+ end
93
+ end
94
+
95
+ def list_full_sentence(elem)
96
+ text = elem.text.strip
97
+ text.match?(/^[^A-Za-z]*[A-Z]/) or
98
+ style_warning(elem, "List entry of separate sentences must start "\
99
+ "with uppercase letter", text)
100
+ punct = text.sub(/^.*?(\S)\s*$/, "\\1")
101
+ punct == "." or
102
+ style_warning(elem, "List entry of separate sentences must "\
103
+ "end with full stop", text)
104
+ end
105
+ end
106
+ end
107
+ 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,19 +34,20 @@ 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
 
40
40
  def symbols_validate(root)
41
41
  f = root.xpath("//definitions")
42
42
  f.empty? && return
43
- (f.size == 1) || @log.add("Style", f.first, ONE_SYMBOLS_WARNING)
44
- f.first.elements.each do |e|
45
- unless %w(title dl).include? e.name
46
- @log.add("Style", f.first, NON_DL_SYMBOLS_WARNING)
47
- return
48
- end
43
+ (f.size == 1 || @vocab) or
44
+ @log.add("Style", f.first, ONE_SYMBOLS_WARNING)
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")
49
51
  end
50
52
  end
51
53
 
@@ -71,26 +73,17 @@ module Metanorma
71
73
 
72
74
  # spec of permissible section sequence
73
75
  # we skip normative references, it goes to end of list
74
- SEQ =
75
- [
76
- {
77
- msg: "Initial section must be (content) Foreword",
78
- val: ["./self::foreword"],
79
- },
80
- {
81
- msg: "Prefatory material must be followed by (clause) Scope",
82
- val: ["./self::introduction", "./self::clause[@type = 'scope']"],
83
- },
84
- {
85
- msg: "Prefatory material must be followed by (clause) Scope",
86
- val: ["./self::clause[@type = 'scope']"],
87
- },
88
- {
89
- msg: "Normative References must be followed by "\
90
- "Terms and Definitions",
91
- val: ["./self::terms | .//terms"],
92
- },
93
- ].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
94
87
 
95
88
  SECTIONS_XPATH =
96
89
  "//foreword | //introduction | //sections/terms | .//annex | "\
@@ -100,7 +93,7 @@ module Metanorma
100
93
 
101
94
  def sections_sequence_validate(root)
102
95
  names, n = sections_sequence_validate_start(root)
103
- if root&.at("//bibdata/ext/subdoctype")&.text == "vocabulary"
96
+ if @vocab
104
97
  names, n = sections_sequence_validate_body_vocab(names, n)
105
98
  else
106
99
  names, n = sections_sequence_validate_body(names, n)
@@ -197,11 +190,6 @@ module Metanorma
197
190
  end
198
191
  end
199
192
 
200
- ASSETS_TO_STYLE =
201
- "//termsource | //formula | //termnote | "\
202
- "//p[not(ancestor::boilerplate)] | //li[not(p)] | //dt | "\
203
- "//dd[not(p)] | //td[not(p)] | //th[not(p)]".freeze
204
-
205
193
  NORM_BIBITEMS =
206
194
  "//references[@normative = 'true']/bibitem".freeze
207
195
 
@@ -216,7 +204,9 @@ module Metanorma
216
204
 
217
205
  def asset_style(root)
218
206
  root.xpath("//example | //termexample").each { |e| example_style(e) }
219
- root.xpath("//definition/verbal-definition").each { |e| definition_style(e) }
207
+ root.xpath("//definition/verbal-definition").each do |e|
208
+ definition_style(e)
209
+ end
220
210
  root.xpath("//note").each { |e| note_style(e) }
221
211
  root.xpath("//fn").each { |e| footnote_style(e) }
222
212
  root.xpath(ASSETS_TO_STYLE).each { |e| style(e, extract_text(e)) }
@@ -242,6 +232,24 @@ module Metanorma
242
232
  @log.add("Style", nil, "#{location}: subclause is only child")
243
233
  end
244
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
245
253
  end
246
254
  end
247
255
  end
@@ -70,8 +70,8 @@ module Metanorma
70
70
  style(node, extract_text(node))
71
71
  end
72
72
 
73
- def style_regex(regex, warning, n, text)
74
- (m = regex.match(text)) && style_warning(n, warning, m[:num])
73
+ def style_regex(regex, warning, node, text)
74
+ (m = regex.match(text)) && style_warning(node, warning, m[:num])
75
75
  end
76
76
 
77
77
  # style check with a regex on a token
@@ -96,10 +96,12 @@ module Metanorma
96
96
  style_percent(node, text)
97
97
  style_abbrev(node, text)
98
98
  style_units(node, text)
99
+ style_punct(node, text)
99
100
  end
100
101
 
101
102
  # ISO/IEC DIR 2, 9.1
102
103
  # ISO/IEC DIR 2, Table B.1
104
+ # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-n-numbers
103
105
  def style_number(node, text)
104
106
  style_two_regex_not_prev(
105
107
  node, text, /^(?<num>-?[0-9]{4,}[,0-9]*)\Z/,
@@ -110,6 +112,8 @@ module Metanorma
110
112
  "possible decimal point", node, text)
111
113
  style_regex(/\b(?<num>billions?)\b/i,
112
114
  "ambiguous number", node, text)
115
+ style_regex(/(^|\s)(?<num>-[0-9][0-9,.]*)/i,
116
+ "hyphen instead of minus sign U+2212", node, text)
113
117
  end
114
118
 
115
119
  # ISO/IEC DIR 2, 9.2.1
@@ -158,6 +162,15 @@ module Metanorma
158
162
  end
159
163
  end
160
164
 
165
+ # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-p-and
166
+ # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-p-andor
167
+ def style_punct(node, text)
168
+ style_regex(/\b(?<num>and\/?or)\b/i,
169
+ "Use 'either x or y, or both'", node, text)
170
+ style_regex(/\s(?<num>&)\s/i,
171
+ "Avoid ampersand in ordinary text'", node, text)
172
+ end
173
+
161
174
  def style_warning(node, msg, text = nil)
162
175
  return if @novalid
163
176
 
@@ -165,6 +178,23 @@ module Metanorma
165
178
  w += ": #{text}" if text
166
179
  @log.add("Style", node, w)
167
180
  end
181
+
182
+ ASSETS_TO_STYLE =
183
+ "//termsource | //formula | //termnote | "\
184
+ "//p[not(ancestor::boilerplate)] | //li[not(p)] | //dt | "\
185
+ "//dd[not(p)] | //td[not(p)] | //th[not(p)]".freeze
186
+
187
+ def asset_style(root)
188
+ root.xpath("//example | //termexample").each { |e| example_style(e) }
189
+ root.xpath("//definition/verbal-definition").each do |e|
190
+ definition_style(e)
191
+ end
192
+ root.xpath("//note").each { |e| note_style(e) }
193
+ root.xpath("//fn").each { |e| footnote_style(e) }
194
+ root.xpath(ASSETS_TO_STYLE).each { |e| style(e, extract_text(e)) }
195
+ norm_bibitem_style(root)
196
+ super
197
+ end
168
198
  end
169
199
  end
170
200
  end
@@ -68,7 +68,7 @@ module Metanorma
68
68
  title = s&.at("./title")&.text || s.name
69
69
  s.xpath("./clause | ./terms | ./references").each do |ss|
70
70
  subtitle = ss.at("./title")
71
- !subtitle.nil? && !subtitle&.text&.empty? or
71
+ (!subtitle.nil? && !subtitle&.text&.empty?) or
72
72
  @log.add("Style", ss,
73
73
  "#{title}: each first-level subclause must have a title")
74
74
  end
@@ -91,6 +91,17 @@ module Metanorma
91
91
  "#{label}: all subclauses must have a title, or none")
92
92
  end
93
93
 
94
+ # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-p-full
95
+ def title_no_full_stop_validate(root)
96
+ root.xpath("//preface//title | //sections//title | //annex//title | "\
97
+ "//references/title | //preface//name | //sections//name | "\
98
+ "//annex//name").each do |t|
99
+ style_regex(/\A(?<num>.+\.\Z)/i,
100
+ "No full stop at end of title or caption",
101
+ t, t.text.strip)
102
+ end
103
+ end
104
+
94
105
  def title_validate(root)
95
106
  title_intro_validate(root)
96
107
  title_main_validate(root)
@@ -99,6 +110,7 @@ module Metanorma
99
110
  title_names_type_validate(root)
100
111
  title_first_level_validate(root)
101
112
  title_all_siblings(root.xpath(SECTIONS_XPATH), "(top level)")
113
+ title_no_full_stop_validate(root)
102
114
  end
103
115
  end
104
116
  end
@@ -1,5 +1,5 @@
1
1
  module Metanorma
2
2
  module ISO
3
- VERSION = "2.0.2".freeze
3
+ VERSION = "2.0.5".freeze
4
4
  end
5
5
  end
@@ -74,6 +74,7 @@ RSpec.describe IsoDoc do
74
74
  <bibdata>
75
75
  <ext>
76
76
  <doctype language="">amendment</doctype>
77
+ <doctype language='en'>Amendment</doctype>
77
78
  </ext>
78
79
  </bibdata>
79
80
  <preface>
@@ -243,9 +244,9 @@ RSpec.describe IsoDoc do
243
244
  <bibdata>
244
245
  <ext>
245
246
  <doctype language=''>amendment</doctype>
247
+ <doctype language='en'>Amendment</doctype>
246
248
  </ext>
247
249
  </bibdata>
248
-
249
250
  <preface>
250
251
  <foreword obligation='informative' displayorder='1'>
251
252
  <title>Foreword</title>
@@ -436,6 +437,7 @@ RSpec.describe IsoDoc do
436
437
  <bibdata>
437
438
  <ext>
438
439
  <doctype language="">amendment</doctype>
440
+ <doctype language='en'>Amendment</doctype>
439
441
  </ext>
440
442
  </bibdata>
441
443
  <boilerplate>
@@ -636,10 +638,13 @@ RSpec.describe IsoDoc do
636
638
  </body>
637
639
  </html>
638
640
  OUTPUT
639
- expect(xmlpp(IsoDoc::Iso::PresentationXMLConvert.new({}).convert("test", input, true))
640
- .sub(%r{<localized-strings>.*</localized-strings>}m, "")).to be_equivalent_to xmlpp(presxml)
641
- expect(xmlpp(IsoDoc::Iso::HtmlConvert.new({}).convert("test", presxml,
642
- true))).to be_equivalent_to xmlpp(html)
641
+ expect(xmlpp(IsoDoc::Iso::PresentationXMLConvert.new({})
642
+ .convert("test", input, true))
643
+ .sub(%r{<localized-strings>.*</localized-strings>}m, ""))
644
+ .to be_equivalent_to xmlpp(presxml)
645
+ expect(xmlpp(IsoDoc::Iso::HtmlConvert.new({})
646
+ .convert("test", presxml, true)))
647
+ .to be_equivalent_to xmlpp(html)
643
648
  end
644
649
 
645
650
  it "processes IsoXML metadata" do