metanorma-iso 1.8.4 → 1.9.0.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +1 -1
  3. data/.rubocop.yml +1 -1
  4. data/lib/asciidoctor/iso/cleanup.rb +0 -1
  5. data/lib/asciidoctor/iso/isodoc.rng +50 -8
  6. data/lib/asciidoctor/iso/isostandard.rng +7 -3
  7. data/lib/asciidoctor/iso/section.rb +3 -0
  8. data/lib/asciidoctor/iso/validate.rb +4 -17
  9. data/lib/asciidoctor/iso/validate_section.rb +50 -34
  10. data/lib/asciidoctor/iso/validate_style.rb +3 -3
  11. data/lib/isodoc/iso/base_convert.rb +41 -16
  12. data/lib/isodoc/iso/html/isodoc.css +475 -20
  13. data/lib/isodoc/iso/html/isodoc.scss +456 -23
  14. data/lib/isodoc/iso/html/wordstyle.css +202 -31
  15. data/lib/isodoc/iso/html/wordstyle.scss +194 -32
  16. data/lib/isodoc/iso/iso.amendment.xsl +480 -388
  17. data/lib/isodoc/iso/iso.international-standard.xsl +480 -388
  18. data/lib/isodoc/iso/isosts_convert.rb +12 -13
  19. data/lib/isodoc/iso/metadata.rb +2 -2
  20. data/lib/isodoc/iso/presentation_xml_convert.rb +62 -9
  21. data/lib/isodoc/iso/sts_convert.rb +4 -5
  22. data/lib/isodoc/iso/word_convert.rb +153 -39
  23. data/lib/isodoc/iso/xref.rb +17 -1
  24. data/lib/metanorma/iso/version.rb +1 -1
  25. data/metanorma-iso.gemspec +4 -4
  26. data/spec/asciidoctor/section_spec.rb +128 -7
  27. data/spec/asciidoctor/validate_spec.rb +15 -15
  28. data/spec/isodoc/amd_spec.rb +193 -201
  29. data/spec/isodoc/blocks_spec.rb +100 -88
  30. data/spec/isodoc/i18n_spec.rb +36 -36
  31. data/spec/isodoc/inline_spec.rb +472 -2
  32. data/spec/isodoc/iso_spec.rb +86 -138
  33. data/spec/isodoc/postproc_spec.rb +19 -10
  34. data/spec/isodoc/ref_spec.rb +6 -6
  35. data/spec/isodoc/section_spec.rb +394 -276
  36. data/spec/isodoc/table_spec.rb +166 -231
  37. data/spec/isodoc/terms_spec.rb +11 -8
  38. data/spec/isodoc/xref_spec.rb +147 -118
  39. metadata +8 -8
@@ -1,31 +1,30 @@
1
1
  require "isodoc"
2
- require "mn2sts"
2
+ require "mnconvert"
3
3
 
4
4
  module IsoDoc
5
5
  module Iso
6
-
7
6
  # A {Converter} implementation that generates HTML output, and a document
8
7
  # schema encapsulation of the document for validation
9
8
  #
10
9
  class IsoStsConvert < IsoDoc::XslfoPdfConvert
11
- def initialize(options)
10
+ def initialize(_options)
12
11
  @libdir = File.dirname(__FILE__)
13
12
  @format = :isosts
14
13
  @suffix = "isosts.xml"
15
14
  end
16
15
 
17
- def convert(input_filename, file = nil, debug = false, output_filename = nil)
18
- file = File.read(input_filename, encoding: "utf-8") if file.nil?
19
- docxml, filename, dir = convert_init(file, input_filename, debug)
20
- /\.xml$/.match(input_filename) or
21
- input_filename = Tempfile.open([filename, ".xml"], encoding: "utf-8") do |f|
22
- f.write file
23
- f.path
24
- end
16
+ def convert(input_fname, file = nil, debug = false, output_fname = nil)
17
+ file = File.read(input_fname, encoding: "utf-8") if file.nil?
18
+ _, fname, dir = convert_init(file, input_fname, debug)
19
+ /\.xml$/.match(input_fname) or
20
+ input_fname = Tempfile.open([fname, ".xml"], encoding: "utf-8") do |f|
21
+ f.write file
22
+ f.path
23
+ end
25
24
  FileUtils.rm_rf dir
26
- Mn2sts.convert(input_filename, output_filename || "#{filename}.#{@suffix}", iso: true)
25
+ MnConvert.convert(input_fname, output_fname || "#{fname}.#{@suffix}",
26
+ MnConvert::InputFormat::MN, { output_format: :iso })
27
27
  end
28
28
  end
29
29
  end
30
30
  end
31
-
@@ -38,12 +38,12 @@ module IsoDoc
38
38
  set(:statusabbr, status_abbrev(docstatus["abbreviation"] || "??",
39
39
  isoxml&.at(ns("//bibdata/status/substage"))&.text,
40
40
  isoxml&.at(ns("//bibdata/status/iteration"))&.text,
41
- isoxml&.at(ns("//version/draft"))&.text,
41
+ isoxml&.at(ns("//bibdata/version/draft"))&.text,
42
42
  isoxml&.at(ns("//bibdata/ext/doctype"))&.text))
43
43
  unpublished(docstatus.text) and
44
44
  set(:stageabbr, docstatus["abbreviation"])
45
45
  end
46
- revdate = isoxml.at(ns("//version/revision-date"))
46
+ revdate = isoxml.at(ns("//bibdata/version/revision-date"))
47
47
  set(:revdate, revdate&.text)
48
48
  end
49
49
 
@@ -45,7 +45,7 @@ module IsoDoc
45
45
  prefix_name(node, "&nbsp;&mdash; ", lbl, "name")
46
46
  end
47
47
 
48
- def eref_localities1_zh(target, type, from, to, n, delim)
48
+ def eref_localities1_zh(target, type, from, upto, node, delim)
49
49
  subsection = from&.text&.match(/\./)
50
50
  ret = if delim == ";"
51
51
  ";"
@@ -53,32 +53,31 @@ module IsoDoc
53
53
  type == "list" ? "" : delim
54
54
  end
55
55
  ret += " 第#{from.text}" if from
56
- ret += "&ndash;#{to.text}" if to
56
+ ret += "&ndash;#{upto.text}" if upto
57
57
  loc = (@i18n.locality[type] || type.sub(/^locality:/, "").capitalize)
58
58
  ret += " #{loc}" unless subsection && type == "clause" ||
59
59
  type == "list" || target.match(/^IEV$|^IEC 60050-/) ||
60
- n["droploc"] == "true"
60
+ node["droploc"] == "true"
61
61
  ret += ")" if type == "list"
62
62
  ret
63
63
  end
64
64
 
65
- def eref_localities1(target, type, from, to, delim, n, lang = "en")
65
+ def eref_localities1(target, type, from, upto, delim, node, lang = "en")
66
66
  return "" if type == "anchor"
67
67
 
68
68
  subsection = from&.text&.match(/\./)
69
69
  type = type.downcase
70
70
  lang == "zh" and
71
- return l10n(eref_localities1_zh(target, type, from, to, n, delim))
72
- ret = if delim == ";"
73
- ";"
71
+ return l10n(eref_localities1_zh(target, type, from, upto, node, delim))
72
+ ret = if delim == ";" then ";"
74
73
  else
75
74
  type == "list" ? "" : delim
76
75
  end
77
- ret += eref_locality_populate(type, n) unless subsection &&
76
+ ret += eref_locality_populate(type, node) unless subsection &&
78
77
  type == "clause" || type == "list" ||
79
78
  target.match(/^IEV$|^IEC 60050-/)
80
79
  ret += " #{from.text}" if from
81
- ret += "&ndash;#{to.text}" if to
80
+ ret += "&ndash;#{upto.text}" if upto
82
81
  ret += ")" if type == "list"
83
82
  l10n(ret)
84
83
  end
@@ -113,6 +112,60 @@ module IsoDoc
113
112
  end
114
113
  end
115
114
 
115
+ def concept(docxml)
116
+ docxml.xpath(ns("//terms//concept")).each_with_object({}) do |f, m|
117
+ concept_term(f, m)
118
+ end
119
+ docxml.xpath(ns("//concept")).each do |node|
120
+ concept_render(node, node["ital"] || "false", node["ref"] || "false")
121
+ end
122
+ end
123
+
124
+ def concept_term(node, seen)
125
+ term = node&.at(ns("./refterm"))&.to_xml
126
+ if term && seen[term]
127
+ concept_render(node, node["ital"] || "false", node["ref"] || "false")
128
+ else concept_render(node, node["ital"] || "true", node["ref"] || "true")
129
+ end
130
+ seen[term] = true if term
131
+ seen
132
+ end
133
+
134
+ def concept1_ref(node, ref)
135
+ if r = node.at(ns("./xref | ./eref | ./termref"))
136
+ return r.remove if ref == "false"
137
+
138
+ r.name == "termref" and
139
+ r.replace(@i18n.term_defined_in.sub(/%/, r.to_xml)) or
140
+ r.replace("(#{r.to_xml})")
141
+ end
142
+ end
143
+
144
+ def concept1(node)
145
+ node.replace(node&.at(ns("./renderterm"))&.children ||
146
+ node&.at(ns("./refterm"))&.children ||
147
+ node.children)
148
+ end
149
+
150
+ # we're assuming terms and clauses in the right place for display,
151
+ # to cope with multiple terms sections
152
+
153
+ def display_order(docxml)
154
+ i = 0
155
+ i = display_order_xpath(docxml, "//preface/*", i)
156
+ i = display_order_at(docxml, "//clause[@type = 'scope']", i)
157
+ i = display_order_at(docxml, @xrefs.klass.norm_ref_xpath, i)
158
+ # i = display_order_at(docxml, "//sections/terms | "\
159
+ # "//sections/clause[descendant::terms]", i)
160
+ # i = display_order_at(docxml, "//sections/definitions", i)
161
+ # i = display_order_xpath(docxml, @xrefs.klass.middle_clause(docxml), i)
162
+ i = display_order_xpath(docxml, "//sections/clause[not(@type = 'scope')] | "\
163
+ "//sections/terms | //sections/definitions", i)
164
+ i = display_order_xpath(docxml, "//annex", i)
165
+ i = display_order_xpath(docxml, @xrefs.klass.bibliography_xpath, i)
166
+ display_order_xpath(docxml, "//indexsect", i)
167
+ end
168
+
116
169
  include Init
117
170
  end
118
171
  end
@@ -1,9 +1,8 @@
1
1
  require "isodoc"
2
- require "mn2sts"
2
+ require "mnconvert"
3
3
 
4
4
  module IsoDoc
5
5
  module Iso
6
-
7
6
  class StsConvert < IsoDoc::XslfoPdfConvert
8
7
  def initialize(_options) # rubocop:disable Lint/MissingSuper
9
8
  @libdir = File.dirname(__FILE__)
@@ -14,14 +13,14 @@ module IsoDoc
14
13
  def convert(in_fname, file = nil, debug = false, out_fname = nil)
15
14
  file = File.read(in_fname, encoding: "utf-8") if file.nil?
16
15
  _docxml, filename, dir = convert_init(file, in_fname, debug)
17
- unless /\.xml$/.match?(in_fname)
16
+ /\.xml$/.match?(in_fname) or
18
17
  in_fname = Tempfile.open([filename, ".xml"], encoding: "utf-8") do |f|
19
18
  f.write file
20
19
  f.path
21
20
  end
22
- end
23
21
  FileUtils.rm_rf dir
24
- Mn2sts.convert(in_fname, out_fname || "#{filename}.#{@suffix}")
22
+ MnConvert.convert(in_fname, out_fname || "#{filename}.#{@suffix}",
23
+ MnConvert::InputFormat::MN)
25
24
  end
26
25
  end
27
26
  end
@@ -14,34 +14,34 @@ module IsoDoc
14
14
  @htmlToClevels = 3 if @htmlToClevels.zero?
15
15
  end
16
16
 
17
+ def font_choice(options)
18
+ if options[:script] == "Hans" then '"Source Han Sans",serif'
19
+ else '"Cambria",serif'
20
+ end
21
+ end
22
+
17
23
  def default_fonts(options)
18
- {
19
- bodyfont: (options[:script] == "Hans" ? '"Source Han Sans",serif' :
20
- '"Cambria",serif'),
21
- headerfont: (options[:script] == "Hans" ? '"Source Han Sans",sans-serif' :
22
- '"Cambria",serif'),
23
- monospacefont: '"Courier New",monospace',
24
- normalfontsize: "11.0pt",
25
- monospacefontsize: "9.0pt",
26
- smallerfontsize: "10.0pt",
27
- footnotefontsize: "10.0pt",
28
- }
24
+ { bodyfont: font_choice(options),
25
+ headerfont: font_choice(options),
26
+ monospacefont: '"Courier New",monospace',
27
+ normalfontsize: "11.0pt",
28
+ monospacefontsize: "9.0pt",
29
+ smallerfontsize: "10.0pt",
30
+ footnotefontsize: "10.0pt" }
29
31
  end
30
32
 
31
33
  def default_file_locations(options)
32
- {
33
- htmlstylesheet: (options[:alt] ? html_doc_path("style-human.scss") :
34
- html_doc_path("style-iso.scss")),
35
- htmlcoverpage: html_doc_path("html_iso_titlepage.html"),
36
- htmlintropage: html_doc_path("html_iso_intro.html"),
37
- wordstylesheet: html_doc_path("wordstyle.scss"),
38
- standardstylesheet: html_doc_path("isodoc.scss"),
39
- header: html_doc_path("header.html"),
40
- wordcoverpage: html_doc_path("word_iso_titlepage.html"),
41
- wordintropage: html_doc_path("word_iso_intro.html"),
42
- ulstyle: "l3",
43
- olstyle: "l2",
44
- }
34
+ a = options[:alt] ? "style-human.scss" : "style-iso.scss"
35
+ { htmlstylesheet: html_doc_path(a),
36
+ htmlcoverpage: html_doc_path("html_iso_titlepage.html"),
37
+ htmlintropage: html_doc_path("html_iso_intro.html"),
38
+ wordstylesheet: html_doc_path("wordstyle.scss"),
39
+ standardstylesheet: html_doc_path("isodoc.scss"),
40
+ header: html_doc_path("header.html"),
41
+ wordcoverpage: html_doc_path("word_iso_titlepage.html"),
42
+ wordintropage: html_doc_path("word_iso_intro.html"),
43
+ ulstyle: "l3",
44
+ olstyle: "l2" }
45
45
  end
46
46
 
47
47
  def make_body(xml, docxml)
@@ -54,9 +54,10 @@ module IsoDoc
54
54
  end
55
55
  end
56
56
 
57
- def colophon(body, docxml)
58
- stage = @meta.get[:stage_int]
57
+ def colophon(body, _docxml)
58
+ stage = @meta.get[:stage_int]
59
59
  return if !stage.nil? && stage < 60
60
+
60
61
  body.br **{ clear: "all", style: "page-break-before:left;"\
61
62
  "mso-break-type:section-break" }
62
63
  body.div **{ class: "colophon" } do |div|
@@ -67,35 +68,55 @@ module IsoDoc
67
68
  super
68
69
  xml.xpath("//div[@class = 'figure']//table[@class = 'dl']").each do |t|
69
70
  t["class"] = "figdl"
70
- d = t.add_previous_sibling("<div class='figdl'/>")
71
+ d = t.add_previous_sibling("<div class='figdl' "\
72
+ "style='page-break-after:avoid;'/>")
71
73
  t.parent = d.first
72
74
  end
73
75
  end
74
76
 
75
77
  # force Annex h2 down to be p.h2Annex, so it is not picked up by ToC
76
- def word_annex_cleanup1(docxml, i)
77
- docxml.xpath("//h#{i}[ancestor::*[@class = 'Section3']]").each do |h2|
78
+ def word_annex_cleanup1(docxml, lvl)
79
+ docxml.xpath("//h#{lvl}[ancestor::*[@class = 'Section3']]").each do |h2|
78
80
  h2.name = "p"
79
- h2["class"] = "h#{i}Annex"
81
+ h2["class"] = "h#{lvl}Annex"
80
82
  end
81
83
  end
82
84
 
83
85
  def word_annex_cleanup(docxml)
84
- word_annex_cleanup1(docxml, 2)
85
- word_annex_cleanup1(docxml, 3)
86
- word_annex_cleanup1(docxml, 4)
87
- word_annex_cleanup1(docxml, 5)
88
- word_annex_cleanup1(docxml, 6)
86
+ (2..6).each { |i| word_annex_cleanup1(docxml, i) }
87
+ end
88
+
89
+ def word_annex_cleanup_h1(docxml)
90
+ docxml.xpath("//h1[@class = 'Annex']").each do |h|
91
+ h.name = "p"
92
+ h["class"] = "ANNEX"
93
+ end
94
+ %w(BiblioTitle ForewordTitle IntroTitle).each do |s|
95
+ docxml.xpath("//*[@class = '#{s}']").each do |h|
96
+ h.name = "p"
97
+ end
98
+ end
99
+ end
100
+
101
+ def style_cleanup(docxml)
102
+ word_annex_cleanup_h1(docxml)
103
+ style_cleanup1(docxml)
104
+ end
105
+
106
+ def style_cleanup1(docxml)
107
+ docxml.xpath("//*[@class = 'example']").each do |p|
108
+ p["class"] = "Example"
109
+ end
89
110
  end
90
111
 
91
112
  def authority_hdr_cleanup(docxml)
92
- docxml&.xpath("//div[@class = 'boilerplate-license']").each do |d|
113
+ docxml&.xpath("//div[@class = 'boilerplate-license']")&.each do |d|
93
114
  d.xpath(".//h1").each do |p|
94
115
  p.name = "p"
95
116
  p["class"] = "zzWarningHdr"
96
117
  end
97
118
  end
98
- docxml&.xpath("//div[@class = 'boilerplate-copyright']").each do |d|
119
+ docxml&.xpath("//div[@class = 'boilerplate-copyright']")&.each do |d|
99
120
  d.xpath(".//h1").each do |p|
100
121
  p.name = "p"
101
122
  p["class"] = "zzCopyrightHdr"
@@ -120,11 +141,104 @@ module IsoDoc
120
141
  def word_cleanup(docxml)
121
142
  authority_hdr_cleanup(docxml)
122
143
  super
144
+ style_cleanup(docxml)
123
145
  docxml
124
146
  end
125
147
 
126
- def footnote_reference_format(a)
127
- a.children = "<span class='MsoFootnoteReference'>#{a.children.to_xml}</span>)"
148
+ def word_toc_preface(level)
149
+ <<~TOC.freeze
150
+ <span lang="EN-GB"><span
151
+ style='mso-element:field-begin'></span><span
152
+ style='mso-spacerun:yes'>&#xA0;</span>TOC
153
+ \\o &quot;1-#{level}&quot; \\h \\z \\t &quot;Heading
154
+ 1;1;ANNEX;1;Biblio Title;1;Foreword Title;1;Intro Title;1&quot; <span
155
+ style='mso-element:field-separator'></span></span>
156
+ TOC
157
+ end
158
+
159
+ def footnote_reference_format(link)
160
+ link.children =
161
+ "<span class='MsoFootnoteReference'>#{link.children.to_xml}</span>)"
162
+ end
163
+
164
+ def bibliography_attrs
165
+ { class: "BiblioTitle" }
166
+ end
167
+
168
+ def bibliography(xml, out)
169
+ f = xml.at(ns(bibliography_xpath)) and f["hidden"] != "true" or return
170
+ page_break(out)
171
+ out.div do |div|
172
+ div.h1 **bibliography_attrs do |h1|
173
+ f&.at(ns("./title"))&.children&.each { |c2| parse(c2, h1) }
174
+ end
175
+ biblio_list(f, div, true)
176
+ end
177
+ end
178
+
179
+ def bibliography_parse(node, out)
180
+ node["hidden"] != true or return
181
+ out.div do |div|
182
+ clause_parse_title(node, div, node.at(ns("./title")), out,
183
+ bibliography_attrs)
184
+ biblio_list(node, div, true)
185
+ end
186
+ end
187
+
188
+ def para_class(node)
189
+ if !node.ancestors("definition").empty? && !@in_footnote
190
+ "Definition"
191
+ elsif !node.ancestors("foreword").empty? && !@in_footnote
192
+ "ForewordText"
193
+ else
194
+ super
195
+ end
196
+ end
197
+
198
+ def termref_attrs
199
+ { class: "Source" }
200
+ end
201
+
202
+ def termref_parse(node, out)
203
+ out.p **termref_attrs do |p|
204
+ p << "[TERMREF]"
205
+ node.children.each { |n| parse(n, p) }
206
+ p << "[/TERMREF]"
207
+ end
208
+ end
209
+
210
+ def figure_name_attrs(node)
211
+ s = node.ancestors("annex").empty? ? "FigureTitle" : "AnnexFigureTitle"
212
+ { class: s, style: "text-align:center;" }
213
+ end
214
+
215
+ def figure_name_parse(node, div, name)
216
+ return if name.nil?
217
+
218
+ div.p **figure_name_attrs(node) do |p|
219
+ name.children.each { |n| parse(n, p) }
220
+ end
221
+ end
222
+
223
+ def table_title_attrs(node)
224
+ s = node.ancestors("annex").empty? ? "Tabletitle" : "AnnexTableTitle"
225
+ { class: s, style: "text-align:center;" }
226
+ end
227
+
228
+ def table_title_parse(node, out)
229
+ name = node.at(ns("./name")) or return
230
+ out.p **table_title_attrs(node) do |p|
231
+ name&.children&.each { |n| parse(n, p) }
232
+ end
233
+ end
234
+
235
+ def annex_name(_annex, name, div)
236
+ return if name.nil?
237
+
238
+ name&.at(ns("./strong"))&.remove # supplied by CSS list numbering
239
+ div.h1 **{ class: "Annex" } do |t|
240
+ name.children.each { |c2| parse(c2, t) }
241
+ end
128
242
  end
129
243
 
130
244
  include BaseConvert
@@ -8,7 +8,7 @@ module IsoDoc
8
8
  if @klass.amd(doc)
9
9
  initial_anchor_names_amd(doc)
10
10
  else
11
- super
11
+ initial_anchor_names1(doc)
12
12
  end
13
13
  introduction_names(doc.at(ns("//introduction")))
14
14
  end
@@ -26,6 +26,22 @@ module IsoDoc
26
26
  termexample_anchor_names(doc)
27
27
  end
28
28
 
29
+ def initial_anchor_names1(doc)
30
+ doc.xpath(ns("//preface/*")).each { |c| c.element? and preface_names(c) }
31
+ # potentially overridden in middle_section_asset_names()
32
+ sequential_asset_names(doc.xpath(ns("//preface/*")))
33
+ n = Counter.new
34
+ n = section_names(doc.at(ns("//clause[@type = 'scope']")), n, 1)
35
+ n = section_names(doc.at(ns(@klass.norm_ref_xpath)), n, 1)
36
+ doc.xpath(ns("//sections/clause[not(@type = 'scope')] | "\
37
+ "//sections/terms | //sections/definitions")).each do |c|
38
+ n = section_names(c, n, 1)
39
+ end
40
+ middle_section_asset_names(doc)
41
+ termnote_anchor_names(doc)
42
+ termexample_anchor_names(doc)
43
+ end
44
+
29
45
  # we can reference 0-number clauses in introduction
30
46
  def introduction_names(clause)
31
47
  return if clause.nil?