metanorma-unece 0.0.2

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 (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.hound.yml +3 -0
  4. data/.rubocop.ribose.yml +66 -0
  5. data/.rubocop.tb.yml +650 -0
  6. data/.rubocop.yml +15 -0
  7. data/.travis.yml +21 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE +25 -0
  11. data/README.adoc +202 -0
  12. data/Rakefile +6 -0
  13. data/bin/console +14 -0
  14. data/bin/rspec +17 -0
  15. data/bin/setup +8 -0
  16. data/lib/asciidoctor/unece.rb +7 -0
  17. data/lib/asciidoctor/unece/biblio.rng +880 -0
  18. data/lib/asciidoctor/unece/converter.rb +240 -0
  19. data/lib/asciidoctor/unece/isodoc.rng +1066 -0
  20. data/lib/asciidoctor/unece/isostandard.rng +1054 -0
  21. data/lib/asciidoctor/unece/unece.rng +247 -0
  22. data/lib/isodoc/unece.rb +9 -0
  23. data/lib/isodoc/unece/html/header.html +164 -0
  24. data/lib/isodoc/unece/html/html_unece_intro.html +27 -0
  25. data/lib/isodoc/unece/html/html_unece_titlepage.html +112 -0
  26. data/lib/isodoc/unece/html/htmlstyle.scss +1119 -0
  27. data/lib/isodoc/unece/html/logo.jpg +0 -0
  28. data/lib/isodoc/unece/html/scripts.html +82 -0
  29. data/lib/isodoc/unece/html/unece.scss +660 -0
  30. data/lib/isodoc/unece/html/word_unece_intro.html +24 -0
  31. data/lib/isodoc/unece/html/word_unece_plenary_titlepage.html +129 -0
  32. data/lib/isodoc/unece/html/word_unece_titlepage.html +17 -0
  33. data/lib/isodoc/unece/html/wordstyle.scss +1088 -0
  34. data/lib/isodoc/unece/html_convert.rb +392 -0
  35. data/lib/isodoc/unece/metadata.rb +93 -0
  36. data/lib/isodoc/unece/word_convert.rb +405 -0
  37. data/lib/metanorma-unece.rb +8 -0
  38. data/lib/metanorma/unece.rb +11 -0
  39. data/lib/metanorma/unece/UN_emblem_blue.svg +193 -0
  40. data/lib/metanorma/unece/processor.rb +40 -0
  41. data/lib/metanorma/unece/version.rb +5 -0
  42. data/metanorma-unece.gemspec +47 -0
  43. metadata +353 -0
@@ -0,0 +1,392 @@
1
+ require "isodoc"
2
+ require_relative "metadata"
3
+ require "fileutils"
4
+ require "roman-numerals"
5
+
6
+ module IsoDoc
7
+ module Unece
8
+
9
+ # A {Converter} implementation that generates HTML output, and a document
10
+ # schema encapsulation of the document for validation
11
+ #
12
+ class HtmlConvert < IsoDoc::HtmlConvert
13
+ def initialize(options)
14
+ @libdir = File.dirname(__FILE__)
15
+ super
16
+ FileUtils.cp html_doc_path('logo.jpg'), "logo.jpg"
17
+ @files_to_delete << "logo.jpg"
18
+ end
19
+
20
+ def default_fonts(options)
21
+ {
22
+ bodyfont: (
23
+ options[:script] == "Hans" ?
24
+ '"SimSun",serif' :
25
+ '"Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif'
26
+ ),
27
+ headerfont: (
28
+ options[:script] == "Hans" ?
29
+ '"SimHei",sans-serif' :
30
+ '"Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif'
31
+ ),
32
+ monospacefont: '"Space Mono",monospace'
33
+ }
34
+ end
35
+
36
+ def default_file_locations(_options)
37
+ {
38
+ htmlstylesheet: html_doc_path("htmlstyle.scss"),
39
+ htmlcoverpage: html_doc_path("html_unece_titlepage.html"),
40
+ htmlintropage: html_doc_path("html_unece_intro.html"),
41
+ scripts: html_doc_path("scripts.html"),
42
+ }
43
+ end
44
+
45
+ def metadata_init(lang, script, labels)
46
+ @meta = Metadata.new(lang, script, labels)
47
+ end
48
+
49
+ def html_head
50
+ <<~HEAD.freeze
51
+ <title>{{ doctitle }}</title>
52
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
53
+
54
+ <!--TOC script import-->
55
+ <script type="text/javascript" src="https://cdn.rawgit.com/jgallen23/toc/0.3.2/dist/toc.min.js"></script>
56
+
57
+ <!--Google fonts-->
58
+ <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i|Space+Mono:400,700" rel="stylesheet">
59
+ <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,400i,500,700,900" rel="stylesheet">
60
+ <!--Font awesome import for the link icon-->
61
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/solid.css" integrity="sha384-v2Tw72dyUXeU3y4aM2Y0tBJQkGfplr39mxZqlTBDUZAb9BGoC40+rdFCG0m10lXk" crossorigin="anonymous">
62
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/fontawesome.css" integrity="sha384-q3jl8XQu1OpdLgGFvNRnPdj5VIlCvgsDQTQB6owSOHWlAurxul7f+JpUOVdAiJ5P" crossorigin="anonymous">
63
+ <style class="anchorjs"></style>
64
+ HEAD
65
+ end
66
+
67
+ def make_body(xml, docxml)
68
+ plenary = docxml.at(ns("//bibdata[@type = 'plenary']"))
69
+ body_attr = { lang: "EN-US", link: "blue", vlink: "#954F72", "xml:lang": "EN-US", class: "container" }
70
+ @htmlintropage = nil if plenary
71
+ xml.body **body_attr do |body|
72
+ make_body1(body, docxml)
73
+ make_body2(body, docxml)
74
+ make_body3(body, docxml)
75
+ end
76
+ end
77
+
78
+ def make_body3(body, docxml)
79
+ body.div **{ class: "main-section" } do |div3|
80
+ abstract docxml, div3
81
+ foreword docxml, div3
82
+ introduction docxml, div3
83
+ middle docxml, div3
84
+ footnotes div3
85
+ comments div3
86
+ end
87
+ end
88
+
89
+ def html_preface(docxml)
90
+ super
91
+ =begin
92
+ preface_container = docxml.at("//div[@id = 'preface_container']") # recommendation
93
+ abstractbox = docxml.at("//div[@id = 'abstractbox']") # plenary
94
+ foreword = docxml.at("//p[@class = 'ForewordTitle']/..")
95
+ intro = docxml.at("//p[@class = 'IntroTitle']/..")
96
+ abstract = docxml.at("//h1[@class = 'AbstractTitle']/..")
97
+ abstract.parent = (abstractbox || preface_container) if abstract
98
+ docxml.at("//h1[@class = 'AbstractTitle']").remove if abstractbox
99
+ foreword.parent = preface_container if foreword && preface_container
100
+ intro.parent = preface_container if intro && preface_container
101
+ if abstractbox && !intro && !foreword
102
+ sect2 = docxml.at("//div[@class='WordSection2']")
103
+ sect2.next_element.remove # pagebreak
104
+ sect2.remove # pagebreak
105
+ end
106
+ =end
107
+ docxml
108
+ end
109
+
110
+ def middle(isoxml, out)
111
+ clause isoxml, out
112
+ annex isoxml, out
113
+ bibliography isoxml, out
114
+ end
115
+
116
+ def html_toc(docxml)
117
+ docxml
118
+ end
119
+
120
+ def clause_parse_title(node, div, c1, out)
121
+ if node["inline-header"] == "true"
122
+ inline_header_title(out, node, c1)
123
+ else
124
+ div.send "h#{get_anchors[node['id']][:level]}" do |h|
125
+ lbl = get_anchors[node['id']][:label]
126
+ h << "#{lbl}. " if lbl && !@suppressheadingnumbers
127
+ insert_tab(h, 1)
128
+ c1&.children&.each { |c2| parse(c2, h) }
129
+ end
130
+ end
131
+ end
132
+
133
+ def introduction(isoxml, out)
134
+ f = isoxml.at(ns("//introduction")) || return
135
+ page_break(out)
136
+ out.div **{ class: "Section3", id: f["id"] } do |div|
137
+ s.h1(**{ class: "IntroTitle" }) do |h1|
138
+ #insert_tab(h1, 1)
139
+ h1 << @introduction_lbl
140
+ end
141
+ f.elements.each do |e|
142
+ parse(e, div) unless e.name == "title"
143
+ end
144
+ end
145
+ end
146
+
147
+ def foreword(isoxml, out)
148
+ f = isoxml.at(ns("//foreword")) || return
149
+ page_break(out)
150
+ out.div **attr_code(id: f["id"]) do |s|
151
+ s.h1(**{ class: "ForewordTitle" }) do |h1|
152
+ #insert_tab(h1, 1)
153
+ h1 << @foreword_lbl
154
+ end
155
+ f.elements.each { |e| parse(e, s) unless e.name == "title" }
156
+ end
157
+ end
158
+
159
+ def annex_name(annex, name, div)
160
+ div.h1 **{ class: "Annex" } do |t|
161
+ t << "#{get_anchors[annex['id']][:label]}"
162
+ t.br
163
+ t.b do |b|
164
+ name&.children&.each { |c2| parse(c2, b) }
165
+ end
166
+ end
167
+ end
168
+
169
+ def pre_parse(node, out)
170
+ out.pre node.text # content.gsub(/</, "&lt;").gsub(/>/, "&gt;")
171
+ end
172
+
173
+ def term_defs_boilerplate(div, source, term, preface)
174
+ if source.empty? && term.nil?
175
+ div << @no_terms_boilerplate
176
+ else
177
+ div << term_defs_boilerplate_cont(source, term)
178
+ end
179
+ end
180
+
181
+ def i18n_init(lang, script)
182
+ super
183
+ @admonition_lbl = "Box"
184
+ end
185
+
186
+ def fileloc(loc)
187
+ File.join(File.dirname(__FILE__), loc)
188
+ end
189
+
190
+ def cleanup(docxml)
191
+ super
192
+ term_cleanup(docxml)
193
+ end
194
+
195
+ def term_cleanup(docxml)
196
+ docxml.xpath("//p[@class = 'Terms']").each do |d|
197
+ h2 = d.at("./preceding-sibling::*[@class = 'TermNum'][1]")
198
+ h2.add_child("&nbsp;")
199
+ h2.add_child(d.remove)
200
+ end
201
+ docxml
202
+ end
203
+
204
+ MIDDLE_CLAUSE = "//clause[parent::sections]".freeze
205
+
206
+ def initial_anchor_names(d)
207
+ preface_names(d.at(ns("//foreword")))
208
+ preface_names(d.at(ns("//introduction")))
209
+ sequential_asset_names(d.xpath(ns("//foreword | //introduction")))
210
+ middle_section_asset_names(d)
211
+ clause_names(d, 0)
212
+ termnote_anchor_names(d)
213
+ termexample_anchor_names(d)
214
+ end
215
+
216
+ def clause_names(docxml, sect_num)
217
+ q = "//clause[parent::sections]"
218
+ @paranumber = 0
219
+ docxml.xpath(ns(q)).each_with_index do |c, i|
220
+ section_names(c, (i + sect_num), 1)
221
+ end
222
+ end
223
+
224
+ def levelnumber(num, lvl)
225
+ case lvl % 3
226
+ when 1 then RomanNumerals.to_roman(num)
227
+ when 2 then ("A".ord + num - 1).chr
228
+ when 0 then num.to_s
229
+ end
230
+ end
231
+
232
+ def annex_levelnumber(num, lvl)
233
+ case lvl % 3
234
+ when 0 then RomanNumerals.to_roman(num)
235
+ when 1 then ("A".ord + num - 1).chr
236
+ when 2 then num.to_s
237
+ end
238
+ end
239
+
240
+ def leaf_section(clause, lvl)
241
+ @paranumber += 1
242
+ @anchors[clause["id"]] = {label: @paranumber.to_s, xref: "paragraph #{@paranumber}", level: lvl, type: "paragraph" }
243
+ end
244
+
245
+ def annex_leaf_section(clause, num, lvl)
246
+ @paranumber += 1
247
+ @anchors[clause["id"]] = {label: @paranumber.to_s, xref: "paragraph #{num}.#{@paranumber}", level: lvl, type: "paragraph" }
248
+ end
249
+
250
+ def section_names(clause, num, lvl)
251
+ return num if clause.nil?
252
+ clause.at(ns("./clause | ./term | ./terms | ./definitions")) or
253
+ leaf_section(clause, lvl) && return
254
+ num = num + 1
255
+ lbl = levelnumber(num, 1)
256
+ @anchors[clause["id"]] =
257
+ { label: lbl, xref: l10n("#{@clause_lbl} #{lbl}"), level: lvl, type: "clause" }
258
+ i = 1
259
+ clause.xpath(ns("./clause | ./term | ./terms | ./definitions")).each do |c|
260
+ section_names1(c, "#{lbl}.#{levelnumber(i, lvl + 1)}", lvl + 1)
261
+ i += 1 if c.at(ns("./clause | ./term | ./terms | ./definitions"))
262
+ end
263
+ num
264
+ end
265
+
266
+ def section_names1(clause, num, level)
267
+ unless clause.at(ns("./clause | ./term | ./terms | ./definitions"))
268
+ leaf_section(clause, level) and return
269
+ end
270
+ /\.(?<leafnum>[^.]+$)/ =~ num
271
+ @anchors[clause["id"]] =
272
+ { label: leafnum, level: level, xref: l10n("#{@clause_lbl} #{num}"), type: "clause" }
273
+ i = 1
274
+ clause.xpath(ns("./clause | ./terms | ./term | ./definitions")).each do |c|
275
+ section_names1(c, "#{num}.#{levelnumber(i, level + 1)}", level + 1)
276
+ i += 1 if c.at(ns("./clause | ./term | ./terms | ./definitions"))
277
+ end
278
+ end
279
+
280
+ def annex_name_lbl(clause, num)
281
+ l10n("<b>#{@annex_lbl} #{num}</b>")
282
+ end
283
+
284
+ def annex_names(clause, num)
285
+ unless clause.at(ns("./clause | ./term | ./terms | ./definitions"))
286
+ annex_leaf_section(clause, num, 1) and return
287
+ end
288
+ @anchors[clause["id"]] = { label: annex_name_lbl(clause, num), type: "clause",
289
+ xref: "#{@annex_lbl} #{num}", level: 1 }
290
+ i = 1
291
+ clause.xpath(ns("./clause")).each do |c|
292
+ annex_names1(c, "#{num}.#{annex_levelnumber(i, 2)}", 2)
293
+ i += 1 if c.at(ns("./clause | ./term | ./terms | ./definitions"))
294
+ end
295
+ hierarchical_asset_names(clause, num)
296
+ end
297
+
298
+ def annex_names1(clause, num, level)
299
+ unless clause.at(ns("./clause | ./term | ./terms | ./definitions"))
300
+ annex_leaf_section(clause, num, level) and return
301
+ end
302
+ /\.(?<leafnum>[^.]+$)/ =~ num
303
+ @anchors[clause["id"]] = { label: leafnum, xref: "#{@annex_lbl} #{num}",
304
+ level: level, type: "clause" }
305
+ i = 1
306
+ clause.xpath(ns("./clause")).each do |c|
307
+ annex_names1(c, "#{num}.#{annex_levelnumber(i, level + 1)}", level + 1)
308
+ i += 1 if c.at(ns("./clause | ./term | ./terms | ./definitions"))
309
+ end
310
+ end
311
+
312
+ def back_anchor_names(docxml)
313
+ docxml.xpath(ns("//annex")).each_with_index do |c, i|
314
+ @paranumber = 0
315
+ annex_names(c, RomanNumerals.to_roman(i + 1))
316
+ end
317
+ docxml.xpath(ns("//bibitem[not(ancestor::bibitem)]")).each do |ref|
318
+ reference_names(ref)
319
+ end
320
+ end
321
+
322
+ def sequential_admonition_names(clause)
323
+ i = 0
324
+ clause.xpath(ns(".//admonition")).each do |t|
325
+ i += 1
326
+ next if t["id"].nil? || t["id"].empty?
327
+ @anchors[t["id"]] = anchor_struct(i.to_s, nil, @admonition_lbl, "box")
328
+ end
329
+ end
330
+
331
+ def hierarchical_admonition_names(clause, num)
332
+ i = 0
333
+ clause.xpath(ns(".//admonition")).each do |t|
334
+ i += 1
335
+ next if t["id"].nil? || t["id"].empty?
336
+ @anchors[t["id"]] = anchor_struct("#{num}.#{i}", nil, @admonition_lbl, "box")
337
+ end
338
+ end
339
+
340
+ def sequential_asset_names(clause)
341
+ super
342
+ sequential_admonition_names(clause)
343
+ end
344
+
345
+ def hierarchical_asset_names(clause, num)
346
+ super
347
+ hierarchical_admonition_names(clause, num)
348
+ end
349
+
350
+ def admonition_name_parse(node, div, name)
351
+ div.p **{ class: "FigureTitle", align: "center" } do |p|
352
+ p << l10n("#{@admonition_lbl} #{get_anchors[node['id']][:label]}")
353
+ if name
354
+ p << "&nbsp;&mdash; "
355
+ name.children.each { |n| parse(n, div) }
356
+ end
357
+ end
358
+ end
359
+
360
+ def admonition_parse(node, out)
361
+ name = node.at(ns("./name"))
362
+ out.div **{ class: "Admonition" } do |t|
363
+ admonition_name_parse(node, t, name) if name
364
+ node.children.each do |n|
365
+ parse(n, t) unless n.name == "name"
366
+ end
367
+ end
368
+ end
369
+
370
+ def inline_header_title(out, node, c1)
371
+ title = c1&.content || ""
372
+ out.span **{ class: "zzMoveToFollowing" } do |s|
373
+ if get_anchors[node['id']][:label]
374
+ s << "#{get_anchors[node['id']][:label]}. " unless @suppressheadingnumbers
375
+ insert_tab(s, 1)
376
+ end
377
+ s << "#{title} "
378
+ end
379
+ end
380
+
381
+ def abstract(isoxml, out)
382
+ f = isoxml.at(ns("//abstract")) || return
383
+ page_break(out)
384
+ out.div **attr_code(id: f["id"]) do |s|
385
+ s.h1(**{ class: "AbstractTitle" }) { |h1| h1 << "Summary" }
386
+ f.elements.each { |e| parse(e, s) unless e.name == "title" }
387
+ end
388
+ end
389
+ end
390
+ end
391
+ end
392
+
@@ -0,0 +1,93 @@
1
+ require "isodoc"
2
+ require "twitter_cldr"
3
+
4
+ module IsoDoc
5
+ module Unece
6
+
7
+ class Metadata < IsoDoc::Metadata
8
+ def initialize(lang, script, labels)
9
+ super
10
+ set(:status, "XXX")
11
+ end
12
+
13
+ def title(isoxml, _out)
14
+ main = isoxml&.at(ns("//bibdata/title[@language='en']"))&.text
15
+ set(:doctitle, main)
16
+ end
17
+
18
+ def subtitle(isoxml, _out)
19
+ main = isoxml&.at(ns("//bibdata/subtitle[@language='en']"))&.text
20
+ set(:docsubtitle, main)
21
+ end
22
+
23
+ def author(isoxml, _out)
24
+ tc = isoxml.at(ns("//bibdata/editorialgroup/committee"))
25
+ set(:tc, tc.text) if tc
26
+ set(:session_number, isoxml&.at(ns("//bibdata/session/number"))&.text&.to_i&.
27
+ localize&.to_rbnf_s("SpelloutRules", "spellout-ordinal")&.capitalize)
28
+ set(:session_date, isoxml&.at(ns("//bibdata/session/date"))&.text)
29
+ set(:session_agendaitem, isoxml&.at(ns("//bibdata/session/agenda_item"))&.text)
30
+ set(:session_collaborator, isoxml&.at(ns("//bibdata/session/collaborator"))&.text)
31
+ set(:session_id, isoxml&.at(ns("//bibdata/session/id"))&.text)
32
+ set(:session_distribution, isoxml&.at(ns("//bibdata/session/distribution"))&.text)
33
+ set(:language, isoxml&.at(ns("//bibdata/language"))&.text)
34
+ end
35
+
36
+ def docid(isoxml, _out)
37
+ docnumber = isoxml.at(ns("//bibdata/docidentifier"))
38
+ docstatus = isoxml.at(ns("//bibdata/status"))
39
+ dn = docnumber&.text
40
+ if docstatus
41
+ set(:status, status_print(docstatus.text))
42
+ abbr = status_abbr(docstatus.text)
43
+ dn = "#{dn}(#{abbr})" unless abbr.empty?
44
+ end
45
+ set(:docnumber, dn)
46
+ type = isoxml&.at(ns("//bibdata/@type"))&.value
47
+ set(:formatted_docnumber, type == "recommendation" ? "Recommendation No. #{dn}" : dn)
48
+ end
49
+
50
+ def status_print(status)
51
+ status.split(/-/).map{ |w| w.capitalize }.join(" ")
52
+ end
53
+
54
+ def status_abbr(status)
55
+ case status
56
+ when "working-draft" then "wd"
57
+ when "committee-draft" then "cd"
58
+ when "draft-standard" then "d"
59
+ else
60
+ ""
61
+ end
62
+ end
63
+
64
+ def version(isoxml, _out)
65
+ super
66
+ revdate = get[:revdate]
67
+ set(:revdate_monthyear, monthyr(revdate))
68
+ end
69
+
70
+ MONTHS = {
71
+ "01": "January",
72
+ "02": "February",
73
+ "03": "March",
74
+ "04": "April",
75
+ "05": "May",
76
+ "06": "June",
77
+ "07": "July",
78
+ "08": "August",
79
+ "09": "September",
80
+ "10": "October",
81
+ "11": "November",
82
+ "12": "December",
83
+ }.freeze
84
+
85
+ def monthyr(isodate)
86
+ m = /(?<yr>\d\d\d\d)-(?<mo>\d\d)/.match isodate
87
+ return isodate unless m && m[:yr] && m[:mo]
88
+ return "#{MONTHS[m[:mo].to_sym]} #{m[:yr]}"
89
+ end
90
+
91
+ end
92
+ end
93
+ end