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,133 @@
|
|
1
|
+
module Metanorma
|
2
|
+
class Collection
|
3
|
+
class Document
|
4
|
+
# @return [Strin]
|
5
|
+
attr_reader :file, :attachment, :bibitem, :index
|
6
|
+
|
7
|
+
# @param bibitem [RelatonBib::BibliographicItem]
|
8
|
+
def initialize(bibitem, file, options = {})
|
9
|
+
@bibitem = bibitem
|
10
|
+
@file = file
|
11
|
+
@attachment = options[:attachment]
|
12
|
+
@index = options[:index]
|
13
|
+
@index = true if @index.nil?
|
14
|
+
@raw = options[:raw]
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# @param file [String] file path
|
19
|
+
# @param attachment [Bool] is an attachment
|
20
|
+
# @param identifier [String] is the identifier assigned the file
|
21
|
+
# in the collection file
|
22
|
+
# @param index [Bool] is indication on whether to index this file in coverpage
|
23
|
+
# @return [Metanorma::Document]
|
24
|
+
def parse_file(file, attachment, identifier = nil, index = true)
|
25
|
+
new(bibitem(file, attachment, identifier), file,
|
26
|
+
{ attachment: attachment, index: index })
|
27
|
+
end
|
28
|
+
|
29
|
+
# #param xml [Nokogiri::XML::Document, Nokogiri::XML::Element]
|
30
|
+
# @return [Metanorma::Document]
|
31
|
+
def parse_xml(xml)
|
32
|
+
new from_xml(xml)
|
33
|
+
end
|
34
|
+
|
35
|
+
# raw XML file, can be used to put in entire file instead of just bibitem
|
36
|
+
def raw_file(filename)
|
37
|
+
doc = Nokogiri::XML(File.read(filename, encoding: "UTF-8"), &:huge)
|
38
|
+
new(doc, filename, raw: true)
|
39
|
+
end
|
40
|
+
|
41
|
+
def attachment_bibitem(identifier)
|
42
|
+
Nokogiri::XML <<~DOCUMENT
|
43
|
+
<bibdata><docidentifier>#{identifier}</docidentifier></bibdata>
|
44
|
+
DOCUMENT
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def mn2relaton_parser(tag)
|
50
|
+
case tag.sub(/-standard/, "")
|
51
|
+
when "bipm" then RelatonBipm::XMLParser
|
52
|
+
when "bsi" then RelatonBsi::XMLParser
|
53
|
+
when "ietf" then RelatonIetf::XMLParser
|
54
|
+
when "iho" then RelatonIho::XMLParser
|
55
|
+
when "itu" then RelatonItu::XMLParser
|
56
|
+
when "iec" then RelatonIec::XMLParser
|
57
|
+
when "iso" then RelatonIsoBib::XMLParser
|
58
|
+
when "nist" then RelatonNist::XMLParser
|
59
|
+
when "ogc" then RelatonOgc::XMLParser
|
60
|
+
else RelatonBib::XMLParser
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# #param xml [Nokogiri::XML::Document, Nokogiri::XML::Element]
|
65
|
+
# @return [RelatonBib::BibliographicItem,RelatonIso::IsoBibliographicItem]
|
66
|
+
def from_xml(xml)
|
67
|
+
b = xml.at("//xmlns:bibitem|//xmlns:bibdata")
|
68
|
+
r = mn2relaton_parser(xml.root.name)
|
69
|
+
r.from_xml(b.to_xml)
|
70
|
+
end
|
71
|
+
|
72
|
+
# @param file [String]
|
73
|
+
# @return [Symbol] file type
|
74
|
+
def format(file)
|
75
|
+
case file
|
76
|
+
when /\.xml$/ then :xml
|
77
|
+
when /.ya?ml$/ then :yaml
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param file [String]
|
82
|
+
# @return [RelatonBib::BibliographicItem,
|
83
|
+
# RelatonIso::IsoBibliographicItem]
|
84
|
+
def bibitem(file, attachment, identifier)
|
85
|
+
if attachment then attachment_bibitem(identifier)
|
86
|
+
else
|
87
|
+
case format(file)
|
88
|
+
when :xml
|
89
|
+
from_xml (Nokogiri::XML(File.read(file, encoding: "UTF-8"),
|
90
|
+
&:huge))
|
91
|
+
when :yaml
|
92
|
+
yaml = File.read(file, encoding: "UTF-8")
|
93
|
+
Relaton::Cli::YAMLConvertor.convert_single_file(yaml)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# @param builder [Nokogiri::XML::Builder, nil]
|
100
|
+
# @return [Nokogiri::XML::Builder, String]
|
101
|
+
def to_xml(builder = nil)
|
102
|
+
if builder
|
103
|
+
render_xml builder
|
104
|
+
else
|
105
|
+
Nokogiri::XML::Builder.new do |b|
|
106
|
+
root = render_xml b
|
107
|
+
root["xmlns"] = "http://metanorma.org"
|
108
|
+
end.to_xml
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @return [String]
|
113
|
+
def type
|
114
|
+
first = @bibitem.docidentifier.first
|
115
|
+
@type ||= (first&.type&.downcase ||
|
116
|
+
first&.id&.match(/^[^\s]+/)&.to_s)&.downcase ||
|
117
|
+
"standoc"
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def render_xml(builder)
|
123
|
+
if @raw
|
124
|
+
builder.parent.add_child(@bibitem.root)
|
125
|
+
else
|
126
|
+
builder.send("#{type}-standard") do |b|
|
127
|
+
b << @bibitem.to_xml(bibdata: true)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
require "isodoc"
|
2
|
+
require "htmlentities"
|
3
|
+
require "metanorma-utils"
|
4
|
+
require_relative "filelookup_sectionsplit"
|
5
|
+
|
6
|
+
module Metanorma
|
7
|
+
class Collection
|
8
|
+
class FileLookup
|
9
|
+
attr_accessor :files_to_delete, :parent
|
10
|
+
|
11
|
+
# hash for each document in collection of document identifier to:
|
12
|
+
# document reference (fileref or id), type of document reference,
|
13
|
+
# and bibdata entry for that file
|
14
|
+
# @param path [String] path to collection
|
15
|
+
def initialize(path, parent)
|
16
|
+
@c = HTMLEntities.new
|
17
|
+
@files = {}
|
18
|
+
@parent = parent
|
19
|
+
@xml = parent.xml
|
20
|
+
@isodoc = parent.isodoc
|
21
|
+
@path = path
|
22
|
+
@compile = parent.compile
|
23
|
+
@documents = parent.documents
|
24
|
+
@files_to_delete = []
|
25
|
+
@disambig = Util::DisambigFiles.new
|
26
|
+
@manifest = parent.manifest
|
27
|
+
read_files(@manifest.entry)
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_files(entries)
|
31
|
+
Array(entries).each do |e|
|
32
|
+
e.file and read_file(e)
|
33
|
+
read_files(e.entry)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def read_file(manifest)
|
38
|
+
i, k = read_file_idents(manifest)
|
39
|
+
entry = file_entry(manifest, k) or return
|
40
|
+
bibdata_process(entry, i)
|
41
|
+
bibitem_process(entry)
|
42
|
+
@files[key(i)] = entry
|
43
|
+
end
|
44
|
+
|
45
|
+
def read_file_idents(manifest)
|
46
|
+
id = manifest.identifier
|
47
|
+
sanitised_id = key(@isodoc.docid_prefix("", manifest.identifier.dup))
|
48
|
+
# if manifest.bibdata and # NO, DO NOT FISH FOR THE GENUINE IDENTIFIER IN BIBDATA
|
49
|
+
# d = manifest.bibdata.docidentifier.detect { |x| x.primary } ||
|
50
|
+
# manifest.bibdata.docidentifier.first
|
51
|
+
# k = d.id
|
52
|
+
# i = key(@isodoc.docid_prefix(d.type, d.id.dup))
|
53
|
+
# end
|
54
|
+
[id, sanitised_id]
|
55
|
+
end
|
56
|
+
|
57
|
+
def bibdata_process(entry, ident)
|
58
|
+
if entry[:attachment]
|
59
|
+
entry[:bibdata] =
|
60
|
+
Metanorma::Collection::Document.attachment_bibitem(ident).root
|
61
|
+
else
|
62
|
+
file, _filename = targetfile(entry, read: true)
|
63
|
+
xml = Nokogiri::XML(file, &:huge)
|
64
|
+
add_document_suffix(ident, xml)
|
65
|
+
entry.merge!(anchors: read_anchors(xml), ids: read_ids(xml),
|
66
|
+
bibdata: xml.at(ns("//bibdata")),
|
67
|
+
document_suffix: xml.root["document_suffix"])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def bibitem_process(entry)
|
72
|
+
entry[:bibitem] = entry[:bibdata].dup
|
73
|
+
entry[:bibitem].name = "bibitem"
|
74
|
+
entry[:bibitem]["hidden"] = "true"
|
75
|
+
entry[:bibitem].at("./*[local-name() = 'ext']")&.remove
|
76
|
+
end
|
77
|
+
|
78
|
+
# ref is the absolute source file address
|
79
|
+
# rel_path is the relative source file address, relative to the YAML locaton
|
80
|
+
# out_path is the destination file address, with any references outside
|
81
|
+
# the working directory (../../...) truncated, and based on relative path
|
82
|
+
# identifier is the id with only spaces, no nbsp
|
83
|
+
def file_entry(ref, identifier)
|
84
|
+
ref.file or return
|
85
|
+
abs = @documents[Util::key identifier].file
|
86
|
+
ret = if ref.file
|
87
|
+
{ type: "fileref", ref: abs, rel_path: ref.file, url: ref.url,
|
88
|
+
out_path: output_file_path(ref) }
|
89
|
+
else { type: "id", ref: ref.id }
|
90
|
+
end
|
91
|
+
file_entry_copy(ref, ret)
|
92
|
+
ret.compact
|
93
|
+
end
|
94
|
+
|
95
|
+
# TODO make the output file location reflect source location universally,
|
96
|
+
# not just for attachments: no File.basename
|
97
|
+
def output_file_path(ref)
|
98
|
+
f = File.basename(ref.file)
|
99
|
+
ref.attachment and f = ref.file
|
100
|
+
@disambig.source2dest_filename(f)
|
101
|
+
end
|
102
|
+
|
103
|
+
def file_entry_copy(ref, ret)
|
104
|
+
%w(attachment sectionsplit index presentation-xml url
|
105
|
+
bare-after-first).each do |s|
|
106
|
+
ref.respond_to?(s.to_sym) and
|
107
|
+
ret[s.gsub("-", "").to_sym] = ref.send(s)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def add_document_suffix(identifier, doc)
|
112
|
+
document_suffix = Metanorma::Utils::to_ncname(identifier)
|
113
|
+
Metanorma::Utils::anchor_attributes.each do |(tag_name, attribute_name)|
|
114
|
+
Util::add_suffix_to_attributes(doc, document_suffix, tag_name,
|
115
|
+
attribute_name, @isodoc)
|
116
|
+
end
|
117
|
+
url_in_css_styles(doc, document_suffix)
|
118
|
+
doc.root["document_suffix"] ||= ""
|
119
|
+
doc.root["document_suffix"] += document_suffix
|
120
|
+
end
|
121
|
+
|
122
|
+
# update relative URLs, url(#...), in CSS in @style attrs (including SVG)
|
123
|
+
def url_in_css_styles(doc, document_suffix)
|
124
|
+
doc.xpath("//*[@style]").each do |s|
|
125
|
+
s["style"] = s["style"]
|
126
|
+
.gsub(%r{url\(#([^)]+)\)}, "url(#\\1_#{document_suffix})")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# return citation url for file
|
131
|
+
# @param doc [Boolean] I am a Metanorma document,
|
132
|
+
# so my URL should end with html or pdf or whatever
|
133
|
+
def url(ident, options)
|
134
|
+
data = get(ident)
|
135
|
+
data[:url] || targetfile(data, options)[1]
|
136
|
+
end
|
137
|
+
|
138
|
+
# are references to the file to be linked to a file in the collection,
|
139
|
+
# or externally? Determines whether file suffix anchors are to be used
|
140
|
+
def url?(ident)
|
141
|
+
data = get(ident) or return false
|
142
|
+
data[:url]
|
143
|
+
end
|
144
|
+
|
145
|
+
# return file contents + output filename for each file in the collection,
|
146
|
+
# given a docref entry
|
147
|
+
# @param data [Hash] docref entry
|
148
|
+
# @param read [Boolean] read the file in and return it
|
149
|
+
# @param doc [Boolean] I am a Metanorma document,
|
150
|
+
# so my URL should end with html or pdf or whatever
|
151
|
+
# @param relative [Boolean] Return output path,
|
152
|
+
# formed relative to YAML file, not input path, relative to calling function
|
153
|
+
# @return [Array<String, nil>]
|
154
|
+
def targetfile(data, options)
|
155
|
+
options = { read: false, doc: true, relative: false }.merge(options)
|
156
|
+
path = options[:relative] ? data[:rel_path] : data[:ref]
|
157
|
+
if data[:type] == "fileref"
|
158
|
+
ref_file path, data[:out_path], options[:read], options[:doc]
|
159
|
+
else
|
160
|
+
xml_file data[:id], options[:read]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def targetfile_id(ident, options)
|
165
|
+
targetfile(get(ident), options)
|
166
|
+
end
|
167
|
+
|
168
|
+
def ref_file(ref, out, read, doc)
|
169
|
+
file = File.read(ref, encoding: "utf-8") if read
|
170
|
+
filename = out.dup
|
171
|
+
filename.sub!(/\.xml$/, ".html") if doc
|
172
|
+
[file, filename]
|
173
|
+
end
|
174
|
+
|
175
|
+
def xml_file(id, read)
|
176
|
+
file = @xml.at(ns("//doc-container[@id = '#{id}']")).to_xml if read
|
177
|
+
filename = "#{id}.html"
|
178
|
+
[file, filename]
|
179
|
+
end
|
180
|
+
|
181
|
+
# map locality type and label (e.g. "clause" "1") to id = anchor for
|
182
|
+
# a document
|
183
|
+
# Note: will only key clauses, which have unambiguous reference label in
|
184
|
+
# locality. Notes, examples etc with containers are just plunked against
|
185
|
+
# UUIDs, so that their IDs can at least be registered to be tracked
|
186
|
+
# as existing.
|
187
|
+
def read_anchors(xml)
|
188
|
+
xrefs = @isodoc.xref_init(@lang, @script, @isodoc, @isodoc.i18n,
|
189
|
+
{ locale: @locale })
|
190
|
+
xrefs.parse xml
|
191
|
+
xrefs.get.each_with_object({}) do |(k, v), ret|
|
192
|
+
read_anchors1(k, v, ret)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def read_anchors1(key, val, ret)
|
197
|
+
val[:type] ||= "clause"
|
198
|
+
ret[val[:type]] ||= {}
|
199
|
+
index = if val[:container] || val[:label].nil? || val[:label].empty?
|
200
|
+
UUIDTools::UUID.random_create.to_s
|
201
|
+
else val[:label]
|
202
|
+
end
|
203
|
+
ret[val[:type]][index] = key
|
204
|
+
ret[val[:type]][val[:value]] = key if val[:value]
|
205
|
+
end
|
206
|
+
|
207
|
+
# Also parse all ids in doc (including ones which won't be xref targets)
|
208
|
+
def read_ids(xml)
|
209
|
+
ret = {}
|
210
|
+
xml.traverse do |x|
|
211
|
+
x.text? and next
|
212
|
+
/^semantic__/.match?(x.name) and next
|
213
|
+
x["id"] and ret[x["id"]] = true
|
214
|
+
end
|
215
|
+
ret
|
216
|
+
end
|
217
|
+
|
218
|
+
def key(ident)
|
219
|
+
@c.decode(ident).gsub(/(\p{Zs})+/, " ").sub(/^metanorma-collection /,
|
220
|
+
"")
|
221
|
+
end
|
222
|
+
|
223
|
+
def keys
|
224
|
+
@files.keys
|
225
|
+
end
|
226
|
+
|
227
|
+
def get(ident, attr = nil)
|
228
|
+
if attr then @files[key(ident)][attr]
|
229
|
+
else @files[key(ident)]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def set(ident, attr, value)
|
234
|
+
@files[key(ident)][attr] = value
|
235
|
+
end
|
236
|
+
|
237
|
+
def each
|
238
|
+
@files.each
|
239
|
+
end
|
240
|
+
|
241
|
+
def each_with_index
|
242
|
+
@files.each_with_index
|
243
|
+
end
|
244
|
+
|
245
|
+
def ns(xpath)
|
246
|
+
@isodoc.ns(xpath)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require_relative "../sectionsplit/sectionsplit"
|
2
|
+
|
3
|
+
module Metanorma
|
4
|
+
class Collection
|
5
|
+
class FileLookup
|
6
|
+
def add_section_split
|
7
|
+
ret = @files.keys.each_with_object({}) do |k, m|
|
8
|
+
if @files[k][:sectionsplit] && !@files[k][:attachment]
|
9
|
+
process_section_split_instance(k, m)
|
10
|
+
cleanup_section_split_instance(k, m)
|
11
|
+
end
|
12
|
+
m[k] = @files[k]
|
13
|
+
end
|
14
|
+
@files = ret
|
15
|
+
end
|
16
|
+
|
17
|
+
def process_section_split_instance(key, manifest)
|
18
|
+
s, sectionsplit_manifest = sectionsplit(key)
|
19
|
+
s.each_with_index do |f1, i|
|
20
|
+
add_section_split_instance(f1, manifest, key, i)
|
21
|
+
end
|
22
|
+
manifest["#{key}:index.html"] =
|
23
|
+
add_section_split_cover(sectionsplit_manifest, key)
|
24
|
+
end
|
25
|
+
|
26
|
+
def cleanup_section_split_instance(key, manifest)
|
27
|
+
@files_to_delete << manifest["#{key}:index.html"][:ref]
|
28
|
+
# @files[key].delete(:ids).delete(:anchors)
|
29
|
+
@files[key][:indirect_key] = @sectionsplit.key
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_section_split_cover(manifest, ident)
|
33
|
+
cover = @sectionsplit
|
34
|
+
.section_split_cover(manifest, @parent.dir_name_cleanse(ident),
|
35
|
+
one_doc_collection?)
|
36
|
+
@files[ident][:out_path] = cover
|
37
|
+
{ attachment: true, index: false, out_path: cover,
|
38
|
+
ref: File.join(File.dirname(manifest.file), cover) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def one_doc_collection?
|
42
|
+
return false
|
43
|
+
docs = 0
|
44
|
+
@files.each_value do |v|
|
45
|
+
v[:attachment] and next
|
46
|
+
v[:presentationxml] and next
|
47
|
+
docs += 1
|
48
|
+
end
|
49
|
+
docs > 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_section_split_instance(file, manifest, key, idx)
|
53
|
+
presfile, newkey, xml =
|
54
|
+
add_section_split_instance_prep(file, key)
|
55
|
+
manifest[newkey] =
|
56
|
+
{ parentid: key, presentationxml: true, type: "fileref",
|
57
|
+
rel_path: file[:url], out_path: File.basename(file[:url]),
|
58
|
+
anchors: read_anchors(xml), ids: read_ids(xml),
|
59
|
+
sectionsplit_output: true,
|
60
|
+
bibdata: @files[key][:bibdata], ref: presfile }
|
61
|
+
@files_to_delete << file[:url]
|
62
|
+
manifest[newkey][:bare] = true unless idx.zero?
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_section_split_instance_prep(file, key)
|
66
|
+
presfile = File.join(File.dirname(@files[key][:ref]),
|
67
|
+
File.basename(file[:url]))
|
68
|
+
newkey = key("#{key.strip} #{file[:title]}")
|
69
|
+
xml = Nokogiri::XML(File.read(presfile), &:huge)
|
70
|
+
[presfile, newkey, xml]
|
71
|
+
end
|
72
|
+
|
73
|
+
def sectionsplit(ident)
|
74
|
+
file = @files[ident][:ref]
|
75
|
+
@sectionsplit = ::Metanorma::Collection::Sectionsplit
|
76
|
+
.new(input: file, base: @files[ident][:out_path], dir: File.dirname(file),
|
77
|
+
output: @files[ident][:out_path], compile_opts: @parent.compile_options,
|
78
|
+
fileslookup: self, ident: ident, isodoc: @isodoc)
|
79
|
+
coll = @sectionsplit.sectionsplit.sort_by { |f| f[:order] }
|
80
|
+
xml = Nokogiri::XML(File.read(file, encoding: "UTF-8"), &:huge)
|
81
|
+
[coll, @sectionsplit
|
82
|
+
.collection_manifest(File.basename(file), coll, xml, nil,
|
83
|
+
File.dirname(file))]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../util/util"
|
4
|
+
|
5
|
+
module Metanorma
|
6
|
+
class Collection
|
7
|
+
class Manifest
|
8
|
+
# @return [Metanorma::Collection]
|
9
|
+
attr_reader :collection, :config
|
10
|
+
|
11
|
+
# @param level [String]
|
12
|
+
# @param dir [String]
|
13
|
+
# @param title [String, nil]
|
14
|
+
# @param docref [Array<Hash{String=>String}>]
|
15
|
+
# @param manifest [Array<Metanorma::Collection::Manifest>]
|
16
|
+
def initialize(config, collection, dir)
|
17
|
+
@collection = collection
|
18
|
+
@dir = dir
|
19
|
+
@disambig = ::Metanorma::Collection::Util::DisambigFiles.new
|
20
|
+
@config = manifest_postprocess(config)
|
21
|
+
end
|
22
|
+
|
23
|
+
def manifest_postprocess(config)
|
24
|
+
manifest_bibdata(config)
|
25
|
+
manifest_expand_yaml(config, @dir)
|
26
|
+
manifest_compile_adoc(config)
|
27
|
+
manifest_filexist(config)
|
28
|
+
manifest_sectionsplit(config)
|
29
|
+
manifest_identifier(config)
|
30
|
+
config
|
31
|
+
end
|
32
|
+
|
33
|
+
def manifest_bibdata(config)
|
34
|
+
b = config.bibdata
|
35
|
+
@lang = b&.language&.first || "en"
|
36
|
+
@script = b&.script&.first || "Latn"
|
37
|
+
end
|
38
|
+
|
39
|
+
GUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
|
40
|
+
|
41
|
+
def manifest_identifier(config)
|
42
|
+
no_id = populate_id_from_doc(config)
|
43
|
+
config.identifier =
|
44
|
+
if no_id && config.file # make file name be id
|
45
|
+
@collection.class.resolve_identifier(File.basename(config.file))
|
46
|
+
else
|
47
|
+
@collection.class.resolve_identifier(config.identifier)
|
48
|
+
end
|
49
|
+
Array(config.entry).each do |f|
|
50
|
+
manifest_identifier(f)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def populate_id_from_doc(config)
|
55
|
+
no_id = /^#{GUID}$/o.match?(config.identifier)
|
56
|
+
# GUID assumed to be no identifier supplied
|
57
|
+
if no_id && /\.xml$/.match?(config.file) &&
|
58
|
+
(i = retrieve_id_from_doc(config.file))
|
59
|
+
config.identifier = i
|
60
|
+
no_id = false
|
61
|
+
end
|
62
|
+
no_id
|
63
|
+
end
|
64
|
+
|
65
|
+
def retrieve_id_from_doc(file)
|
66
|
+
x = Nokogiri::XML(File.read(File.join(@dir, file)), &:huge)
|
67
|
+
i = x.at("//xmlns:bibdata/xmlns:docidentifier[@primary = 'true']") ||
|
68
|
+
x.at("//xmlns:bibdata/xmlns:docidentifier")
|
69
|
+
i or return nil
|
70
|
+
@doctype ||= i["type"]&.downcase || "standoc"
|
71
|
+
load_isodoc
|
72
|
+
Util::key(@isodoc.docid_prefix(i["type"], i.text))
|
73
|
+
end
|
74
|
+
|
75
|
+
def load_isodoc
|
76
|
+
@isodoc and return
|
77
|
+
@collection.compile.load_flavor(@doctype)
|
78
|
+
@isodoc = Util::load_isodoc(@doctype)
|
79
|
+
@isodoc.i18n_init(@lang, @script, nil) # for @i18n.all_parts in docid
|
80
|
+
end
|
81
|
+
|
82
|
+
def manifest_sectionsplit(config)
|
83
|
+
if config.sectionsplit && !config.file
|
84
|
+
config.sectionsplit = nil
|
85
|
+
Array(config.entry).each do |e|
|
86
|
+
e.attachment and next
|
87
|
+
e.sectionsplit = true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
Array(config.entry).each do |f|
|
91
|
+
manifest_sectionsplit(f)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def manifest_filexist(config)
|
96
|
+
if config.file
|
97
|
+
file = @collection.class.resolve_fileref(@dir, config.file)
|
98
|
+
@collection.class.check_file_existence(file)
|
99
|
+
config.file = Pathname.new(file).relative_path_from(Pathname.new(@dir))
|
100
|
+
end
|
101
|
+
Array(config.entry).each do |f|
|
102
|
+
manifest_filexist(f)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def manifest_expand_yaml(config, dir)
|
107
|
+
Array(config.entry).each do |e|
|
108
|
+
currdir = dir
|
109
|
+
/\.ya?ml$/.match?(e.file) and
|
110
|
+
currdir = manifest_expand_yaml_entry(e, dir)
|
111
|
+
manifest_expand_yaml(e, currdir)
|
112
|
+
end
|
113
|
+
config
|
114
|
+
end
|
115
|
+
|
116
|
+
def manifest_expand_yaml_entry(entry, dir)
|
117
|
+
f = @collection.class.resolve_fileref(dir, entry.file)
|
118
|
+
currdir = File.dirname(f)
|
119
|
+
@collection.class.check_file_existence(f)
|
120
|
+
entry.file = nil
|
121
|
+
entry.entry = ::Metanorma::Collection::Config::Config.from_yaml(File.read(f)).manifest
|
122
|
+
if currdir != dir
|
123
|
+
prefix = Pathname.new(currdir).relative_path_from(Pathname.new(dir))
|
124
|
+
update_filepaths(entry.entry, prefix.to_s)
|
125
|
+
end
|
126
|
+
currdir
|
127
|
+
end
|
128
|
+
|
129
|
+
def update_filepaths(entry, prefix)
|
130
|
+
entry.file && !(Pathname.new entry.file).absolute? and
|
131
|
+
entry.file = File.join(prefix, entry.file)
|
132
|
+
entry.entry.each do |f|
|
133
|
+
update_filepaths(f, prefix)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def manifest_compile_adoc(config)
|
138
|
+
if /\.adoc$/.match?(config.file)
|
139
|
+
file = @collection.class.resolve_fileref(@dir, config.file)
|
140
|
+
config.file = compile_adoc(file, config.file)
|
141
|
+
end
|
142
|
+
Array(config.entry).each do |f|
|
143
|
+
manifest_compile_adoc(f)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def compile_adoc(resolved_filename, rel_filename)
|
148
|
+
compile_adoc_file(resolved_filename)
|
149
|
+
set_adoc2xml(rel_filename)
|
150
|
+
end
|
151
|
+
|
152
|
+
# @param fileref [String]
|
153
|
+
def set_adoc2xml(fileref)
|
154
|
+
File.join(
|
155
|
+
File.dirname(fileref),
|
156
|
+
File.basename(fileref).gsub(/.adoc$/, ".xml"),
|
157
|
+
)
|
158
|
+
end
|
159
|
+
|
160
|
+
# param filepath [String]
|
161
|
+
# @raise [AdocFileNotFoundException]
|
162
|
+
def compile_adoc_file(file)
|
163
|
+
f = (Pathname.new file).absolute? ? file : File.join(@dir, file)
|
164
|
+
unless File.exist? f
|
165
|
+
raise AdocFileNotFoundException.new "#{f} not found!"
|
166
|
+
end
|
167
|
+
|
168
|
+
::Metanorma::Util.log("[metanorma] Info: Compiling #{f}...", :info)
|
169
|
+
::Metanorma::Compile.new
|
170
|
+
.compile(f, agree_to_terms: true, no_install_fonts: true,
|
171
|
+
extension_keys: [:xml])
|
172
|
+
::Metanorma::Util.log("[metanorma] Info: Compiling #{f}...done!", :info)
|
173
|
+
end
|
174
|
+
|
175
|
+
def documents(mnf = @config)
|
176
|
+
Array(mnf.entry).each_with_object({}) do |dr, m|
|
177
|
+
if dr.file
|
178
|
+
m[Util::key dr.identifier] = documents_add(dr)
|
179
|
+
elsif dr.entry
|
180
|
+
m.merge! documents(dr)
|
181
|
+
end
|
182
|
+
m
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def documents_add(docref)
|
187
|
+
::Metanorma::Collection::Document.parse_file(
|
188
|
+
Util::rel_path_resolve(@dir, docref.file),
|
189
|
+
docref.attachment, docref.identifier, docref.index
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
193
|
+
def to_xml(builder = nil)
|
194
|
+
clean_manifest(@config)
|
195
|
+
if builder
|
196
|
+
builder.parent.add_child(@config.to_xml)
|
197
|
+
else
|
198
|
+
@config.to_xml
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def clean_manifest_bibdata(mnf)
|
203
|
+
if mnf.file && !mnf.attachment && !mnf.sectionsplit && @collection &&
|
204
|
+
d = @collection.bibdatas[Util::key mnf.identifier]
|
205
|
+
mnf.bibdata = d.bibitem.dup
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def clean_manifest_id(mnf)
|
210
|
+
@collection.directives.detect do |d|
|
211
|
+
d.key == "documents-inline"
|
212
|
+
end or return
|
213
|
+
id = @collection.documents.find_index do |k, _|
|
214
|
+
k == mnf.identifier
|
215
|
+
end
|
216
|
+
id and mnf.id = format("doc%<index>09d", index: id)
|
217
|
+
end
|
218
|
+
|
219
|
+
def clean_manifest(mnf)
|
220
|
+
clean_manifest_bibdata(mnf)
|
221
|
+
# mnf.file &&= @disambig.strip_root(mnf.file)
|
222
|
+
clean_manifest_id(mnf)
|
223
|
+
Array(mnf.entry).each { |e| clean_manifest(e) }
|
224
|
+
end
|
225
|
+
|
226
|
+
# @return [Array<Hash{String=>String}>]
|
227
|
+
def docrefs
|
228
|
+
@config.entry
|
229
|
+
end
|
230
|
+
|
231
|
+
def docref_by_id(docid)
|
232
|
+
@config.entry.detect { |k| k.identifier == docid } ||
|
233
|
+
@config.entry.detect { |k| /^#{k.identifier}/ =~ docid }
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|