metanorma-nist 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)}&ndash;#{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 += "&ndash;#{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 << "&nbsp;" } # 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
- docxml.xpath("//h2[ancestor::div/@class = 'authority']").each do |h2|
206
+ docxml.xpath("//h2[ancestor::div/@class = 'authority']").each do |h2|
197
207
  h2.name = "p"
198
208
  h2["class"] = "h2Preface"
199
- end
209
+ end
200
210
  end
201
211
 
202
212
  def word_cleanup(docxml)