metanorma 1.7.7 → 2.0.0
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/Gemfile +5 -1
- data/lib/metanorma/array_monkeypatch.rb +9 -0
- data/lib/metanorma/asciidoctor_extensions/glob_include_processor.rb +13 -11
- data/lib/metanorma/collection/collection.rb +225 -0
- data/lib/metanorma/collection/config/bibdata.rb +12 -0
- data/lib/metanorma/collection/config/compile_options.rb +13 -0
- data/lib/metanorma/collection/config/config.rb +163 -0
- data/lib/metanorma/collection/config/converters.rb +30 -0
- data/lib/metanorma/collection/config/directive.rb +10 -0
- data/lib/metanorma/collection/config/manifest.rb +88 -0
- data/lib/metanorma/collection/document/document.rb +133 -0
- data/lib/metanorma/collection/filelookup/filelookup.rb +250 -0
- data/lib/metanorma/collection/filelookup/filelookup_sectionsplit.rb +87 -0
- data/lib/metanorma/collection/manifest/manifest.rb +237 -0
- data/lib/metanorma/collection/renderer/fileparse.rb +247 -0
- data/lib/metanorma/collection/renderer/fileprocess.rb +173 -0
- data/lib/metanorma/collection/renderer/navigation.rb +133 -0
- data/lib/metanorma/collection/renderer/render_word.rb +133 -0
- data/lib/metanorma/collection/renderer/renderer.rb +157 -0
- data/lib/metanorma/collection/renderer/utils.rb +183 -0
- data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +218 -0
- data/lib/metanorma/collection/util/disambig_files.rb +37 -0
- data/lib/metanorma/collection/util/util.rb +72 -0
- data/lib/metanorma/collection/xrefprocess/xrefprocess.rb +222 -0
- data/lib/metanorma/{compile.rb → compile/compile.rb} +17 -11
- data/lib/metanorma/{compile_options.rb → compile/compile_options.rb} +9 -5
- data/lib/metanorma/{compile_validate.rb → compile/compile_validate.rb} +1 -1
- data/lib/metanorma/{extract.rb → compile/extract.rb} +2 -2
- data/lib/metanorma/{config.rb → config/config.rb} +1 -1
- data/lib/metanorma/input/asciidoc.rb +3 -3
- data/lib/metanorma/input/base.rb +1 -5
- data/lib/metanorma/processor/processor.rb +54 -0
- data/lib/metanorma/processor.rb +1 -49
- data/lib/metanorma/{registry.rb → registry/registry.rb} +0 -1
- data/lib/metanorma/shale_monkeypatch.rb +15 -0
- data/lib/metanorma/util/fontist_helper.rb +130 -0
- data/lib/metanorma/util/util.rb +45 -0
- data/lib/metanorma/util/worker_pool.rb +39 -0
- data/lib/metanorma/version.rb +1 -1
- data/lib/metanorma.rb +13 -8
- data/metanorma.gemspec +2 -1
- metadata +50 -24
- data/lib/metanorma/collection.rb +0 -244
- data/lib/metanorma/collection_fileparse.rb +0 -257
- data/lib/metanorma/collection_fileprocess.rb +0 -168
- data/lib/metanorma/collection_manifest.rb +0 -168
- data/lib/metanorma/collection_render_utils.rb +0 -169
- data/lib/metanorma/collection_render_word.rb +0 -131
- data/lib/metanorma/collection_renderer.rb +0 -237
- data/lib/metanorma/collection_xref_process.rb +0 -217
- data/lib/metanorma/document.rb +0 -133
- data/lib/metanorma/files_lookup.rb +0 -224
- data/lib/metanorma/files_lookup_sectionsplit.rb +0 -84
- data/lib/metanorma/fontist_utils.rb +0 -122
- data/lib/metanorma/sectionsplit.rb +0 -216
- data/lib/metanorma/util.rb +0 -127
- data/lib/metanorma/worker_pool.rb +0 -29
@@ -0,0 +1,247 @@
|
|
1
|
+
module Metanorma
|
2
|
+
class Collection
|
3
|
+
class Renderer
|
4
|
+
# Resolves references to other files in the collection. Three routines:
|
5
|
+
# 1. Eref to a document that has been split into multiple documents
|
6
|
+
# (sectionsplit) are resolved to direct eref to the split document
|
7
|
+
# 2. Indirect erefs to a file anchor in an unknwon file in the collection
|
8
|
+
# (bibitem[@type = 'internal'] ) are resolved to direct eref to the
|
9
|
+
# containing document
|
10
|
+
# 3. Direct erefs to other files in collection
|
11
|
+
# (repo(current-metanorma-collection/x) are resolved to hyperlinks
|
12
|
+
# @param file [String] XML content
|
13
|
+
# @param identifier [String] docid
|
14
|
+
# @param internal_refs [Hash{String=>Hash{String=>String}] schema name to
|
15
|
+
# anchor to filename
|
16
|
+
# @return [String] XML content
|
17
|
+
def update_xrefs(file, docid, internal_refs)
|
18
|
+
xml, sso = update_xrefs_prep(file, docid)
|
19
|
+
@nested || sso or
|
20
|
+
Metanorma::Collection::XrefProcess::xref_process(xml, xml, nil, docid,
|
21
|
+
@isodoc)
|
22
|
+
@nested or update_indirect_refs_to_docs(xml, docid, internal_refs)
|
23
|
+
@files.add_document_suffix(docid, xml)
|
24
|
+
@nested or update_sectionsplit_refs_to_docs(xml, internal_refs)
|
25
|
+
update_direct_refs_to_docs(xml, docid)
|
26
|
+
hide_refs(xml)
|
27
|
+
sso and eref2link(xml)
|
28
|
+
svgmap_resolve(xml, docid)
|
29
|
+
xml.to_xml
|
30
|
+
end
|
31
|
+
|
32
|
+
def update_xrefs_prep(file, docid)
|
33
|
+
docxml = file.is_a?(String) ? Nokogiri::XML(file, &:huge) : file
|
34
|
+
supply_repo_ids(docxml)
|
35
|
+
sso = @files.get(docid, :sectionsplit_output)
|
36
|
+
[docxml, sso]
|
37
|
+
end
|
38
|
+
|
39
|
+
def update_sectionsplit_refs_to_docs(docxml, internal_refs)
|
40
|
+
Util::gather_citeases(docxml).each do |k, v|
|
41
|
+
(@files.get(k) && @files.get(k, :sectionsplit)) or next
|
42
|
+
opts = { key: @files.get(k, :indirect_key),
|
43
|
+
source_suffix: docxml.root["document_suffix"],
|
44
|
+
target_suffix: @files.get(k, :document_suffix) }
|
45
|
+
refs = v.each_with_object({}) do |eref, m|
|
46
|
+
update_sectionsplit_eref_to_doc(eref, internal_refs, m, opts)
|
47
|
+
end
|
48
|
+
add_hidden_bibliography(docxml, refs)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def update_sectionsplit_eref_to_doc(eref, internal_refs, doclist, opts)
|
53
|
+
a = eref.at(ns("./localityStack/locality[@type = 'anchor']/" \
|
54
|
+
"referenceFrom")) or return
|
55
|
+
doc = internal_refs[opts[:key]]["#{a.text}_#{opts[:target_suffix]}"]
|
56
|
+
bibitemid = Metanorma::Utils::to_ncname("#{doc}_#{opts[:source_suffix]}")
|
57
|
+
eref["bibitemid"] = bibitemid
|
58
|
+
doclist[bibitemid] ||= doc
|
59
|
+
doclist
|
60
|
+
end
|
61
|
+
|
62
|
+
def new_hidden_ref(xmldoc)
|
63
|
+
ins = xmldoc.at(ns("bibliography")) or
|
64
|
+
xmldoc.root << "<bibliography/>" and ins = xmldoc.at(ns("bibliography"))
|
65
|
+
ins.at(ns("./referenced[@hidden = 'true']")) or
|
66
|
+
ins.add_child("<references hidden='true' normative='false'/>").first
|
67
|
+
end
|
68
|
+
|
69
|
+
def eref2link(docxml)
|
70
|
+
isodoc = IsoDoc::PresentationXMLConvert.new({})
|
71
|
+
isodoc.bibitem_lookup(docxml)
|
72
|
+
isodoc.eref2link(docxml)
|
73
|
+
end
|
74
|
+
|
75
|
+
BIBITEM_NOT_REPO_XPATH = "//bibitem[not(ancestor::bibitem)]" \
|
76
|
+
"[not(./docidentifier[@type = 'repository'])]".freeze
|
77
|
+
|
78
|
+
def supply_repo_ids(doc)
|
79
|
+
doc.xpath(ns(BIBITEM_NOT_REPO_XPATH)).each do |b|
|
80
|
+
b.xpath(ns("./docidentifier")).each do |docid|
|
81
|
+
id = @isodoc.docid_prefix(docid["type"], docid.children.to_xml)
|
82
|
+
@files.get(id) or next
|
83
|
+
@files.get(id, :indirect_key) and next # will resolve as indirect key
|
84
|
+
docid.next = docid_xml(id)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def svgmap_resolve(docxml, docid)
|
90
|
+
ids = @files.get(docid, :ids)
|
91
|
+
dir = File.join(@dirname, File.dirname(@files.get(docid, :rel_path)))
|
92
|
+
docxml = datauri_encode(docxml, dir)
|
93
|
+
isodoc = IsoDoc::PresentationXMLConvert.new({})
|
94
|
+
isodoc.bibitem_lookup(docxml)
|
95
|
+
docxml.xpath(ns("//svgmap//eref")).each do |e|
|
96
|
+
svgmap_resolve1(e, isodoc, docxml, ids)
|
97
|
+
end
|
98
|
+
Vectory::SvgMapping.new(docxml, "").call
|
99
|
+
docxml.xpath(ns("//svgmap")).each { |s| isodoc.svgmap_extract(s) }
|
100
|
+
end
|
101
|
+
|
102
|
+
def svgmap_resolve1(eref, isodoc, _docxml, ids)
|
103
|
+
href = isodoc.eref_target(eref) or return
|
104
|
+
href == "##{eref['bibitemid']}" ||
|
105
|
+
(href =~ /^#/ && !ids[href.sub(/^#/, "")]) and return
|
106
|
+
eref["target"] = href.strip
|
107
|
+
eref.name = "link"
|
108
|
+
eref.elements&.remove
|
109
|
+
end
|
110
|
+
|
111
|
+
# repo(current-metanorma-collection/ISO 17301-1:2016)
|
112
|
+
# replaced by bibdata of "ISO 17301-1:2016" in situ as bibitem.
|
113
|
+
# Any erefs to that bibitem id are replaced with relative URL
|
114
|
+
# Preferably with anchor, and is a job to realise dynamic lookup
|
115
|
+
# of localities.
|
116
|
+
def update_direct_refs_to_docs(docxml, identifier)
|
117
|
+
erefs, erefs1 = update_direct_refs_to_docs_prep(docxml)
|
118
|
+
docxml.xpath(ns("//bibitem")).each do |b|
|
119
|
+
docid = b.at(ns("./docidentifier[@type = 'repository']")) or next
|
120
|
+
strip_unresolved_repo_erefs(identifier, docid, erefs1, b) or next
|
121
|
+
update_bibitem(b, identifier)
|
122
|
+
docid = docid_to_citeas(b) or next
|
123
|
+
erefs[docid] and update_anchors(b, docid, erefs[docid])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def update_direct_refs_to_docs_prep(docxml)
|
128
|
+
@ncnames = {}
|
129
|
+
[Util::gather_citeases(docxml), Util::gather_bibitemids(docxml)]
|
130
|
+
end
|
131
|
+
|
132
|
+
# strip erefs if they are repository erefs, but do not point to a document
|
133
|
+
# within the current collection. This can happen if a collection consists
|
134
|
+
# of many documents, but not all are included in the current collection.
|
135
|
+
# Do not do this if this is a sectionsplit collection or a nested manifest.
|
136
|
+
# Return false if bibitem is not to be further processed
|
137
|
+
def strip_unresolved_repo_erefs(_document_id, bib_docid, erefs, bibitem)
|
138
|
+
%r{^current-metanorma-collection/(?!Missing:)}.match?(bib_docid.text) and
|
139
|
+
return true
|
140
|
+
@nested and return false
|
141
|
+
erefs[bibitem["id"]]&.each { |x| x.parent and strip_eref(x) }
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
145
|
+
# Resolve erefs to a container of ids in another doc,
|
146
|
+
# to an anchor eref (direct link)
|
147
|
+
def update_indirect_refs_to_docs(docxml, _docidentifier, internal_refs)
|
148
|
+
bibitems, erefs = update_indirect_refs_to_docs_prep(docxml)
|
149
|
+
internal_refs.each do |schema, ids|
|
150
|
+
ids.each do |id, file|
|
151
|
+
k = indirect_ref_key(schema, id, docxml)
|
152
|
+
update_indirect_refs_to_docs1(docxml, k, file, bibitems, erefs)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def update_indirect_refs_to_docs_prep(docxml)
|
158
|
+
@updated_anchors = {}
|
159
|
+
[Util::gather_bibitems(docxml), Util::gather_bibitemids(docxml)]
|
160
|
+
end
|
161
|
+
|
162
|
+
def indirect_ref_key(schema, id, docxml)
|
163
|
+
/^#{schema}_/.match?(id) and return id
|
164
|
+
ret = "#{schema}_#{id}"
|
165
|
+
suffix = docxml.root["document_suffix"]
|
166
|
+
(k = docxml.root["type"]) && k != schema && suffix and
|
167
|
+
ret = "#{ret}_#{suffix}"
|
168
|
+
ret
|
169
|
+
end
|
170
|
+
|
171
|
+
def update_indirect_refs_to_docs1(_docxml, key, file, bibitems, erefs)
|
172
|
+
erefs[key]&.each do |e|
|
173
|
+
e["citeas"] = file
|
174
|
+
update_indirect_refs_to_docs_anchor(e, file)
|
175
|
+
end
|
176
|
+
update_indirect_refs_to_docs_docid(bibitems[key], file)
|
177
|
+
end
|
178
|
+
|
179
|
+
def update_indirect_refs_to_docs_anchor(eref, file)
|
180
|
+
a = eref.at(ns(".//locality[@type = 'anchor']/referenceFrom")) or return
|
181
|
+
suffix = file
|
182
|
+
@files.get(file) && p = @files.get(file, :parentid) and
|
183
|
+
suffix = "#{p}_#{suffix}"
|
184
|
+
existing = a.text
|
185
|
+
anchor = existing
|
186
|
+
@files.url?(file) or
|
187
|
+
anchor = Metanorma::Utils::to_ncname("#{anchor}_#{suffix}")
|
188
|
+
@updated_anchors[existing] or a.children = anchor
|
189
|
+
@updated_anchors[anchor] = true
|
190
|
+
end
|
191
|
+
|
192
|
+
def update_indirect_refs_to_docs_docid(bibitem, file)
|
193
|
+
docid = bibitem&.at(ns("./docidentifier[@type = 'repository']")) or
|
194
|
+
return
|
195
|
+
docid.children = "current-metanorma-collection/#{file}"
|
196
|
+
docid.previous =
|
197
|
+
"<docidentifier type='metanorma-collection'>#{file}</docidentifier>"
|
198
|
+
end
|
199
|
+
|
200
|
+
# update crossrefences to other documents, to include
|
201
|
+
# disambiguating document suffix on id
|
202
|
+
def update_anchors(bib, docid, erefs)
|
203
|
+
erefs.each do |e|
|
204
|
+
if @files.get(docid) then update_anchor_loc(bib, e, docid)
|
205
|
+
else
|
206
|
+
msg = "<strong>** Unresolved reference to document #{docid} " \
|
207
|
+
"from eref</strong>"
|
208
|
+
e << msg
|
209
|
+
strip_eref(e)
|
210
|
+
@log&.add("Cross-References", e, msg)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def update_anchor_loc(bib, eref, docid)
|
216
|
+
loc = eref.at(".//xmlns:locality[@type = 'anchor']") or
|
217
|
+
return update_anchor_create_loc(bib, eref, docid)
|
218
|
+
ref = loc.at("./xmlns:referenceFrom") or return
|
219
|
+
anchor = suffix_anchor(ref, docid)
|
220
|
+
a = @files.get(docid, :anchors) or return
|
221
|
+
a.inject([]) { |m, (_, x)| m + x.values }
|
222
|
+
.include?(anchor) or return
|
223
|
+
ref.content = anchor
|
224
|
+
end
|
225
|
+
|
226
|
+
def suffix_anchor(ref, docid)
|
227
|
+
@ncnames[docid] ||= Metanorma::Utils::to_ncname(docid)
|
228
|
+
anchor = ref.text
|
229
|
+
@files.url?(docid) or anchor = "#{@ncnames[docid]}_#{anchor}"
|
230
|
+
anchor
|
231
|
+
end
|
232
|
+
|
233
|
+
# if there is a crossref to another document, with no anchor, retrieve the
|
234
|
+
# anchor given the locality, and insert it into the crossref
|
235
|
+
def update_anchor_create_loc(_bib, eref, docid)
|
236
|
+
ins = eref.at(ns("./localityStack")) or return
|
237
|
+
type = ins.at(ns("./locality/@type"))&.text
|
238
|
+
type = "clause" if type == "annex"
|
239
|
+
ref = ins.at(ns("./locality/referenceFrom"))&.text
|
240
|
+
anchor = @files.get(docid, :anchors).dig(type, ref) or return
|
241
|
+
ins << "<locality type='anchor'><referenceFrom>#{anchor.sub(/^_/,
|
242
|
+
'')}" \
|
243
|
+
"</referenceFrom></locality>"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "isodoc"
|
4
|
+
require "metanorma-utils"
|
5
|
+
require_relative "fileparse"
|
6
|
+
|
7
|
+
module Metanorma
|
8
|
+
class Collection
|
9
|
+
class Renderer
|
10
|
+
# compile and output individual file in collection
|
11
|
+
# warn "metanorma compile -x html #{f.path}"
|
12
|
+
def file_compile(file, filename, identifier)
|
13
|
+
@files.get(identifier, :sectionsplit) and return
|
14
|
+
opts = {
|
15
|
+
format: :asciidoc,
|
16
|
+
extension_keys: @format,
|
17
|
+
output_dir: @outdir,
|
18
|
+
}.merge(compile_options_update(identifier))
|
19
|
+
@compile.compile file, opts
|
20
|
+
@files.set(identifier, :outputs, {})
|
21
|
+
file_compile_formats(filename, identifier)
|
22
|
+
end
|
23
|
+
|
24
|
+
def compile_options_update(identifier)
|
25
|
+
ret = @compile_options.dup
|
26
|
+
@directives.detect { |d| d.key == "presentation-xml" } ||
|
27
|
+
@files.get(identifier, :presentationxml) and
|
28
|
+
ret.merge!(passthrough_presentation_xml: true)
|
29
|
+
@files.get(identifier, :sectionsplit) == true and
|
30
|
+
ret.merge!(sectionsplit: true)
|
31
|
+
@files.get(identifier, :bare) == true and
|
32
|
+
ret.merge!(bare: true)
|
33
|
+
ret
|
34
|
+
end
|
35
|
+
|
36
|
+
def file_compile_formats(filename, identifier)
|
37
|
+
f = @files.get(identifier, :outputs)
|
38
|
+
@format << :presentation if @format.include?(:pdf)
|
39
|
+
@format.each do |e|
|
40
|
+
ext = @compile.processor.output_formats[e]
|
41
|
+
fn = File.basename(filename).sub(/(?<=\.)[^.]+$/, ext.to_s)
|
42
|
+
(/html$/.match?(ext) && @files.get(identifier, :sectionsplit)) or
|
43
|
+
f[e] = File.join(@outdir, fn)
|
44
|
+
end
|
45
|
+
@files.set(identifier, :outputs, f)
|
46
|
+
end
|
47
|
+
|
48
|
+
def copy_file_to_dest(identifier)
|
49
|
+
out = Pathname.new(@files.get(identifier, :out_path)).cleanpath
|
50
|
+
out.absolute? and
|
51
|
+
out = out.relative_path_from(File.expand_path(FileUtils.pwd))
|
52
|
+
dest = File.join(@outdir, @disambig.source2dest_filename(out.to_s))
|
53
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
54
|
+
FileUtils.cp @files.get(identifier, :ref), dest
|
55
|
+
end
|
56
|
+
|
57
|
+
# process each file in the collection
|
58
|
+
# files are held in memory, and altered as postprocessing
|
59
|
+
def files # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
60
|
+
warn "\n\n\n\n\nRender Files: #{DateTime.now.strftime('%H:%M:%S')}"
|
61
|
+
internal_refs = locate_internal_refs
|
62
|
+
@files.keys.each_with_index do |ident, i|
|
63
|
+
i.positive? && @directives.detect do |d|
|
64
|
+
d.key == "bare-after-first"
|
65
|
+
end and
|
66
|
+
@compile_options.merge!(bare: true)
|
67
|
+
if @files.get(ident, :attachment) then copy_file_to_dest(ident)
|
68
|
+
else
|
69
|
+
file, fname = @files.targetfile_id(ident, read: true)
|
70
|
+
warn "\n\n\n\n\nProcess #{fname}: #{DateTime.now.strftime('%H:%M:%S')}"
|
71
|
+
collection_xml = update_xrefs(file, ident, internal_refs)
|
72
|
+
collection_filename = File.basename(fname, File.extname(fname))
|
73
|
+
collection_xml_path = File.join(Dir.tmpdir,
|
74
|
+
"#{collection_filename}.xml")
|
75
|
+
File.write collection_xml_path, collection_xml, encoding: "UTF-8"
|
76
|
+
file_compile(collection_xml_path, fname, ident)
|
77
|
+
FileUtils.rm(collection_xml_path)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# gather internal bibitem references
|
83
|
+
def gather_internal_refs
|
84
|
+
@files.keys.each_with_object({}) do |i, refs|
|
85
|
+
@files.get(i, :attachment) and next
|
86
|
+
file, = @files.targetfile_id(i, read: true)
|
87
|
+
gather_internal_refs1(file, i, refs)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def gather_internal_refs1(file, ident, refs)
|
92
|
+
f = Nokogiri::XML(file, &:huge)
|
93
|
+
!@files.get(ident, :sectionsplit) and
|
94
|
+
gather_internal_refs_indirect(f, refs)
|
95
|
+
key = @files.get(ident, :indirect_key) and
|
96
|
+
gather_internal_refs_sectionsplit(f, ident, key, refs)
|
97
|
+
end
|
98
|
+
|
99
|
+
def gather_internal_refs_indirect(doc, refs)
|
100
|
+
doc.xpath(ns("//bibitem[@type = 'internal']/" \
|
101
|
+
"docidentifier[@type = 'repository']")).each do |d|
|
102
|
+
a = d.text.split(%r{/}, 2)
|
103
|
+
a.size > 1 or next
|
104
|
+
refs[a[0]] ||= {}
|
105
|
+
refs[a[0]][a[1]] = false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def gather_internal_refs_sectionsplit(_doc, ident, key, refs)
|
110
|
+
refs[key] ||= {}
|
111
|
+
@files.get(ident, :ids).each_key do |k|
|
112
|
+
refs[key][k] = false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def populate_internal_refs(refs)
|
117
|
+
@files.keys.reject do |k|
|
118
|
+
@files.get(k, :attachment) || @files.get(k, :sectionsplit)
|
119
|
+
end.each do |ident|
|
120
|
+
locate_internal_refs1(refs, ident,
|
121
|
+
@isodoc.docid_prefix("", ident.dup))
|
122
|
+
end
|
123
|
+
refs
|
124
|
+
end
|
125
|
+
|
126
|
+
# resolve file location for the target of each internal reference
|
127
|
+
def locate_internal_refs
|
128
|
+
warn "\n\n\n\n\nInternal Refs: #{DateTime.now.strftime('%H:%M:%S')}"
|
129
|
+
refs = populate_internal_refs(gather_internal_refs)
|
130
|
+
refs.each do |schema, ids|
|
131
|
+
ids.each do |id, key|
|
132
|
+
key and next
|
133
|
+
refs[schema][id] = "Missing:#{schema}:#{id}"
|
134
|
+
@log&.add("Cross-References", nil, refs[schema][id])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
refs
|
138
|
+
end
|
139
|
+
|
140
|
+
def locate_internal_refs1(refs, identifier, ident)
|
141
|
+
file, = @files.targetfile_id(ident, read: true)
|
142
|
+
t = locate_internal_refs1_prep(file)
|
143
|
+
refs.each do |schema, ids|
|
144
|
+
ids.keys.select { |id| t[id] }.each do |id|
|
145
|
+
t[id].at("./ancestor-or-self::*[@type = '#{schema}']") and
|
146
|
+
refs[schema][id] = identifier
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def locate_internal_refs1_prep(file)
|
152
|
+
xml = Nokogiri::XML(file, &:huge)
|
153
|
+
r = xml.root["document_suffix"]
|
154
|
+
xml.xpath("//*[@id]").each_with_object({}) do |i, x|
|
155
|
+
/^semantic_/.match?(i.name) and next
|
156
|
+
x[i["id"]] = i
|
157
|
+
r and x[i["id"].sub(/_#{r}$/, "")] = i
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def update_bibitem(bib, identifier)
|
162
|
+
docid = get_bibitem_docid(bib, identifier) or return
|
163
|
+
newbib = dup_bibitem(docid, bib)
|
164
|
+
url = @files.url(docid, relative: true,
|
165
|
+
doc: !@files.get(docid, :attachment))
|
166
|
+
dest = newbib.at("./docidentifier") || newbib.at(ns("./docidentifier"))
|
167
|
+
dest or dest = newbib.elements[-1]
|
168
|
+
dest.previous = "<uri type='citation'>#{url}</uri>"
|
169
|
+
bib.replace(newbib)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Metanorma
|
2
|
+
class Collection
|
3
|
+
class Renderer
|
4
|
+
# @param elm [Nokogiri::XML::Element]
|
5
|
+
# @return [String]
|
6
|
+
def indexfile_title(entry)
|
7
|
+
if entry.bibdata &&
|
8
|
+
x = entry.bibdata.title.detect { |t| t.type == "main" } ||
|
9
|
+
entry.bibdata.title.first
|
10
|
+
x.title.content
|
11
|
+
else
|
12
|
+
entry.title
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# uses the identifier to label documents; other attributes (title) can be
|
17
|
+
# looked up in @files[id][:bibdata]
|
18
|
+
#
|
19
|
+
# @param mnf [Collection::Manifest]
|
20
|
+
# @param builder [Nokogiri::XML::Builder]
|
21
|
+
def indexfile_docref(mnf, builder)
|
22
|
+
Array(mnf.entry).detect(&:index) or return ""
|
23
|
+
builder.ul { |b| docrefs(mnf, b) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def docrefs(mnf, builder)
|
27
|
+
ident = docref_ident(mnf)
|
28
|
+
builder.li do |li|
|
29
|
+
li.a href: index_link(mnf, ident) do |a|
|
30
|
+
a << ident.split(/([<>&])/).map do |x|
|
31
|
+
/[<>&]/.match?(x) ? x : @c.encode(x, :hexadecimal)
|
32
|
+
end.join
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def docref_ident(docref)
|
38
|
+
ident = docref.identifier.dup
|
39
|
+
@c.decode(@isodoc.docid_prefix(nil, ident))
|
40
|
+
end
|
41
|
+
|
42
|
+
def index_link(docref, ident)
|
43
|
+
if docref.file
|
44
|
+
@files.get(ident, :out_path).sub(/\.xml$/, ".html")
|
45
|
+
else "#{docref.id}.html"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# single level navigation list, with hierarchical nesting
|
50
|
+
def indexfile(mnf)
|
51
|
+
mnfs = Array(mnf)
|
52
|
+
mnfs.empty? and return ""
|
53
|
+
mnfs.map { |m| "<ul>#{indexfile1(m)}</ul>" }.join("\n")
|
54
|
+
end
|
55
|
+
|
56
|
+
def index?(mnf)
|
57
|
+
mnf.index and return true
|
58
|
+
mnf.entry.detect { |e| index?(e) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def indexfile1(mnf)
|
62
|
+
index?(mnf) or return ""
|
63
|
+
cleanup_indexfile1(build_indexfile1(mnf))
|
64
|
+
end
|
65
|
+
|
66
|
+
def build_indexfile1(mnf)
|
67
|
+
Nokogiri::HTML::Builder.new do |b|
|
68
|
+
if mnf.file then docrefs(mnf, b)
|
69
|
+
else
|
70
|
+
b.li do |l|
|
71
|
+
l << indexfile_title(mnf)
|
72
|
+
l.ul do |u|
|
73
|
+
Array(mnf.entry).each { |e| u << indexfile1(e) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def cleanup_indexfile1(ret)
|
81
|
+
ret = ret.doc.root
|
82
|
+
ret.xpath("/ul").each do |u|
|
83
|
+
if u.at("./li/ul") && !u.at("./li[text()]")
|
84
|
+
u.replace(u.xpath("./li/ul"))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
ret.to_html
|
88
|
+
end
|
89
|
+
|
90
|
+
# object to construct navigation out of in Liquid
|
91
|
+
def index_object(mnf)
|
92
|
+
mnf = Array(mnf).first
|
93
|
+
ret = { title: indexfile_title(mnf), level: mnf.type,
|
94
|
+
docrefs: index_object_docrefs(mnf),
|
95
|
+
children: index_object_children(mnf) }.compact
|
96
|
+
ret.keys == [:children] and ret = ret[:children]
|
97
|
+
ret
|
98
|
+
end
|
99
|
+
|
100
|
+
def index_object_children(mnf)
|
101
|
+
nonfiles = Array(mnf.entry).reject(&:file)
|
102
|
+
c = nonfiles.each_with_object([]) do |d, b|
|
103
|
+
b << index_object(d)
|
104
|
+
end.flatten
|
105
|
+
c.empty? and c = nil
|
106
|
+
c
|
107
|
+
end
|
108
|
+
|
109
|
+
def index_object_docrefs(mnf)
|
110
|
+
files = Array(mnf.entry).select(&:file)
|
111
|
+
files.empty? and return nil
|
112
|
+
r = Nokogiri::HTML::Builder.new do |b|
|
113
|
+
b.ul do |u|
|
114
|
+
files.each { |f| docrefs(f, u) }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
r.doc.root&.to_html&.gsub("\n", " ")
|
118
|
+
end
|
119
|
+
|
120
|
+
def liquid_docrefs(mnfs)
|
121
|
+
Array(mnfs).select(&:index).each_with_object([]) do |d, m|
|
122
|
+
if d.file
|
123
|
+
ident = @c.decode(@isodoc.docid_prefix(nil, d.identifier.dup))
|
124
|
+
m << { "identifier" => ident, "file" => index_link(d, ident),
|
125
|
+
"title" => indexfile_title(d), "level" => d.type }
|
126
|
+
else
|
127
|
+
liquid_docrefs(d.entry).each { |m1| m << m1 }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Metanorma
|
2
|
+
class Collection
|
3
|
+
class Renderer
|
4
|
+
def docconv
|
5
|
+
@tempfile_cache ||= []
|
6
|
+
doctype = @doctype.to_sym
|
7
|
+
x = Asciidoctor.load nil, backend: doctype
|
8
|
+
x.converter.doc_converter(DocOptionsNode.new(@directives, @dirname))
|
9
|
+
end
|
10
|
+
|
11
|
+
def concat_extract_files(filename)
|
12
|
+
xml = Nokogiri::XML(File.read(filename, encoding: "UTF-8"), &:huge)
|
13
|
+
docs = xml.xpath(ns("//doc-container")).each_with_object([]) do |x, m|
|
14
|
+
n = Nokogiri::XML::Document.new
|
15
|
+
n.add_child(x.elements.first.remove)
|
16
|
+
m << n
|
17
|
+
end
|
18
|
+
[wrapping_doc(docs.first.dup, xml), docs]
|
19
|
+
end
|
20
|
+
|
21
|
+
def wrapping_doc(doc, xml)
|
22
|
+
doc.at(ns("//bibdata")).replace(xml.at(ns("//bibdata")).to_xml)
|
23
|
+
sections = wrapping_doc_body(doc)
|
24
|
+
wrapping_doc_intro_outro(xml, sections)
|
25
|
+
set_displayorder_wrapping_doc(doc)
|
26
|
+
end
|
27
|
+
|
28
|
+
def wrapping_doc_intro_outro(xml, sections)
|
29
|
+
p = xml.at(ns("//prefatory-content")) and
|
30
|
+
sections.previous = "<preface>#{p.children.to_xml}</preface>"
|
31
|
+
p = xml.at(ns("//final-content")) and
|
32
|
+
sections.next = "<annex>#{p.children.to_xml}</annex>"
|
33
|
+
end
|
34
|
+
|
35
|
+
def wrapping_doc_body(doc)
|
36
|
+
doc.xpath(ns("//annex | //preface | //bibliography")).each(&:remove)
|
37
|
+
s = doc.at(ns("//sections"))
|
38
|
+
repl = <<~BODY
|
39
|
+
<sections><clause id='_collection_placeholder'><p>PLACEHOLDER</p></clause></sections>
|
40
|
+
BODY
|
41
|
+
s.replace(repl)
|
42
|
+
doc.at(ns("//sections"))
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_displayorder_wrapping_doc(doc)
|
46
|
+
doc.xpath(ns("//preface/* | //sections/* | //annex"))
|
47
|
+
.each_with_index do |x, i|
|
48
|
+
x["displayorder"] = i + 1
|
49
|
+
end
|
50
|
+
doc
|
51
|
+
end
|
52
|
+
|
53
|
+
SECTION_BREAK = '<p class="MsoNormal"><br clear="all" class="section"/></p>'
|
54
|
+
.freeze
|
55
|
+
DIV1 = '<div class="WordSection1"> </div>'.freeze
|
56
|
+
DIV2 = '<div class="WordSection2"> </div>'.freeze
|
57
|
+
|
58
|
+
def docconv_convert1(docs)
|
59
|
+
docs.each_with_index.with_object([]) do |(d, i), m|
|
60
|
+
conv = docconv
|
61
|
+
conv.convert_init(d.to_xml(encoding: "UTF-8"), "xxxx", false)
|
62
|
+
html = conv.postprocess_cleanup(conv.convert1(d, "xxx", "."))
|
63
|
+
@tempfile_cache += conv.tempfile_cache # hold on to the temp img files
|
64
|
+
b = Nokogiri::XML(html).at("//body")
|
65
|
+
i == docs.size - 1 or
|
66
|
+
b << '<p class="MsoNormal"><br clear="all" class="section"/></p>'
|
67
|
+
m << b.children
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def collection_coverpages(conv, docs)
|
72
|
+
conv.wordintropage and [DIV2, SECTION_BREAK].reverse.each do |s|
|
73
|
+
docs.unshift(Nokogiri::XML(s).root)
|
74
|
+
end
|
75
|
+
conv.wordcoverpage and [DIV1, SECTION_BREAK].reverse.each do |s|
|
76
|
+
docs.unshift(Nokogiri::XML(s).root)
|
77
|
+
end
|
78
|
+
docs
|
79
|
+
end
|
80
|
+
|
81
|
+
def docconv_convert(filename)
|
82
|
+
pref_file, docs = concat_extract_files(filename)
|
83
|
+
body = docconv_convert1(docs)
|
84
|
+
collection_conv = overall_docconv_converter(body)
|
85
|
+
collection_coverpages(collection_conv, body)
|
86
|
+
collection_conv.convert(filename, pref_file.to_xml, false)
|
87
|
+
end
|
88
|
+
|
89
|
+
def overall_docconv_cover(collection_conv)
|
90
|
+
p = Util::hash_key_detect(@directives, "collection-word-coverpage", nil)
|
91
|
+
collection_conv.wordcoverpage =
|
92
|
+
Util::rel_path_resolve(@dirname, p)
|
93
|
+
p = Util::hash_key_detect(@directives, "collection-word-intropage", nil)
|
94
|
+
collection_conv.wordintropage =
|
95
|
+
Util::rel_path_resolve(@dirname, p)
|
96
|
+
end
|
97
|
+
|
98
|
+
def overall_docconv_converter(body)
|
99
|
+
collection_conv = docconv
|
100
|
+
collection_conv.options[:collection_doc] = body.map(&:to_xml).join
|
101
|
+
overall_docconv_cover(collection_conv)
|
102
|
+
|
103
|
+
def collection_conv.postprocess_cleanup(result)
|
104
|
+
ret = to_xhtml(super)
|
105
|
+
b = ret.at("//div[@id = '_collection_placeholder']")
|
106
|
+
b.replace(@options[:collection_doc])
|
107
|
+
from_xhtml(ret)
|
108
|
+
end
|
109
|
+
|
110
|
+
collection_conv
|
111
|
+
end
|
112
|
+
|
113
|
+
class DocOptionsNode
|
114
|
+
def initialize(directives, dir)
|
115
|
+
@dir = dir
|
116
|
+
@wordcoverpage =
|
117
|
+
Util::hash_key_detect(directives, "document-word-coverpage",
|
118
|
+
@wordcoverpage)
|
119
|
+
@wordintropage =
|
120
|
+
Util::hash_key_detect(directives, "document-word-intropage",
|
121
|
+
@wordintropage)
|
122
|
+
end
|
123
|
+
|
124
|
+
def attr(key)
|
125
|
+
case key
|
126
|
+
when "wordcoverpage" then Util::rel_path_resolve(@dir, @wordcoverpage)
|
127
|
+
when "wordintropage" then Util::rel_path_resolve(@dir, @wordintropage)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|