metanorma 1.7.6 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +51 -26
- data/Gemfile.devel +0 -2
- data/lib/metanorma/collection.rb +0 -243
- data/lib/metanorma/collection_fileparse.rb +0 -254
- data/lib/metanorma/collection_fileprocess.rb +0 -157
- data/lib/metanorma/collection_manifest.rb +0 -139
- 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 -230
- data/lib/metanorma/document.rb +0 -133
- data/lib/metanorma/files_lookup.rb +0 -208
- 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/sectionsplit_links.rb +0 -189
- 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
|