metanorma-nist 0.0.8 → 0.0.9
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/README.adoc +137 -47
- data/lib/asciidoctor/nist/biblio.rng +22 -1
- data/lib/asciidoctor/nist/cleanup.rb +6 -6
- data/lib/asciidoctor/nist/converter.rb +1 -1
- data/lib/asciidoctor/nist/front.rb +41 -21
- data/lib/asciidoctor/nist/isodoc.rng +46 -15
- data/lib/asciidoctor/nist/isostandard.rng +3 -50
- data/lib/asciidoctor/nist/nist.rng +3 -38
- data/lib/asciidoctor/nist/nist_intro.xml +19 -15
- data/lib/isodoc/nist/base_convert.rb +86 -106
- data/lib/isodoc/nist/html/header.html +2 -2
- data/lib/isodoc/nist/html/html_nist_titlepage.html +1 -1
- data/lib/isodoc/nist/html/htmlstyle.scss +2 -0
- data/lib/isodoc/nist/html/nist.scss +4 -0
- data/lib/isodoc/nist/html/word_nist_titlepage.html +20 -11
- data/lib/isodoc/nist/html/wordstyle.scss +12 -12
- data/lib/isodoc/nist/html_convert.rb +7 -12
- data/lib/isodoc/nist/metadata.rb +136 -74
- data/lib/isodoc/nist/pdf_convert.rb +7 -12
- data/lib/isodoc/nist/render.rb +352 -0
- data/lib/isodoc/nist/word_convert.rb +14 -4
- data/lib/metanorma-nist.rb +1 -0
- data/lib/metanorma/nist/version.rb +1 -1
- data/metanorma-nist.gemspec +1 -0
- metadata +3 -2
@@ -83,8 +83,10 @@ module IsoDoc
|
|
83
83
|
|
84
84
|
def make_body3(body, docxml)
|
85
85
|
body.div **{ class: "main-section" } do |div3|
|
86
|
+
foreword docxml, div3
|
86
87
|
abstract docxml, div3
|
87
88
|
keywords docxml, div3
|
89
|
+
boilerplate docxml, div3
|
88
90
|
preface docxml, div3
|
89
91
|
middle docxml, div3
|
90
92
|
footnotes div3
|
@@ -96,6 +98,11 @@ module IsoDoc
|
|
96
98
|
dest = docxml.at("//div[@id = 'authority']") || return
|
97
99
|
auth = docxml.at("//div[@class = 'authority']") || return
|
98
100
|
dest.replace(auth.remove)
|
101
|
+
a = docxml.at("//div[@id = 'authority1']") and a["class"] = "authority1"
|
102
|
+
a = docxml.at("//div[@id = 'authority2']") and a["class"] = "authority2"
|
103
|
+
a = docxml.at("//div[@id = 'authority3']") and a["class"] = "authority3"
|
104
|
+
a = docxml.at("//div[@id = 'authority4']") and a["class"] = "authority4"
|
105
|
+
a = docxml.at("//div[@id = 'authority5']") and a["class"] = "authority5"
|
99
106
|
end
|
100
107
|
|
101
108
|
def cleanup(docxml)
|
@@ -141,18 +148,6 @@ module IsoDoc
|
|
141
148
|
end
|
142
149
|
end
|
143
150
|
|
144
|
-
def pseudocode_parse(node, out)
|
145
|
-
@in_figure = true
|
146
|
-
name = node.at(ns("./name"))
|
147
|
-
out.div **attr_code(id: node["id"], class: "pseudocode") do |div|
|
148
|
-
node.children.each do |n|
|
149
|
-
parse(n, div) unless n.name == "name"
|
150
|
-
end
|
151
|
-
figure_name_parse(node, div, name) if name
|
152
|
-
end
|
153
|
-
@in_figure = false
|
154
|
-
end
|
155
|
-
|
156
151
|
def termdef_parse(node, out)
|
157
152
|
pref = node.at(ns("./preferred"))
|
158
153
|
out.dl **{ class: "terms_dl" } do |dl|
|
@@ -0,0 +1,352 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
require "twitter_cldr"
|
3
|
+
|
4
|
+
module Iso690Render
|
5
|
+
=begin
|
6
|
+
Out of scope: Provenance (differentiating elements by @source in rendering)
|
7
|
+
=end
|
8
|
+
|
9
|
+
def self.render(bib, embedded = false)
|
10
|
+
docxml = Nokogiri::XML(bib)
|
11
|
+
docxml.remove_namespaces!
|
12
|
+
parse(docxml.root, embedded)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.multiplenames_and(names)
|
16
|
+
return "" if names.length == 0
|
17
|
+
return names[0] if names.length == 1
|
18
|
+
return "#{names[0]} and #{names[1]}" if names.length == 2
|
19
|
+
names[0..-2].join(", ") + " and #{names[-1]}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.multiplenames(names)
|
23
|
+
names.join(", ")
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.extract_orgname(org)
|
27
|
+
name = org.at("./name")
|
28
|
+
name&.text || "--"
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.frontname(given, initials)
|
32
|
+
if given.empty? && initials.empty? then ""
|
33
|
+
elsif initials.empty?
|
34
|
+
given.map{ |m| m.text[0] }.join("")
|
35
|
+
else
|
36
|
+
initials.map{ |m| m.text }.join("")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.commajoin(a, b)
|
41
|
+
return a unless b
|
42
|
+
return b unless a
|
43
|
+
#"#{a}, #{b}"
|
44
|
+
"#{a} #{b}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.extract_personname(person)
|
48
|
+
completename = person.at("./name/completename")
|
49
|
+
return completename.text if completename
|
50
|
+
surname = person.at("./name/surname")
|
51
|
+
initials = person.xpath("./name/initials")
|
52
|
+
forenames = person.xpath("./name/forename")
|
53
|
+
#given = []
|
54
|
+
#forenames.each { |x| given << x.text }
|
55
|
+
#given.empty? && initials.each { |x| given << x.text }
|
56
|
+
commajoin(surname&.text, frontname(forenames, initials))
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.extractname(contributor)
|
60
|
+
org = contributor.at("./organization")
|
61
|
+
person = contributor.at("./person")
|
62
|
+
return extract_orgname(org) if org
|
63
|
+
return extract_personname(person) if person
|
64
|
+
"--"
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.contributorRole(contributors)
|
68
|
+
return "" unless contributors.length > 0
|
69
|
+
if contributors[0]["role"] == "editor"
|
70
|
+
return contributors.length > 1 ? " (Eds.)" : "(Ed.)"
|
71
|
+
end
|
72
|
+
""
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.creatornames(doc)
|
76
|
+
cr = doc.xpath("./contributor[role/@type = 'author']")
|
77
|
+
cr.empty? and cr = doc.xpath("./contributor[role/@type = 'performer']")
|
78
|
+
cr.empty? and cr = doc.xpath("./contributor[role/@type = 'adapter']")
|
79
|
+
cr.empty? and cr = doc.xpath("./contributor[role/@type = 'translator']")
|
80
|
+
cr.empty? and cr = doc.xpath("./contributor[role/@type = 'editor']")
|
81
|
+
cr.empty? and cr = doc.xpath("./contributor[role/@type = 'publisher']")
|
82
|
+
cr.empty? and cr = doc.xpath("./contributor[role/@type = 'distributor']")
|
83
|
+
cr.empty? and cr = doc.xpath("./contributor")
|
84
|
+
cr.empty? and return ""
|
85
|
+
ret = []
|
86
|
+
cr.each do |x|
|
87
|
+
ret << extractname(x)
|
88
|
+
end
|
89
|
+
multiplenames(ret) + contributorRole(cr)
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.title(doc)
|
93
|
+
doc&.at("./title")&.text
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.medium(doc)
|
97
|
+
doc&.at("./medium")&.text
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.edition(doc)
|
101
|
+
x = doc.at("./edition")
|
102
|
+
return "" unless x
|
103
|
+
return x.text unless /^\d+$/.match x.text
|
104
|
+
x.text.to_i.localize.to_rbnf_s("SpelloutRules", "spellout-ordinal")
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.is_nist(doc)
|
108
|
+
publisher = doc&.at("./contributor[role/@type = 'publisher']/organization/name")&.text
|
109
|
+
abbr = doc&.at("./contributor[role/@type = 'publisher']/organization/abbreviation")&.text
|
110
|
+
publisher == "NIST" || abbr == "NIST"
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.placepub(doc)
|
114
|
+
place = doc&.at("./place")&.text
|
115
|
+
publisher = doc&.at("./contributor[role/@type = 'publisher']/organization/name")&.text
|
116
|
+
abbr = doc&.at("./contributor[role/@type = 'publisher']/organization/abbreviation")&.text
|
117
|
+
series = series_title(doc)
|
118
|
+
series == "NIST Federal Information Processing Standards" and
|
119
|
+
return "U.S. Department of Commerce, Washington, D.C."
|
120
|
+
is_nist(doc) and
|
121
|
+
return "National Institute of Standards and Technology, Gaithersburg, MD"
|
122
|
+
ret = ""
|
123
|
+
ret += place if place
|
124
|
+
ret += ": " if place && publisher
|
125
|
+
ret += publisher if publisher
|
126
|
+
ret
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.date1(date)
|
130
|
+
return nil if date.nil?
|
131
|
+
on = date&.at("./on")&.text
|
132
|
+
from = date&.at("./from")&.text
|
133
|
+
to = date&.at("./to")&.text
|
134
|
+
return MMMddyyyy(on) if on
|
135
|
+
return "#{MMMddyyyy(from)}–#{MMMMddyyyy(to)}" if from
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.date(doc)
|
140
|
+
updated = date1(doc&.at("./date[@type = 'updated']"))
|
141
|
+
pub = date1(doc&.at("./date[@type = 'issued']"))
|
142
|
+
if pub
|
143
|
+
ret = pub
|
144
|
+
ret += " (updated #{updated})" if updated
|
145
|
+
return pub
|
146
|
+
end
|
147
|
+
pub = date1(doc&.at("./date[@type = 'circulated']")) and
|
148
|
+
return pub
|
149
|
+
date1(doc&.at("./date"))
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.year(date)
|
153
|
+
return nil if date.nil?
|
154
|
+
date.sub(/^(\d\d\d\d).*$/, "\\1")
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.series_title(doc)
|
158
|
+
s = doc.at("./series[@type = 'main']") ||
|
159
|
+
doc.at("./series[not(@type)]") ||
|
160
|
+
doc.at("./series")
|
161
|
+
s&.at("./title")&.text
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.series(doc, type)
|
165
|
+
s = doc.at("./series[@type = 'main']") ||
|
166
|
+
doc.at("./series[not(@type)]") ||
|
167
|
+
doc.at("./series")
|
168
|
+
return "" unless s
|
169
|
+
f = s.at("./formattedref") and return r.text
|
170
|
+
t = s.at("./title")
|
171
|
+
a = s.at("./abbreviation")
|
172
|
+
n = s.at("./number")
|
173
|
+
p = s.at("./partnumber")
|
174
|
+
dn = doc.at("./docnumber")
|
175
|
+
rev = doc.at(".//revision")
|
176
|
+
ret = ""
|
177
|
+
if t
|
178
|
+
title = included(type) ? wrap(t.text, " <I>", "</I>") : wrap(t.text, " ", "")
|
179
|
+
ret += title
|
180
|
+
ret += " (#{a.text.sub(/^NIST /, "")})" if a
|
181
|
+
end
|
182
|
+
if n || p
|
183
|
+
ret += " #{n.text}" if n
|
184
|
+
ret += ".#{p.text}" if p
|
185
|
+
elsif dn && is_nist(doc)
|
186
|
+
ret += " #{dn.text}"
|
187
|
+
ret += " Rev. #{rev.text}" if rev
|
188
|
+
end
|
189
|
+
ret
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.standardidentifier(doc)
|
193
|
+
ret = []
|
194
|
+
doc.xpath("./docidentifier").each do |id|
|
195
|
+
next if %w(nist-mr nist-long).include? id["type"]
|
196
|
+
ret << standardidentifier1(id)
|
197
|
+
end
|
198
|
+
ret.join(". ")
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.standardidentifier1(id)
|
202
|
+
r = ""
|
203
|
+
r += "#{id['type']} " if id["type"] and
|
204
|
+
!%w(ISO IEC nist).include? id["type"]
|
205
|
+
r += id.text
|
206
|
+
r
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.uri(doc)
|
210
|
+
uri = doc.at("./uri[@type = 'doi']") ||
|
211
|
+
doc.at("./uri[@type = 'uri']") ||
|
212
|
+
doc.at("./uri")
|
213
|
+
uri&.text
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.accessLocation(doc)
|
217
|
+
s = doc.at("./accessLocation") or return ""
|
218
|
+
s.text
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.included(type)
|
222
|
+
["article", "inbook", "incollection", "inproceedings"].include? type
|
223
|
+
end
|
224
|
+
|
225
|
+
def self.wrap(text, startdelim = " ", enddelim = ".")
|
226
|
+
return "" if text.nil? || text.empty?
|
227
|
+
"#{startdelim}#{text}#{enddelim}"
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.type(doc)
|
231
|
+
type = doc.at("./@type") and return type&.text
|
232
|
+
doc.at("./includedIn") and return "inbook"
|
233
|
+
"book"
|
234
|
+
end
|
235
|
+
|
236
|
+
def self.extent1(type, from, to)
|
237
|
+
ret = ""
|
238
|
+
if type == "page"
|
239
|
+
type = to ? "pp." : "p"
|
240
|
+
end
|
241
|
+
ret += "#{type} "
|
242
|
+
ret += from.text if from
|
243
|
+
ret += "–#{to.text}" if to
|
244
|
+
ret
|
245
|
+
end
|
246
|
+
|
247
|
+
def self.extent(localities)
|
248
|
+
ret = []
|
249
|
+
localities.each do |l|
|
250
|
+
ret << extent1(l["type"] || "page",
|
251
|
+
l.at("./referenceFrom"), l.at("./referenceTo"))
|
252
|
+
end
|
253
|
+
ret.join(", ")
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.monthyr(isodate)
|
257
|
+
return nil if isodate.nil?
|
258
|
+
DateTime.parse(isodate).localize(:en).to_additional_s("yMMMM")
|
259
|
+
end
|
260
|
+
|
261
|
+
def self.mmddyyyy(isodate)
|
262
|
+
return nil if isodate.nil?
|
263
|
+
Date.parse(isodate).strftime("%m-%d-%Y")
|
264
|
+
end
|
265
|
+
|
266
|
+
def self.MMMddyyyy(isodate)
|
267
|
+
return nil if isodate.nil?
|
268
|
+
Date.parse(isodate).strftime("%B %d, %Y")
|
269
|
+
end
|
270
|
+
|
271
|
+
def self.draft(doc)
|
272
|
+
return nil unless is_nist(doc)
|
273
|
+
dr = doc&.at("./status/stage")&.text
|
274
|
+
iter = doc&.at("./status/iteration")&.text
|
275
|
+
return nil unless /^draft/.match(dr)
|
276
|
+
iterord = iter_ordinal(doc)
|
277
|
+
status = status_print(dr)
|
278
|
+
status = "#{iterord} #{status}" if iterord
|
279
|
+
status
|
280
|
+
end
|
281
|
+
|
282
|
+
def self.iter_ordinal(isoxml)
|
283
|
+
docstatus = isoxml.at(("./status/stage"))&.text
|
284
|
+
return nil unless docstatus == "draft-public"
|
285
|
+
iter = isoxml.at(("./status/iteration"))&.text || "1"
|
286
|
+
return "Initial" if iter == "1"
|
287
|
+
return "Final" if iter.downcase == "final"
|
288
|
+
iter.to_i.localize.to_rbnf_s("SpelloutRules", "spellout-ordinal").capitalize
|
289
|
+
end
|
290
|
+
|
291
|
+
def self.status_print(status)
|
292
|
+
case status
|
293
|
+
when "draft-internal" then "Internal Draft"
|
294
|
+
when "draft-wip" then "Work-in-Progress Draft"
|
295
|
+
when "draft-prelim" then "Preliminary Draft"
|
296
|
+
when "draft-public" then "Public Draft"
|
297
|
+
when "draft-retire" then "Retired Draft"
|
298
|
+
when "draft-withdrawn" then "Withdrawn Draft"
|
299
|
+
when "final" then "Final"
|
300
|
+
when "final-review" then "Under Review"
|
301
|
+
when "final-withdrawn" then "Withdrawn"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def self.parse(doc, embedded = false)
|
306
|
+
ret = ""
|
307
|
+
type = type(doc)
|
308
|
+
container = doc.at("./relation[@type='includedIn']")
|
309
|
+
if container && date(doc) && !date(container)
|
310
|
+
container("./bibitem") <<
|
311
|
+
( doc.at("./date[@type = 'issued']").remove || doc.at("./date[@type = 'circulated']").remove )
|
312
|
+
end
|
313
|
+
ser = series_title(doc)
|
314
|
+
dr = draft(doc)
|
315
|
+
|
316
|
+
# NIST has seen fit to completely change rendering based on the type of publication.
|
317
|
+
if ser == "NIST Federal Information Processing Standards"
|
318
|
+
ret += "National Institute of Standards and Technology"
|
319
|
+
else
|
320
|
+
ret += embedded ? wrap(creatornames(doc), "", "") : wrap(creatornames(doc), "", "")
|
321
|
+
end
|
322
|
+
|
323
|
+
if dr
|
324
|
+
mdy = MMMddyyyy(date(doc)) and ret += wrap(mdy, " (", ")")
|
325
|
+
else
|
326
|
+
yr = year(date(doc)) and ret += wrap(yr, " (", ")")
|
327
|
+
end
|
328
|
+
ret += included(type) ? wrap(title(doc)) : wrap(title(doc), " <I>", "</I>.")
|
329
|
+
ret += wrap(medium(doc), " [", "].")
|
330
|
+
#ret += wrap(edition(doc), "", " edition.")
|
331
|
+
s = series(doc, type)
|
332
|
+
ret += wrap(placepub(doc), " (", "),")
|
333
|
+
if dr
|
334
|
+
ret += " Draft (#{dr})"
|
335
|
+
end
|
336
|
+
ret += wrap(series(doc, type), " ", "")
|
337
|
+
ret += "," if series(doc, type) && date(doc)
|
338
|
+
ret += wrap(date(doc))
|
339
|
+
ret += wrap(standardidentifier(doc)) unless is_nist(doc)
|
340
|
+
ret += wrap(uri(doc))
|
341
|
+
ret += wrap(accessLocation(doc), "At: ", ".")
|
342
|
+
if container
|
343
|
+
ret += wrap(parse(container.at("./bibitem").to_xml, true), " In:", "")
|
344
|
+
locality = container.xpath("./locality")
|
345
|
+
locality.empty? and locality = doc.xpath("./extent")
|
346
|
+
ret += wrap(extent(locality))
|
347
|
+
else
|
348
|
+
ret += wrap(extent(doc.xpath("./extent")))
|
349
|
+
end
|
350
|
+
embedded ? ret : "<p>#{ret}</p>"
|
351
|
+
end
|
352
|
+
end
|
@@ -62,8 +62,10 @@ module IsoDoc
|
|
62
62
|
body.div **{ class: "WordSection2" } do |div2|
|
63
63
|
@prefacenum = 0
|
64
64
|
info docxml, div2
|
65
|
+
foreword docxml, div2
|
65
66
|
abstract docxml, div2
|
66
67
|
keywords docxml, div2
|
68
|
+
boilerplate docxml, div2
|
67
69
|
preface docxml, div2
|
68
70
|
div2.p { |p| p << " " } # placeholder
|
69
71
|
end
|
@@ -74,6 +76,11 @@ module IsoDoc
|
|
74
76
|
insert = docxml.at("//div[@class = 'WordSection2']")
|
75
77
|
auth = docxml&.at("//div[@class = 'authority']")&.remove || return
|
76
78
|
insert.children.first.add_previous_sibling(auth)
|
79
|
+
a = docxml.at("//div[@id = 'authority1']") and a["class"] = "authority1"
|
80
|
+
a = docxml.at("//div[@id = 'authority2']") and a["class"] = "authority2"
|
81
|
+
a = docxml.at("//div[@id = 'authority3']") and a["class"] = "authority3"
|
82
|
+
a = docxml.at("//div[@id = 'authority4']") and a["class"] = "authority4"
|
83
|
+
a = docxml.at("//div[@id = 'authority5']") and a["class"] = "authority5"
|
77
84
|
end
|
78
85
|
|
79
86
|
def cleanup(docxml)
|
@@ -82,6 +89,7 @@ module IsoDoc
|
|
82
89
|
requirement_cleanup(docxml)
|
83
90
|
h1_cleanup(docxml)
|
84
91
|
word_annex_cleanup(docxml) # need it earlier
|
92
|
+
word_preface_cleanup(docxml) # need it earlier, since early ToC insertion
|
85
93
|
toc_insert(docxml, @wordToClevels)
|
86
94
|
end
|
87
95
|
|
@@ -154,7 +162,8 @@ module IsoDoc
|
|
154
162
|
TOC
|
155
163
|
|
156
164
|
def header_strip(h)
|
157
|
-
h = h.to_s.gsub(/<\/?p[^>]*>/, "")
|
165
|
+
h = h.to_s.gsub(/<\/?p[^>]*>/, "").
|
166
|
+
gsub(%r{<a [^>]+>[^<]+</a><aside>.*</aside>}m, "")
|
158
167
|
super
|
159
168
|
end
|
160
169
|
|
@@ -188,15 +197,16 @@ module IsoDoc
|
|
188
197
|
|
189
198
|
def word_preface_cleanup(docxml)
|
190
199
|
docxml.xpath("//h1[@class = 'AbstractTitle'] | "\
|
191
|
-
"//h1[@class = 'IntroTitle'] |
|
200
|
+
"//h1[@class = 'IntroTitle'] | "\
|
201
|
+
"//h1[@class = 'ForewordTitle'] |
|
192
202
|
//h1[parent::div/@class = 'authority']").each do |h2|
|
193
203
|
h2.name = "p"
|
194
204
|
h2["class"] = "h1Preface"
|
195
205
|
end
|
196
|
-
|
206
|
+
docxml.xpath("//h2[ancestor::div/@class = 'authority']").each do |h2|
|
197
207
|
h2.name = "p"
|
198
208
|
h2["class"] = "h2Preface"
|
199
|
-
|
209
|
+
end
|
200
210
|
end
|
201
211
|
|
202
212
|
def word_cleanup(docxml)
|