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.
@@ -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)