require "isodoc"
require_relative "metadata"
require "fileutils"
module IsoDoc
module NIST
# A {Converter} implementation that generates HTML output, and a document
# schema encapsulation of the document for validation
class HtmlConvert < IsoDoc::HtmlConvert
def initialize(options)
@libdir = File.dirname(__FILE__)
super
end
def convert1(docxml, filename, dir)
@bibliographycount = docxml.xpath(ns("//bibliography/references | //annex/references | //bibliography/clause/references")).size
FileUtils.cp html_doc_path('logo.png'), "#{@localdir}/logo.png"
FileUtils.cp html_doc_path('commerce-logo-color.png'), "#{@localdir}/commerce-logo-color.png"
@files_to_delete << "#{@localdir}/logo.png"
@files_to_delete << "#{@localdir}/commerce-logo-color.png"
super
end
def default_fonts(options)
{
bodyfont: (options[:script] == "Hans" ? '"SimSun",serif' : '"Libre Baskerville",serif'),
headerfont: (options[:script] == "Hans" ? '"SimHei",sans-serif' : '"Libre Baskerville",serif'),
monospacefont: '"Space Mono",monospace'
}
end
def default_file_locations(_options)
{
htmlstylesheet: html_doc_path("htmlstyle.scss"),
htmlcoverpage: html_doc_path("html_nist_titlepage.html"),
htmlintropage: html_doc_path("html_nist_intro.html"),
scripts: html_doc_path("scripts.html"),
}
end
def metadata_init(lang, script, labels)
@meta = Metadata.new(lang, script, labels)
end
def googlefonts
<<~HEAD.freeze
HEAD
end
def make_body(xml, docxml)
body_attr = { lang: "EN-US", link: "blue", vlink: "#954F72", "xml:lang": "EN-US", class: "container" }
xml.body **body_attr do |body|
make_body1(body, docxml)
make_body2(body, docxml)
make_body3(body, docxml)
end
end
def html_toc(docxml)
docxml
end
def authority_cleanup(docxml)
dest = docxml.at("//div[@id = 'authority']") || return
auth = docxml.at("//div[@class = 'authority']") || return
dest.replace(auth.remove)
end
def cleanup(docxml)
super
term_cleanup(docxml)
requirement_cleanup(docxml)
end
def html_preface(docxml)
super
authority_cleanup(docxml)
docxml
end
def make_body3(body, docxml)
body.div **{ class: "main-section" } do |div3|
abstract docxml, div3
keywords docxml, div3
preface docxml, div3
middle docxml, div3
footnotes div3
comments div3
end
end
def bibliography(isoxml, out)
f = isoxml.at(ns("//bibliography/clause | //bibliography/references")) || return
page_break(out)
isoxml.xpath(ns("//bibliography/clause | //bibliography/references")).each do |f|
out.div do |div|
div.h1 **{ class: "Section3" } do |h1|
if @bibliographycount == 1
h1 << "References"
else
f&.at(ns("./title"))&.children.each { |n| parse(n, h1) }
end
end
f.elements.reject do |e|
["reference", "title", "bibitem"].include? e.name
end.each { |e| parse(e, div) }
biblio_list(f, div, false)
end
end
end
# common from here on
def abstract(isoxml, out)
f = isoxml.at(ns("//preface/abstract")) || return
page_break(out)
out.div **attr_code(id: f["id"]) do |s|
clause_name(nil, @abstract_lbl, s, class: "AbstractTitle")
f.elements.each { |e| parse(e, s) unless e.name == "title" }
end
end
def keywords(_docxml, out)
kw = @meta.get[:keywords]
kw.empty? and return
out.div **{ class: "Section3" } do |div|
clause_name(nil, "Keywords", div, class: "IntroTitle")
div.p kw.sort.join("; ")
end
end
FRONT_CLAUSE = "//*[parent::preface][not(local-name() = 'abstract')]".freeze
def preface(isoxml, out)
isoxml.xpath(ns(FRONT_CLAUSE)).each do |c|
foreword(isoxml, out) and next if c.name == "foreword"
authority_parse(c, out) and next if c.name == "authority"
next if skip_render(c, isoxml)
out.div **attr_code(id: c["id"]) do |s|
clause_name(get_anchors[c['id']][:label],
c&.at(ns("./title"))&.content, s, nil)
c.elements.reject { |c1| c1.name == "title" }.each do |c1|
parse(c1, s)
end
end
end
end
def skip_render(c, isoxml)
return false unless c.name == "reviewernote"
status = isoxml&.at(ns("//bibdata/status/stage"))&.text
return true if status.nil?
return ["final", "withdrawn"].include? status
end
def term_defs_boilerplate(div, source, term, preface)
if source.empty? && term.nil?
div << @no_terms_boilerplate
else
div << term_defs_boilerplate_cont(source, term)
end
end
def i18n_init(lang, script)
super
end
def fileloc(loc)
File.join(File.dirname(__FILE__), loc)
end
def requirement_cleanup(docxml)
docxml.xpath("//div[@class = 'recommend' or @class = 'require' "\
"or @class = 'permission'][title]").each do |d|
title = d.at("./title")
title.name = "b"
n = title.next_element
n&.children&.first&.add_previous_sibling(" ")
n&.children&.first&.add_previous_sibling(title.remove)
end
docxml
end
def figure_parse(node, out)
return pseudocode_parse(node, out) if node["type"] == "pseudocode"
super
end
def pseudocode_parse(node, out)
@in_figure = true
name = node.at(ns("./name"))
out.div **attr_code(id: node["id"], class: "pseudocode") do |div|
node.children.each do |n|
parse(n, div) unless n.name == "name"
end
figure_name_parse(node, div, name) if name
end
@in_figure = false
end
def dl_parse(node, out)
return glossary_parse(node, out) if node["type"] == "glossary"
super
end
def glossary_parse(node, out)
out.dl **attr_code(id: node["id"], class: "glossary") do |v|
node.elements.select { |n| dt_dd? n }.each_slice(2) do |dt, dd|
v.dt **attr_code(id: dt["id"]) do |term|
dt_parse(dt, term)
end
v.dd **attr_code(id: dd["id"]) do |listitem|
dd.children.each { |n| parse(n, listitem) }
end
end
end
node.elements.reject { |n| dt_dd? n }.each { |n| parse(n, out) }
end
def error_parse(node, out)
case node.name
when "nistvariable" then nistvariable_parse(node, out)
when "recommendation" then recommendation_parse(node, out)
when "requirement" then requirement_parse(node, out)
when "permission" then permission_parse(node, out)
when "errata" then errata_parse(node, out)
when "authority" then authority_parse(node, out)
when "authority1" then authority1_parse(node, out, "authority1")
when "authority2" then authority1_parse(node, out, "authority2")
when "authority3" then authority1_parse(node, out, "authority3")
when "authority4" then authority1_parse(node, out, "authority4")
when "authority5" then authority1_parse(node, out, "authority5")
else
super
end
end
def authority_parse(node, out)
out.div **{class: "authority"} do |s|
node.children.each do |n|
if n.name == "title"
s.h1 do |h|
n.children.each { |nn| parse(nn, h) }
end
else
parse(n, s)
end
end
end
end
def authority1_parse(node, out, classname)
out.div **{class: classname} do |s|
node.children.each do |n|
if n.name == "title"
s.h2 do |h|
n.children.each { |nn| parse(nn, h) }
end
else
parse(n, s)
end
end
end
end
def nistvariable_parse(node, out)
out.span **{class: "nistvariable"} do |s|
node.children.each { |n| parse(n, s) }
end
end
def recommendation_parse(node, out)
name = node["type"]
out.div **{ class: "recommend" } do |t|
t.title { |b| b << "Recommendation #{get_anchors[node['id']][:label]}:" }
node.children.each do |n|
parse(n, t)
end
end
end
def requirement_parse(node, out)
name = node["type"]
out.div **{ class: "require" } do |t|
t.title { |b| b << "Requirement #{get_anchors[node['id']][:label]}:" }
node.children.each do |n|
parse(n, t)
end
end
end
def permission_parse(node, out)
name = node["type"]
out.div **{ class: "permission" } do |t|
t.title { |b| b << "Permission #{get_anchors[node['id']][:label]}:" }
node.children.each do |n|
parse(n, t)
end
end
end
def errata_parse(node, out)
out.table **make_table_attr(node) do |t|
t.thead do |h|
h.tr do |tr|
%w(Date Type Change Pages).each do |hdr|
tr.th hdr
end
end
end
t.tbody do |b|
node.xpath(ns("./row")).each do |row|
b.tr do |tr|
tr.td do |td|
row&.at(ns("./date"))&.children.each do |n|
parse(n, td)
end
end
tr.td do |td|
row&.at(ns("./type"))&.children.each do |n|
parse(n, td)
end
end
tr.td do |td|
row&.at(ns("./change"))&.children.each do |n|
parse(n, td)
end
end
tr.td do |td|
row&.at(ns("./pages"))&.children.each do |n|
parse(n, td)
end
end
end
end
end
end
end
MIDDLE_CLAUSE = "//clause[parent::sections]|//terms[parent::sections]".freeze
def middle(isoxml, out)
middle_title(out)
clause isoxml, out
bibliography isoxml, out
annex isoxml, out
end
def info(isoxml, out)
@meta.keywords isoxml, out
@meta.series isoxml, out
@meta.commentperiod isoxml, out
@meta.commentperiod isoxml, out
super
end
SECTIONS_XPATH =
"//foreword | //introduction | //reviewnote | //execsummary | //annex | "\
"//sections/clause | //bibliography/references | "\
"//bibliography/clause".freeze
def initial_anchor_names(d)
d.xpath("//xmlns:preface/child::*").each do |c|
preface_names(c)
end
sequential_asset_names(d.xpath("//xmlns:preface/child::*"))
clause_names(d, 0)
middle_section_asset_names(d)
termnote_anchor_names(d)
termexample_anchor_names(d)
end
def back_anchor_names(docxml)
docxml.xpath(ns("//annex")).each_with_index do |c, i|
annex_names(c, (65 + i).chr.to_s)
end
docxml.xpath(ns("//bibliography/clause | "\
"//bibliography/references")).each do |b|
preface_names(b)
end
docxml.xpath(ns("//bibitem[not(ancestor::bibitem)]")).each do |ref|
reference_names(ref)
end
end
=begin
def prefaceprefix(nodes)
i = 0
nodes.each do |n|
case n.name
when "executivesummary" then @anchors[n["id"]][:prefix] = "ES"
when "abstract" then @anchors[n["id"]][:prefix] = "ABS"
when "reviewernote" then @anchors[n["id"]][:prefix] = "NTR"
else
@anchors[n["id"]][:prefix] = "PR" + i.to_s
i += 1
end
end
end
def middle_section_asset_names(d)
prefaceprefix(d.xpath("//xmlns:preface/child::*"))
d.xpath("//xmlns:preface/child::*").each do |s|
hierarchical_asset_names(s, @anchors[s["id"]][:prefix])
end
d.xpath("//xmlns:sections/child::*").each do |s|
hierarchical_asset_names(s, @anchors[s["id"]][:label])
end
end
=end
def middle_section_asset_names(d)
middle_sections =
"//xmlns:preface/child::* | //xmlns:sections/child::*"
sequential_asset_names(d.xpath(middle_sections))
end
def sequential_asset_names(clause)
super
sequential_permission_names(clause)
sequential_requirement_names(clause)
sequential_recommendation_names(clause)
end
def sequential_permission_names(clause)
clause.xpath(ns(".//permission")).each_with_index do |t, i|
next if t["id"].nil? || t["id"].empty?
@anchors[t["id"]] = anchor_struct(i + 1, t, "Permission", "permission")
end
end
def sequential_requirement_names(clause)
clause.xpath(ns(".//requirement")).each_with_index do |t, i|
next if t["id"].nil? || t["id"].empty?
@anchors[t["id"]] = anchor_struct(i + 1, t, "Requirement", "requirement")
end
end
def sequential_recommendation_names(clause)
clause.xpath(ns(".//recommendation")).each_with_index do |t, i|
next if t["id"].nil? || t["id"].empty?
@anchors[t["id"]] = anchor_struct(i + 1, t, "Recommendation", "recommendation")
end
end
def hierarchical_asset_names(clause, num)
super
hierarchical_permission_names(clause, num)
hierarchical_requirement_names(clause, num)
hierarchical_recommendation_names(clause, num)
end
def hierarchical_permission_names(clause, num)
clause.xpath(ns(".//permission")).each_with_index do |t, i|
next if t["id"].nil? || t["id"].empty?
@anchors[t["id"]] = anchor_struct("#{num}.#{i + 1}",
t, "Permission", "permission")
end
end
def hierarchical_requirement_names(clause, num)
clause.xpath(ns(".//requirement")).each_with_index do |t, i|
next if t["id"].nil? || t["id"].empty?
@anchors[t["id"]] = anchor_struct("#{num}.#{i + 1}",
t, "Requirement", "requirement")
end
end
def hierarchical_recommendation_names(clause, num)
clause.xpath(ns(".//recommendation")).each_with_index do |t, i|
next if t["id"].nil? || t["id"].empty?
@anchors[t["id"]] = anchor_struct("#{num}.#{i + 1}",
t, "Recommendation", "recommendation")
end
end
def clause_names(docxml, sect_num)
q = "//xmlns:sections/child::*"
docxml.xpath(q).each_with_index do |c, i|
section_names(c, (i + sect_num), 1)
end
end
def get_linkend(node)
link = anchor_linkend(node, docid_l10n(node["target"] || "[#{node['citeas']}]"))
link += eref_localities(node.xpath(ns("./locality")), link)
contents = node.children.select { |c| c.name != "locality" }
return link if contents.nil? || contents.empty?
Nokogiri::XML::NodeSet.new(node.document, contents).to_xml
# so not
# 3.1
end
def load_yaml(lang, script)
y = if @i18nyaml then YAML.load_file(@i18nyaml)
elsif lang == "en"
YAML.load_file(File.join(File.dirname(__FILE__), "i18n-en.yaml"))
else
YAML.load_file(File.join(File.dirname(__FILE__), "i18n-en.yaml"))
end
super.merge(y)
end
def annex_name_lbl(clause, num)
l10n("#{@annex_lbl} #{num}")
end
def annex_name(annex, name, div)
div.h1 **{ class: "Annex" } do |t|
t << "#{get_anchors[annex['id']][:label]} — "
t.b do |b|
if @bibliographycount == 1 && annex.at(ns("./references"))
b << "References"
else
name&.children&.each { |c2| parse(c2, b) }
end
end
end
end
def hiersep
"-"
end
def annex_names(clause, num)
@anchors[clause["id"]] = { label: annex_name_lbl(clause, num), type: "clause",
xref: "#{@annex_lbl} #{num}", level: 1 }
clause.xpath(ns("./clause | ./terms | ./term | ./references")).each_with_index do |c, i|
annex_names1(c, "#{num}.#{i + 1}", 2)
end
hierarchical_asset_names(clause, num)
end
def annex_names1(clause, num, level)
@anchors[clause["id"]] = { label: num, xref: "#{@annex_lbl} #{num}",
level: level, type: "clause" }
clause.xpath(ns("./clause | ./terms | ./term | ./references")).each_with_index do |c, i|
annex_names1(c, "#{num}.#{i + 1}", level + 1)
end
end
def terms_parse(node, out)
out.div **attr_code(id: node["id"]) do |div|
node.at(ns("./title")) and
clause_parse_title(node, div, node.at(ns("./title")), out)
term_defs_boilerplate(div, node.xpath(ns(".//termdocsource")),
node.at(ns(".//term")), node.at(ns("./p")))
node.elements.each do |e|
parse(e, div) unless %w{title source}.include? e.name
end
end
end
def termdef_parse(node, out)
pref = node.at(ns("./preferred"))
out.dl **{ class: "terms_dl" } do |dl|
dl.dt do |dt|
pref.children.each { |n| parse(n, dt) }
end
set_termdomain("")
dl.dd do |dd|
node.children.each { |n| parse(n, dd) unless n.name == "preferred" }
end
end
end
def term_cleanup(docxml)
docxml.xpath("//dl[@class = 'terms_dl']").each do |d|
prev = d.previous_element
next unless prev.name == "dl" and prev["class"] == "terms_dl"
d.children.each { |n| prev.add_child(n.remove) }
d.remove
end
docxml
end
def bibliography_parse(node, out)
title = node&.at(ns("./title"))&.text || ""
out.div do |div|
node.parent.name == "annex" or
div.h2 title, **{ class: "Section3" }
node.elements.reject do |e|
["reference", "title", "bibitem"].include? e.name
end.each { |e| parse(e, div) }
biblio_list(node, div, true)
end
end
end
end
end