metanorma 2.1.4 → 2.5.4
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/.rubocop.yml +23 -2
- data/Gemfile +14 -0
- data/README.adoc +1 -1
- data/lib/metanorma/collection/artifact_store.rb +142 -0
- data/lib/metanorma/collection/collection.rb +140 -131
- data/lib/metanorma/collection/config/bibdata.rb +1 -1
- data/lib/metanorma/collection/config/config.rb +50 -20
- data/lib/metanorma/collection/config/converters.rb +89 -31
- data/lib/metanorma/collection/config/doc_container.rb +28 -0
- data/lib/metanorma/collection/config/manifest.rb +30 -6
- data/lib/metanorma/collection/config/namespaces.rb +12 -0
- data/lib/metanorma/collection/document/document.rb +54 -15
- data/lib/metanorma/collection/filelookup/filelookup.rb +170 -74
- data/lib/metanorma/collection/filelookup/filelookup_sectionsplit.rb +62 -27
- data/lib/metanorma/collection/filelookup/utils.rb +52 -0
- data/lib/metanorma/collection/helpers.rb +82 -0
- data/lib/metanorma/collection/log.rb +24 -0
- data/lib/metanorma/collection/manifest/manifest.rb +27 -14
- data/lib/metanorma/collection/multilingual/multilingual.rb +1 -1
- data/lib/metanorma/collection/renderer/filelocation.rb +166 -0
- data/lib/metanorma/collection/renderer/fileparse.rb +60 -55
- data/lib/metanorma/collection/renderer/fileprocess.rb +184 -40
- data/lib/metanorma/collection/renderer/navigation.rb +20 -6
- data/lib/metanorma/collection/renderer/render_word.rb +8 -4
- data/lib/metanorma/collection/renderer/renderer.rb +202 -19
- data/lib/metanorma/collection/renderer/svg.rb +60 -5
- data/lib/metanorma/collection/renderer/utils.rb +76 -42
- data/lib/metanorma/collection/sectionsplit/collection.rb +14 -4
- data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +141 -61
- data/lib/metanorma/collection/util/disambig_files.rb +4 -5
- data/lib/metanorma/collection/util/util.rb +147 -23
- data/lib/metanorma/collection/xrefprocess/xrefprocess.rb +7 -7
- data/lib/metanorma/compile/assets/icc-boilerplate.adoc +41 -0
- data/lib/metanorma/compile/compile.rb +180 -165
- data/lib/metanorma/compile/compile_options.rb +155 -83
- data/lib/metanorma/compile/extract.rb +76 -56
- data/lib/metanorma/compile/flavor.rb +19 -0
- data/lib/metanorma/compile/output_filename.rb +75 -0
- data/lib/metanorma/compile/output_filename_config.rb +27 -0
- data/lib/metanorma/compile/relaton_drop.rb +56 -0
- data/lib/metanorma/compile/render.rb +196 -0
- data/lib/metanorma/compile/validator.rb +26 -0
- data/lib/metanorma/compile/writeable.rb +12 -0
- data/lib/metanorma/util/fontist_helper.rb +35 -19
- data/lib/metanorma/version.rb +1 -1
- data/lib/metanorma.rb +1 -7
- data/metanorma.gemspec +11 -20
- data/plans/collection-rendering-architecture-review.md +516 -0
- metadata +42 -122
- data/.hound.yml +0 -5
- data/lib/metanorma/asciidoctor_extensions/glob_include_processor.rb +0 -22
- data/lib/metanorma/asciidoctor_extensions.rb +0 -1
- data/lib/metanorma/compile/compile_validate.rb +0 -68
- data/lib/metanorma/config/config.rb +0 -23
- data/lib/metanorma/input/asciidoc.rb +0 -88
- data/lib/metanorma/input/base.rb +0 -9
- data/lib/metanorma/input.rb +0 -7
- data/lib/metanorma/processor/processor.rb +0 -54
- data/lib/metanorma/processor.rb +0 -6
- data/lib/metanorma/registry/registry.rb +0 -63
- data/lib/metanorma/util/util.rb +0 -45
|
@@ -44,18 +44,28 @@ module Metanorma
|
|
|
44
44
|
bibdata: {
|
|
45
45
|
title: {
|
|
46
46
|
type: "title-main", language: @lang,
|
|
47
|
-
content: xml.at(ns("//bibdata/title"))
|
|
47
|
+
content: xml.at(ns("//bibdata/title"))&.text || "[TITLE]"
|
|
48
48
|
},
|
|
49
49
|
type: "collection",
|
|
50
50
|
docid: {
|
|
51
|
-
type: xml.at(ns("//bibdata/docidentifier/@type"))
|
|
52
|
-
id: xml.at(ns("//bibdata/docidentifier"))
|
|
51
|
+
type: xml.at(ns("//bibdata/docidentifier/@type"))&.text || "[FLAVOR]",
|
|
52
|
+
id: xml.at(ns("//bibdata/docidentifier"))&.text || "[DOCID]",
|
|
53
53
|
},
|
|
54
54
|
},
|
|
55
55
|
manifest: {
|
|
56
56
|
level: "collection", title: "Collection",
|
|
57
57
|
docref: files.sort_by { |f| f[:order] }.each.map do |f|
|
|
58
|
-
|
|
58
|
+
# XML files are always stored flat, so fileref is always the basename
|
|
59
|
+
# f[:url] may include directory for HTML output, but XML is basename only
|
|
60
|
+
fileref = File.basename(f[:url])
|
|
61
|
+
entry = { fileref: fileref, identifier: f[:title] }
|
|
62
|
+
# Include sectionsplit_filename and sectionsplit-output when there's a directory structure
|
|
63
|
+
# This tells the renderer to preserve directory structure in HTML output
|
|
64
|
+
if @sectionsplit_filename && File.dirname(@sectionsplit_filename) != "."
|
|
65
|
+
entry[:"sectionsplit-filename"] = @sectionsplit_filename
|
|
66
|
+
entry[:"sectionsplit-output"] = true
|
|
67
|
+
end
|
|
68
|
+
entry
|
|
59
69
|
end
|
|
60
70
|
},
|
|
61
71
|
}
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
require "yaml"
|
|
2
|
-
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require "metanorma-core"
|
|
3
4
|
require_relative "../xrefprocess/xrefprocess"
|
|
4
5
|
require_relative "collection"
|
|
5
6
|
require "concurrent-ruby"
|
|
6
7
|
|
|
7
8
|
module Metanorma
|
|
8
9
|
class Collection
|
|
10
|
+
# Sectionsplit is a collection render nested inside a collection render:
|
|
11
|
+
# a document is split into one output file per section, and those files are
|
|
12
|
+
# then rendered as their own (sub-)collection.
|
|
13
|
+
#
|
|
14
|
+
# Two output-location invariants matter (both load-bearing -- see the
|
|
15
|
+
# collection architecture review plan and metanorma/iso-10303#208):
|
|
16
|
+
#
|
|
17
|
+
# * XML section files are always written FLAT to the split directory
|
|
18
|
+
# (basename only). HTML output may instead carry a directory taken from
|
|
19
|
+
# sectionsplit_filename, reattached only at HTML compile time
|
|
20
|
+
# (preserve_directory_structure?).
|
|
21
|
+
# * A sectionsplit document's content is emitted at the sectionsplit output
|
|
22
|
+
# location (the collection root, unless sectionsplit_filename sets a
|
|
23
|
+
# directory), NOT at the document's own :out_path. Anything relativising a
|
|
24
|
+
# URL for such a document (e.g. attachment/citation links) must base it on
|
|
25
|
+
# the split location, or the ../../ overshoots.
|
|
9
26
|
class Sectionsplit
|
|
10
|
-
attr_accessor :filecache, :key
|
|
27
|
+
attr_accessor :filecache, :key, :whole_presentation_file
|
|
11
28
|
|
|
12
29
|
def initialize(opts)
|
|
13
30
|
@input_filename = opts[:input]
|
|
@@ -19,7 +36,11 @@ module Metanorma
|
|
|
19
36
|
@fileslookup = opts[:fileslookup]
|
|
20
37
|
@ident = opts[:ident]
|
|
21
38
|
@isodoc = opts[:isodoc]
|
|
39
|
+
@isodoc_presxml = opts[:isodoc_presxml]
|
|
22
40
|
@document_suffix = opts[:document_suffix]
|
|
41
|
+
@sectionsplit_filename = opts[:sectionsplit_filename] ||
|
|
42
|
+
"{basename_legacy}.{sectionsplit-num}"
|
|
43
|
+
@parent_idx = opts[:parent_idx] || 0
|
|
23
44
|
end
|
|
24
45
|
|
|
25
46
|
def ns(xpath)
|
|
@@ -32,21 +53,30 @@ module Metanorma
|
|
|
32
53
|
["//bibliography/*[not(@hidden = 'true')]", "bibliography"],
|
|
33
54
|
["//indexsect", nil], ["//colophon", nil]].freeze
|
|
34
55
|
|
|
35
|
-
# Input XML is Semantic
|
|
56
|
+
# Input XML is Semantic XML
|
|
36
57
|
def sectionsplit
|
|
37
58
|
xml = sectionsplit_prep(File.read(@input_filename), @base, @dir)
|
|
59
|
+
# Retain the whole (un-split) Presentation XML. sectionsplit is an
|
|
60
|
+
# HTML-only mechanism, but the collection PDF/presentation path must
|
|
61
|
+
# inline each document intact; without this the sectionsplit parent
|
|
62
|
+
# reaches concatenation with no presentation output and degrades to a
|
|
63
|
+
# bibdata-only, namespace-less <metanorma> stub that mn2pdf drops.
|
|
64
|
+
# https://github.com/metanorma/iso-10303/issues/208
|
|
65
|
+
@whole_presentation_file = write_whole_presentation(xml)
|
|
38
66
|
@key = Metanorma::Collection::XrefProcess::xref_preprocess(xml, @isodoc)
|
|
39
67
|
empty = empty_doc(xml)
|
|
40
68
|
empty1 = empty_attachments(empty)
|
|
41
69
|
@mutex = Mutex.new
|
|
42
|
-
|
|
70
|
+
# @pool = Concurrent::FixedThreadPool.new(4)
|
|
43
71
|
@pool = Concurrent::FixedThreadPool.new(1)
|
|
44
72
|
sectionsplit1(xml, empty, empty1, 0)
|
|
45
73
|
end
|
|
46
74
|
|
|
75
|
+
# xml is Presentation XML
|
|
47
76
|
def sectionsplit1(xml, empty, empty1, idx)
|
|
48
77
|
ret = SPLITSECTIONS.each_with_object([]) do |n, m|
|
|
49
78
|
conflate_floatingtitles(xml.xpath(ns(n[0]))).each do |s|
|
|
79
|
+
# require "debug"; binding.b
|
|
50
80
|
sectionsplit2(xml, idx.zero? ? empty : empty1, s, n[1],
|
|
51
81
|
{ acc: m, idx: idx })
|
|
52
82
|
idx += 1
|
|
@@ -59,16 +89,23 @@ module Metanorma
|
|
|
59
89
|
|
|
60
90
|
def sectionsplit2(xml, empty, chunks, parentnode, opt)
|
|
61
91
|
@pool.post do
|
|
62
|
-
|
|
92
|
+
output_filename = Util.substitute_filename_pattern(
|
|
93
|
+
@sectionsplit_filename,
|
|
94
|
+
document_num: @parent_idx, basename: File.basename(@base, ".*"),
|
|
95
|
+
basename_legacy: @base, sectionsplit_num: opt[:idx]
|
|
96
|
+
)
|
|
97
|
+
warn "Sectionsplit: #{output_filename}"
|
|
98
|
+
a = sectionfile(xml, empty, output_filename, chunks,
|
|
63
99
|
parentnode)
|
|
64
100
|
@mutex.synchronize { opt[:acc] << a }
|
|
65
101
|
end
|
|
66
102
|
end
|
|
67
103
|
|
|
104
|
+
# TODO move to metanorma-utils
|
|
68
105
|
def block?(node)
|
|
69
106
|
%w(p table formula admonition ol ul dl figure quote sourcecode example
|
|
70
107
|
pre note pagebreak hr bookmark requirement recommendation permission
|
|
71
|
-
svgmap inputform toc passthrough
|
|
108
|
+
svgmap inputform toc passthrough annotation imagemap).include?(node.name)
|
|
72
109
|
end
|
|
73
110
|
|
|
74
111
|
def conflate_floatingtitles(nodes)
|
|
@@ -86,7 +123,6 @@ module Metanorma
|
|
|
86
123
|
xml, type = sectionsplit_preprocess_semxml(file, filename)
|
|
87
124
|
flags = { format: :asciidoc, extension_keys: [:presentation],
|
|
88
125
|
type: type }.merge(@compile_opts)
|
|
89
|
-
#require "debug"; binding.b
|
|
90
126
|
Compile.new.compile(xml, flags)
|
|
91
127
|
f = File.open(xml.sub(/\.xml$/, ".presentation.xml"), encoding: "utf-8")
|
|
92
128
|
r = Nokogiri::XML(f, &:huge)
|
|
@@ -95,6 +131,16 @@ module Metanorma
|
|
|
95
131
|
r
|
|
96
132
|
end
|
|
97
133
|
|
|
134
|
+
# Persist the whole, svgmap-restored Presentation XML (the in-memory doc,
|
|
135
|
+
# not the on-disk tmp which still carries the internal svgmap1 marker) so
|
|
136
|
+
# the collection PDF path can inline the document whole. Written flat to
|
|
137
|
+
# the split output directory, where the per-section files also live.
|
|
138
|
+
def write_whole_presentation(xml)
|
|
139
|
+
fname = File.join(@splitdir, "#{@base}.whole.presentation.xml")
|
|
140
|
+
File.write(fname, xml.to_xml, encoding: "UTF-8")
|
|
141
|
+
fname
|
|
142
|
+
end
|
|
143
|
+
|
|
98
144
|
def sectionsplit_preprocess_semxml(file, filename)
|
|
99
145
|
xml = Nokogiri::XML(file, &:huge)
|
|
100
146
|
type = xml.root["flavor"]
|
|
@@ -107,11 +153,8 @@ module Metanorma
|
|
|
107
153
|
|
|
108
154
|
def sectionsplit_update_xrefs(xml)
|
|
109
155
|
if c = @fileslookup&.parent
|
|
110
|
-
|
|
111
|
-
c.
|
|
112
|
-
#require "debug"; binding.b
|
|
113
|
-
c.update_xrefs(xml, @ident, {})
|
|
114
|
-
c.nested = n
|
|
156
|
+
# nested mode so unresolved erefs are not deleted
|
|
157
|
+
c.with_nested { c.update_xrefs(xml, @ident, {}) }
|
|
115
158
|
xml.xpath("//xmlns:svgmap").each { |x| x.name = "svgmap1" }
|
|
116
159
|
# do not process svgmap until after files are split
|
|
117
160
|
end
|
|
@@ -125,35 +168,19 @@ module Metanorma
|
|
|
125
168
|
outname
|
|
126
169
|
end
|
|
127
170
|
|
|
128
|
-
def emptydoc(xml, ordinal)
|
|
129
|
-
out = xml.dup
|
|
130
|
-
out.xpath(
|
|
131
|
-
ns("//preface | //sections | //annex | //bibliography/clause | " \
|
|
132
|
-
"//bibliography/references[not(@hidden = 'true')] | " \
|
|
133
|
-
"//indexsect | //colophon"),
|
|
134
|
-
).each(&:remove)
|
|
135
|
-
ordinal.zero? or out.xpath(ns("//metanorma-ext//attachment | " \
|
|
136
|
-
"//semantic__metanorma-ext//semantic__attachment"))
|
|
137
|
-
.each(&:remove) # keep only one copy of attachments
|
|
138
|
-
out
|
|
139
|
-
end
|
|
140
|
-
|
|
141
171
|
def empty_doc(xml)
|
|
142
172
|
out = xml.dup
|
|
143
173
|
out.xpath(
|
|
144
|
-
ns("//preface | //sections | //annex |
|
|
145
|
-
|
|
146
|
-
|
|
174
|
+
ns("//preface | //sections | //annex | " \
|
|
175
|
+
"//references/bibitem[not(@hidden = 'true')] | " \
|
|
176
|
+
"//indexsect | //colophon"),
|
|
147
177
|
).each(&:remove)
|
|
178
|
+
::Metanorma::Collection::Util::hide_refs(out)
|
|
148
179
|
out
|
|
149
180
|
end
|
|
150
181
|
|
|
151
182
|
def empty_attachments(xml)
|
|
152
|
-
|
|
153
|
-
out.xpath(ns("//metanorma-ext//attachment | " \
|
|
154
|
-
"//semantic__metanorma-ext//semantic__attachment"))
|
|
155
|
-
.each(&:remove) # keep only one copy of attachments
|
|
156
|
-
out
|
|
183
|
+
xml.dup
|
|
157
184
|
end
|
|
158
185
|
|
|
159
186
|
def sectionfile(fulldoc, xml, file, chunks, parentnode)
|
|
@@ -164,48 +191,101 @@ module Metanorma
|
|
|
164
191
|
|
|
165
192
|
def create_sectionfile(xml, out, file, chunks, parentnode)
|
|
166
193
|
ins = out.at(ns("//metanorma-extension")) || out.at(ns("//bibdata"))
|
|
167
|
-
#require "debug"; binding.b
|
|
168
194
|
sectionfile_insert(ins, chunks, parentnode)
|
|
195
|
+
sectionfile_fn_filter(sectionfile_annotation_filter(out))
|
|
169
196
|
Metanorma::Collection::XrefProcess::xref_process(out, xml, @key,
|
|
170
197
|
@ident, @isodoc, true)
|
|
171
|
-
#
|
|
172
|
-
|
|
173
|
-
File.
|
|
174
|
-
|
|
198
|
+
# XML files are always written flat to the splitdir (_files directory)
|
|
199
|
+
# Directory structure from sectionsplit_filename is only used for HTML output
|
|
200
|
+
xml_filename = "#{File.basename(file)}.xml"
|
|
201
|
+
full_path = File.join(@splitdir, xml_filename)
|
|
202
|
+
File.open(full_path, "w:UTF-8") { |f| f.write(out) }
|
|
203
|
+
# Return full file path (with directory) for use in manifest
|
|
204
|
+
# This preserves directory info for HTML rendering without affecting XML storage
|
|
205
|
+
"#{file}.xml"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def sectionfile_insert(ins, chunks, parentnode)
|
|
209
|
+
if parentnode
|
|
210
|
+
ins.next = "<#{parentnode}/>"
|
|
211
|
+
chunks.each { |c| ins.next.add_child(c.dup) }
|
|
212
|
+
else chunks.each { |c| ins.next = c.dup }
|
|
175
213
|
end
|
|
176
|
-
outname
|
|
177
214
|
end
|
|
178
215
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
216
|
+
def sectionfile_fn_filter(xml)
|
|
217
|
+
ids = sectionfile_fn_filter_prep(xml)
|
|
218
|
+
xml.root.xpath(ns("./fmt-footnote-container/fmt-fn-body")).each do |f|
|
|
219
|
+
ids.has_key?(f["id"]) or f.remove
|
|
220
|
+
end
|
|
221
|
+
seen = {}
|
|
222
|
+
xml.root.xpath(ns("/fmt-footnote-container/fmt-fn-body"))
|
|
223
|
+
.each_with_index do |fnbody, i|
|
|
224
|
+
sectionfile_fn_filter_renumber(fnbody, i, ids, seen)
|
|
186
225
|
end
|
|
226
|
+
xml
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# map fmt-fn-body/@id = fn/@target to fn
|
|
230
|
+
def sectionfile_fn_filter_prep(xml)
|
|
231
|
+
xml.xpath(ns("//fn")).each_with_object({}) do |f, m|
|
|
232
|
+
m[f["target"]] ||= []
|
|
233
|
+
m[f["target"]] << f
|
|
187
234
|
end
|
|
188
235
|
end
|
|
189
236
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
237
|
+
FN_CAPTIONS = ".//fmt-fn-label/span[@class = 'fmt-caption-label']".freeze
|
|
238
|
+
|
|
239
|
+
def sectionfile_fn_filter_renumber(fnbody, idx, ids, seen)
|
|
240
|
+
sectionfile_fn_filter_fn_renumber(fnbody, idx, ids, seen)
|
|
241
|
+
sectionfile_fn_filter_fnbody_renumber(fnbody, idx, ids)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def sectionfile_fn_filter_fn_renumber(fnbody, idx, ids, seen)
|
|
245
|
+
ids[fnbody["id"]].each do |f|
|
|
246
|
+
@isodoc_presxml.renumber_document_footnote(f, idx, seen)
|
|
247
|
+
fnlabel = f.at(ns(FN_CAPTIONS)) and
|
|
248
|
+
fnlabel.children = @isodoc_presxml.fn_ref_label(f)
|
|
194
249
|
end
|
|
195
250
|
end
|
|
196
251
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
nodes.each_value(&:remove)
|
|
252
|
+
def sectionfile_fn_filter_fnbody_renumber(fnbody, _idx, ids)
|
|
253
|
+
fnlabel = fnbody.at(ns(FN_CAPTIONS)) or return
|
|
254
|
+
fnbody["reference"] = ids[fnbody["id"]].first["reference"]
|
|
255
|
+
fnlabel.children = @isodoc_presxml.fn_body_label(fnbody)
|
|
202
256
|
end
|
|
203
257
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
258
|
+
# map fmt-annotation-body/@id = fmt-annotation-{start/end}/@target
|
|
259
|
+
# to fmt-annotation-{start/end}
|
|
260
|
+
def sectionfile_annotation_filter_prep(xml)
|
|
261
|
+
xml.xpath(ns("//fmt-annotation-start | //fmt-annotation-end"))
|
|
262
|
+
.each_with_object({}) do |f, m|
|
|
263
|
+
m[f["target"]] ||= []
|
|
264
|
+
m[f["target"]] << f
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def sectionfile_annotation_filter(xml)
|
|
269
|
+
ids = sectionfile_annotation_filter_prep(xml)
|
|
270
|
+
xml.root.xpath(ns("./annotation-container/fmt-annotation-body"))
|
|
271
|
+
.each do |f|
|
|
272
|
+
ids.has_key?(f["id"]) or f.remove
|
|
273
|
+
end
|
|
274
|
+
xml.root.xpath(ns("./annotation-container/fmt-annotation-body"))
|
|
275
|
+
.each_with_index do |fnbody, i|
|
|
276
|
+
sectionfile_annotation_filter_renumber(fnbody, i, ids)
|
|
277
|
+
end
|
|
278
|
+
xml
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def sectionfile_annotation_filter_renumber(fnbody, _idx, ids)
|
|
282
|
+
ids[fnbody["id"]].each do |f|
|
|
283
|
+
case f.name
|
|
284
|
+
when "fmt-annotation-start"
|
|
285
|
+
f.children = @isodoc_presxml.comment_bookmark_start_label(f)
|
|
286
|
+
when "fmt-annotation-end"
|
|
287
|
+
f.children = @isodoc_presxml.comment_bookmark_end_label(f)
|
|
288
|
+
end
|
|
209
289
|
end
|
|
210
290
|
end
|
|
211
291
|
|
|
@@ -214,7 +294,7 @@ module Metanorma
|
|
|
214
294
|
t = title.dup
|
|
215
295
|
t.xpath(ns(".//tab | .//br")).each { |x| x.replace(" ") }
|
|
216
296
|
t.xpath(ns(".//bookmark")).each(&:remove)
|
|
217
|
-
t.xpath(
|
|
297
|
+
t.xpath(".//text()").map(&:text).join
|
|
218
298
|
end
|
|
219
299
|
end
|
|
220
300
|
end
|
|
@@ -10,13 +10,12 @@ module Metanorma
|
|
|
10
10
|
name.sub(%r{^(\./)?(\.\./)+}, "")
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def source2dest_filename(name, disambig
|
|
13
|
+
def source2dest_filename(name, disambig: true, preserve_dirs: false)
|
|
14
14
|
n = strip_root(name)
|
|
15
|
-
dir = File.dirname(n)
|
|
16
|
-
base = File.basename(n)
|
|
17
|
-
|
|
15
|
+
dir = preserve_dirs ? "." : File.dirname(n)
|
|
16
|
+
base = preserve_dirs ? n : File.basename(n)
|
|
17
|
+
disambig && @seen_filenames.include?(base) and
|
|
18
18
|
base = disambiguate_filename(base)
|
|
19
|
-
end
|
|
20
19
|
@seen_filenames << base
|
|
21
20
|
dir == "." ? base : File.join(dir, base)
|
|
22
21
|
end
|
|
@@ -1,22 +1,34 @@
|
|
|
1
|
+
require "marcel"
|
|
2
|
+
|
|
1
3
|
module Metanorma
|
|
2
4
|
class Collection
|
|
3
5
|
module Util
|
|
4
6
|
class << self
|
|
7
|
+
def mime_file_recognised?(filename)
|
|
8
|
+
mime_type = Marcel::MimeType.for name: filename
|
|
9
|
+
mime_type != "application/octet-stream"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def anchor_id_attributes
|
|
13
|
+
Metanorma::Utils::anchor_attributes(presxml: true) +
|
|
14
|
+
[%w(* id), %w(* anchor), %w(link bibitemid), %w(fmt-link bibitemid)]
|
|
15
|
+
end
|
|
16
|
+
|
|
5
17
|
def gather_bibitems(xml)
|
|
6
18
|
xml.xpath("//xmlns:bibitem[@id]").each_with_object({}) do |b, m|
|
|
7
|
-
|
|
19
|
+
id = b["anchor"] || b["id"]
|
|
20
|
+
if m[id]
|
|
8
21
|
b.remove
|
|
9
22
|
next
|
|
10
23
|
# we can't update duplicate bibitem, processing updates wrong one
|
|
11
24
|
else
|
|
12
|
-
m[
|
|
25
|
+
m[id] = b
|
|
13
26
|
end
|
|
14
27
|
end
|
|
15
28
|
end
|
|
16
29
|
|
|
17
30
|
def gather_bibitemids(xml, presxml)
|
|
18
31
|
xml.xpath("//*[@bibitemid]").each_with_object({}) do |e, m|
|
|
19
|
-
#/^semantic__/.match?(e.name) and next
|
|
20
32
|
presxml && %w(xref eref link).include?(e.name) and next
|
|
21
33
|
m[e["bibitemid"]] ||= []
|
|
22
34
|
m[e["bibitemid"]] << e
|
|
@@ -25,7 +37,6 @@ module Metanorma
|
|
|
25
37
|
|
|
26
38
|
def gather_citeases(xml, presxml)
|
|
27
39
|
xml.xpath("//*[@citeas]").each_with_object({}) do |e, m|
|
|
28
|
-
#/^semantic__/.match?(e.name) and next
|
|
29
40
|
presxml && %w(xref eref link).include?(e.name) and next
|
|
30
41
|
k = key(e["citeas"])
|
|
31
42
|
m[k] ||= []
|
|
@@ -33,12 +44,14 @@ module Metanorma
|
|
|
33
44
|
end
|
|
34
45
|
end
|
|
35
46
|
|
|
36
|
-
|
|
47
|
+
# Skip SVGs, they are processed by Vectory::SvgDocument#suffix_ids
|
|
48
|
+
def add_suffix_to_attrs(doc, suffix, tag, attr, isodoc)
|
|
37
49
|
(suffix.nil? || suffix.empty?) and return
|
|
38
|
-
doc.xpath(isodoc.ns("//#{
|
|
39
|
-
|
|
50
|
+
doc.xpath(isodoc.ns("//#{tag}[@#{attr}][not(ancestor-or-self::*[local-name()='svg'])]")).each do |elem|
|
|
51
|
+
# doc.xpath(isodoc.ns("//#{tag}[@#{attr}]")).each do |elem|
|
|
52
|
+
a = elem.attributes[attr].value
|
|
40
53
|
/_#{suffix}$/.match?(a) or
|
|
41
|
-
elem.attributes[
|
|
54
|
+
elem.attributes[attr].value = "#{a}_#{suffix}"
|
|
42
55
|
end
|
|
43
56
|
end
|
|
44
57
|
|
|
@@ -55,32 +68,143 @@ module Metanorma
|
|
|
55
68
|
p.absolute? ? path : File.join(dir, path)
|
|
56
69
|
end
|
|
57
70
|
|
|
71
|
+
def hide_refs(docxml)
|
|
72
|
+
p = "//xmlns:references[xmlns:bibitem]"\
|
|
73
|
+
"[not(./xmlns:bibitem[not(@hidden) or @hidden = 'false'])]"
|
|
74
|
+
docxml.xpath(p).each do |f|
|
|
75
|
+
f["hidden"] = "true"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
58
79
|
def key(ident)
|
|
59
80
|
@c ||= HTMLEntities.new
|
|
60
81
|
@c.decode(ident).gsub(/(\p{Zs})+/, " ")
|
|
61
82
|
end
|
|
62
83
|
|
|
63
|
-
|
|
64
|
-
|
|
84
|
+
# Substitute special strings in filename patterns. Single source for
|
|
85
|
+
# both FileLookup#substitute_filename_pattern and sectionsplit.
|
|
86
|
+
# @param pattern [String] filename pattern with placeholders
|
|
87
|
+
# @param options [Hash] substitution values:
|
|
88
|
+
# :document_num, :basename, :basename_legacy, :sectionsplit_num
|
|
89
|
+
def substitute_filename_pattern(pattern, options = {})
|
|
90
|
+
pattern or return pattern
|
|
91
|
+
subs = { "{document-num}" => options[:document_num],
|
|
92
|
+
"{basename}" => options[:basename],
|
|
93
|
+
"{basename_legacy}" => options[:basename_legacy],
|
|
94
|
+
"{sectionsplit-num}" => options[:sectionsplit_num] }
|
|
95
|
+
subs.each_with_object(pattern.dup) do |(token, val), result|
|
|
96
|
+
val and result.gsub!(token, val.to_s)
|
|
97
|
+
end
|
|
65
98
|
end
|
|
66
99
|
|
|
67
|
-
def
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
100
|
+
def taste2flavor(taste)
|
|
101
|
+
tastes = Metanorma::TasteRegister.instance.aliases
|
|
102
|
+
tastes[taste.to_sym] and taste = tastes[taste.to_sym]
|
|
103
|
+
taste
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def taste2isodoc_attrs(taste, format)
|
|
107
|
+
tastes = Metanorma::TasteRegister.instance.aliases
|
|
108
|
+
tastes[taste.to_sym] or return {}
|
|
109
|
+
Metanorma::TasteRegister.isodoc_attrs(taste.to_sym, format)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def taste2coverpage_pdf_portfolio(taste)
|
|
113
|
+
tastes = Metanorma::TasteRegister.instance.aliases
|
|
114
|
+
tastes[taste.to_sym] or return nil
|
|
115
|
+
taste = Metanorma::TasteRegister.instance.get(taste.to_sym)
|
|
116
|
+
ret = taste.config.base_override&.filename_attributes
|
|
117
|
+
&.coverpage_pdf_portfolio or return
|
|
118
|
+
File.join(taste.directory, ret)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
SVG_NS = "http://www.w3.org/2000/svg".freeze
|
|
122
|
+
|
|
123
|
+
# update relative URLs, url(#...), in CSS in @style attrs (incl. SVG),
|
|
124
|
+
# and in include SVG url(#..) attrs,
|
|
125
|
+
# not processed already by add_suffix_to_attrs
|
|
126
|
+
def url_in_css_styles(doc, ids, document_suffix)
|
|
127
|
+
update_ids_css(doc.root, ids, document_suffix)
|
|
128
|
+
doc.xpath("//i:svg", "i" => SVG_NS).each do |s|
|
|
129
|
+
svg = Vectory::SvgDocument.new(s.to_xml)
|
|
130
|
+
svg.suffix_ids(document_suffix)
|
|
73
131
|
end
|
|
74
132
|
end
|
|
75
133
|
|
|
134
|
+
# replicates Vectory update_ids_css, but skips over svgs,
|
|
135
|
+
# which are processed separately by Vectory::SvgDocument#suffix_ids
|
|
136
|
+
def update_ids_css(document, ids, suffix)
|
|
137
|
+
suffix = suffix.is_a?(Integer) ? sprintf("%09d", suffix) : suffix
|
|
138
|
+
document.xpath(".//m:style[not(ancestor::i:svg)]",
|
|
139
|
+
"m" => SVG_NS, "i" => SVG_NS).each do |s|
|
|
140
|
+
s.children = update_ids_css_string(s.children.to_xml, ids, suffix)
|
|
141
|
+
end
|
|
142
|
+
document.xpath(".//*[@style][not(ancestor::i:svg)]",
|
|
143
|
+
"i" => SVG_NS).each do |s|
|
|
144
|
+
s["style"] = update_ids_css_string(s["style"], ids, suffix)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Updates ID references in a CSS style string.
|
|
149
|
+
# Replicates Vectory update_ids_css_string
|
|
150
|
+
def update_ids_css_string(style, ids, suffix)
|
|
151
|
+
ids.each do |i|
|
|
152
|
+
style = style.gsub(%r[##{i}\b],
|
|
153
|
+
sprintf("#%<id>s_%<suffix>s", id: i,
|
|
154
|
+
suffix: suffix))
|
|
155
|
+
.gsub(%r(\[id\s*=\s*['"]?#{i}['"]?\]),
|
|
156
|
+
sprintf("[id='%<id>s_%<suffix>s']", id: i,
|
|
157
|
+
suffix: suffix))
|
|
158
|
+
end
|
|
159
|
+
style
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# doc.xpath("//*[@style]").each do |s|
|
|
163
|
+
# s["style"] = url_in_css_styles1(s["style"], document_suffix)
|
|
164
|
+
# end
|
|
165
|
+
# doc.xpath("//i:svg//i:style", "i" => "http://www.w3.org/2000/svg")
|
|
166
|
+
# .each do |s|
|
|
167
|
+
# s.children = url_in_css_styles1(s.text, document_suffix)
|
|
168
|
+
# end
|
|
169
|
+
#
|
|
170
|
+
# # KILL
|
|
171
|
+
# def url_in_css_styles1(style, document_suffix)
|
|
172
|
+
# style.gsub(%r{url\(#([^()]+)\)}, "url(#\\1_#{document_suffix})")
|
|
173
|
+
# end
|
|
174
|
+
|
|
175
|
+
# @deprecated Call Metanorma::Core::Isodoc.resolve_converter directly.
|
|
176
|
+
def load_isodoc(flavor, presxml: false)
|
|
177
|
+
Metanorma::Core::Isodoc.resolve_converter(flavor, presxml: presxml)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# @deprecated Call Metanorma::Core::Isodoc.create directly. Kept as
|
|
181
|
+
# a back-compat shim so existing callers keep working unchanged
|
|
182
|
+
# while the wrapper is retired.
|
|
76
183
|
def isodoc_create(flavor, lang, script, xml, presxml: false)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
184
|
+
Metanorma::Core::Isodoc.create(flavor, lang: lang, script: script,
|
|
185
|
+
xml: xml, presxml: presxml)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Dummy asciidoc header used by the prefatory-content parse so we
|
|
189
|
+
# can run Asciidoctor against arbitrary text. If +docidentifier+
|
|
190
|
+
# is supplied, it is injected as +:docidentifier:+ so the flavor
|
|
191
|
+
# processor's metadata_id has a real value to put into the
|
|
192
|
+
# bibdata, instead of falling back to its (Liquid-templated)
|
|
193
|
+
# docid_template. This prevents the prefatory parse from feeding
|
|
194
|
+
# an unresolved Liquid template through Relaton/pubid in flavors
|
|
195
|
+
# whose relaton-* gem eagerly parses the docidentifier in
|
|
196
|
+
# content= (issue #558).
|
|
197
|
+
def asciidoc_dummy_header(docidentifier: nil)
|
|
198
|
+
attrs = docidentifier ? ":docidentifier: #{docidentifier}\n" : ""
|
|
199
|
+
"= X\nA\n#{attrs}\n"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def nokogiri_to_temp(xml, filename, suffix)
|
|
203
|
+
file = Tempfile.new([filename, suffix])
|
|
204
|
+
file.write(xml.to_xml(indent: 0))
|
|
205
|
+
file.close
|
|
206
|
+
[file, file.path]
|
|
207
|
+
end
|
|
84
208
|
end
|
|
85
209
|
end
|
|
86
210
|
end
|
|
@@ -10,8 +10,7 @@ module Metanorma
|
|
|
10
10
|
@isodoc = isodoc
|
|
11
11
|
key = (0...8).map { rand(65..90).chr }.join # random string
|
|
12
12
|
xml.root["type"] = key
|
|
13
|
-
|
|
14
|
-
.each do |(tag_name, attr_name)|
|
|
13
|
+
Util::anchor_id_attributes.each do |(tag_name, attr_name)|
|
|
15
14
|
#tag_name == "xref" and tag_name = "fmt-xref"
|
|
16
15
|
::Metanorma::Collection::Util::add_suffix_to_attrs(
|
|
17
16
|
xml, xml.root["document_suffix"], tag_name, attr_name, isodoc
|
|
@@ -37,7 +36,7 @@ module Metanorma
|
|
|
37
36
|
def svg_preprocess(xml, doc_suffix, presxml)
|
|
38
37
|
suffix = doc_suffix.nil? || doc_suffix.blank? ? "" : "_#{doc_suffix}"
|
|
39
38
|
xml.xpath("//m:svg", "m" => "http://www.w3.org/2000/svg").each do |s|
|
|
40
|
-
m = svgmap_wrap(s)
|
|
39
|
+
m = svgmap_wrap(s) or next
|
|
41
40
|
svg_xrefs(s, m, suffix, presxml)
|
|
42
41
|
end
|
|
43
42
|
xml
|
|
@@ -45,7 +44,7 @@ module Metanorma
|
|
|
45
44
|
|
|
46
45
|
def svgmap_wrap(svg)
|
|
47
46
|
ret = svg.at("./ancestor::xmlns:svgmap") and return ret
|
|
48
|
-
ret = svg.at("./ancestor::xmlns:figure")
|
|
47
|
+
ret = svg.at("./ancestor::xmlns:figure") or return
|
|
49
48
|
ret.wrap("<svgmap/>")
|
|
50
49
|
svg.at("./ancestor::xmlns:svgmap")
|
|
51
50
|
end
|
|
@@ -76,7 +75,7 @@ module Metanorma
|
|
|
76
75
|
xref_prefix_key(x, key, indirect)
|
|
77
76
|
x["bibitemid"] = x["target"]
|
|
78
77
|
m[x["bibitemid"]] = true
|
|
79
|
-
xref_to_internal_eref_anchor(x,
|
|
78
|
+
xref_to_internal_eref_anchor(x, bibitems,
|
|
80
79
|
xml.root["document_suffix"], presxml)
|
|
81
80
|
end.keys
|
|
82
81
|
end
|
|
@@ -88,8 +87,9 @@ module Metanorma
|
|
|
88
87
|
[bibitems, indirect_bibitems]
|
|
89
88
|
end
|
|
90
89
|
|
|
91
|
-
def xref_to_internal_eref_anchor(xref,
|
|
90
|
+
def xref_to_internal_eref_anchor(xref, bibitems, document_suffix, presxml)
|
|
92
91
|
t = xref["target"]
|
|
92
|
+
key = xref["type"]
|
|
93
93
|
if d = bibitems[t]&.at(ns("./docidentifier[@type = 'repository']"))
|
|
94
94
|
m = %r{^([^/]+)}.match(d.text) and
|
|
95
95
|
t.sub!(%r(#{m[0]}_), "")
|
|
@@ -225,7 +225,7 @@ module Metanorma
|
|
|
225
225
|
|
|
226
226
|
def new_indirect_bibitem(ident, prefix)
|
|
227
227
|
<<~BIBENTRY
|
|
228
|
-
<bibitem id="#{ident}" type="internal">
|
|
228
|
+
<bibitem id="#{ident}" anchor="#{ident}" type="internal">
|
|
229
229
|
<docidentifier type="repository">#{ident.sub(/^#{prefix}_/, "#{prefix}/")}</docidentifier>
|
|
230
230
|
</bibitem>
|
|
231
231
|
BIBENTRY
|