metanorma 1.3.5 → 1.3.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4ccf8d6a09fd9345faa44ae7201b4f7ee8816a6f36245c26b2482abd3f7afa9
4
- data.tar.gz: 7997e0bea6b752e07f7db96f19b6d136b7a9740c046a0850022b477a2223a85b
3
+ metadata.gz: 46ea9a7ce8316d94001c40043488f3472bcf639264e446bfb5bfaad88b6bed70
4
+ data.tar.gz: 7e4bfc4c4346c518d845052fd2dbb0b1f51f63e2951a317c18d938f9663cdea3
5
5
  SHA512:
6
- metadata.gz: 162c6e5e79ff9f7f4f41b17444c68ec23168ee593173b72c251c8623f9a16916f6ae557bc581f39d220c2c6d68d6350c2e4f4de7c000a87e90f39bed7e825609
7
- data.tar.gz: a92a8a6ddd41e404cb6a1e00e0a0aaa2ecd645723486769b8656bb09e10f9e595a9df81231713224f6f9781d17c1c6943135d978b5865d0c02842b5e0ccbb84c
6
+ metadata.gz: fdb777c1e35bbeeac65f3e5b0654ead9633a196340017da8cd4bd84342a9500a891a14ac19d83cc0cda0e5301728afdddf367fa3871eece62b389d93d0144238
7
+ data.tar.gz: c264222db4c7e2cbd7342aac8da9c5480a1d48f277ce2c5ef23000443d618379abb0769d111a8846733d60a53364706a65edd1a14de7ada03b94d16182c479bb
@@ -16,7 +16,7 @@ jobs:
16
16
  strategy:
17
17
  fail-fast: false
18
18
  matrix:
19
- ruby: [ '3.0', '2.7', '2.6', '2.5', '2.4' ]
19
+ ruby: [ '3.0', '2.7', '2.6', '2.5' ]
20
20
  os: [ ubuntu-latest, windows-latest, macos-latest ]
21
21
  experimental: [ false ]
22
22
  steps:
data/Gemfile CHANGED
@@ -6,6 +6,6 @@ git_source(:github) { |repo| "https://github.com/#{repo}" }
6
6
 
7
7
  gemspec
8
8
 
9
- if File.exist? 'Gemfile.devel'
10
- eval File.read('Gemfile.devel'), nil, 'Gemfile.devel' # rubocop:disable Security/Eval
9
+ if File.exist? "Gemfile.devel"
10
+ eval File.read("Gemfile.devel"), nil, "Gemfile.devel" # rubocop:disable Security/Eval
11
11
  end
data/lib/metanorma.rb CHANGED
@@ -5,7 +5,6 @@ require "asciidoctor"
5
5
  require "metanorma/util"
6
6
  require "metanorma/config"
7
7
  require "metanorma/input"
8
- require "metanorma/output"
9
8
  require "metanorma/registry"
10
9
  require "metanorma/processor"
11
10
  require "metanorma/asciidoctor_extensions"
@@ -91,7 +91,9 @@ module Metanorma
91
91
  private
92
92
 
93
93
  def parse_xml(file)
94
- xml = Nokogiri::XML File.read(file, encoding: "UTF-8")
94
+ xml = Nokogiri::XML File.read(file, encoding: "UTF-8") do |config|
95
+ config.huge
96
+ end
95
97
  if (b = xml.at("/xmlns:metanorma-collection/xmlns:bibdata"))
96
98
  bd = Relaton::Cli.parse_xml b
97
99
  end
@@ -11,17 +11,21 @@ module Metanorma
11
11
  xrefs = @isodoc.xref_init(@lang, @script, @isodoc, @isodoc.i18n, {})
12
12
  xrefs.parse xml
13
13
  xrefs.get.each_with_object({}) do |(k, v), ret|
14
- v[:type] ||= "clause"
15
- ret[v[:type]] ||= {}
16
- index = if v[:container] || v[:label].nil? || v[:label].empty?
17
- UUIDTools::UUID.random_create.to_s
18
- else v[:label]
19
- end
20
- ret[v[:type]][index] = k
21
- ret[v[:type]][v[:value]] = k if v[:value]
14
+ read_anchors1(k, v, ret)
22
15
  end
23
16
  end
24
17
 
18
+ def read_anchors1(key, val, ret)
19
+ val[:type] ||= "clause"
20
+ ret[val[:type]] ||= {}
21
+ index = if val[:container] || val[:label].nil? || val[:label].empty?
22
+ UUIDTools::UUID.random_create.to_s
23
+ else val[:label]
24
+ end
25
+ ret[val[:type]][index] = key
26
+ ret[val[:type]][val[:value]] = key if val[:value]
27
+ end
28
+
25
29
  # @param id [String]
26
30
  # @param read [Boolean]
27
31
  # @return [Array<String, nil>]
@@ -33,30 +37,36 @@ module Metanorma
33
37
 
34
38
  # @param bib [Nokogiri::XML::Element]
35
39
  # @param identifier [String]
36
- def update_bibitem(bib, identifier) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
40
+ def update_bibitem(bib, identifier) # rubocop:disable Metrics/AbcSize
37
41
  docid = bib&.at(ns("./docidentifier"))&.children&.to_xml
38
- unless @files[docid]
39
- error = "[metanorma] Cannot find crossreference to document #{docid} "\
40
- "in document #{identifier}."
41
- @log.add("Cross-References", nil, error)
42
- Util.log(error, :warning)
43
- return
44
- end
45
- id = bib["id"]
46
- newbib = @files[docid][:bibdata].dup
47
- newbib.name = "bibitem"
48
- newbib["id"] = id
49
- newbib["hidden"] = "true"
50
- newbib&.at("./*[local-name() = 'ext']")&.remove
42
+ return fail_update_bibitem(docid, identifier) unless @files[docid]
43
+
44
+ newbib = dup_bibitem(docid, bib)
51
45
  bib.replace(newbib)
52
46
  _file, url = targetfile(@files[docid], relative: true, read: false,
53
- doc: !@files[docid][:attachment])
47
+ doc: !@files[docid][:attachment])
54
48
  uri_node = Nokogiri::XML::Node.new "uri", newbib
55
49
  uri_node[:type] = "citation"
56
50
  uri_node.content = url
57
51
  newbib.at(ns("./docidentifier")).previous = uri_node
58
52
  end
59
53
 
54
+ def fail_update_bibitem(docid, identifier)
55
+ error = "[metanorma] Cannot find crossreference to document #{docid} "\
56
+ "in document #{identifier}."
57
+ @log.add("Cross-References", nil, error)
58
+ Util.log(error, :warning)
59
+ end
60
+
61
+ def dup_bibitem(docid, bib)
62
+ newbib = @files[docid][:bibdata].dup
63
+ newbib.name = "bibitem"
64
+ newbib["hidden"] = "true"
65
+ newbib&.at("./*[local-name() = 'ext']")&.remove
66
+ newbib["id"] = bib["id"]
67
+ newbib
68
+ end
69
+
60
70
  # Resolves direct links to other files in collection
61
71
  # (repo(current-metanorma-collection/x),
62
72
  # and indirect links to other files in collection
@@ -67,7 +77,7 @@ module Metanorma
67
77
  # @param internal_refs [Hash{String=>Hash{String=>String}] schema name to anchor to filename
68
78
  # @return [String] XML content
69
79
  def update_xrefs(file, identifier, internal_refs)
70
- docxml = Nokogiri::XML(file)
80
+ docxml = Nokogiri::XML(file) { |config| config.huge }
71
81
  update_indirect_refs_to_docs(docxml, internal_refs)
72
82
  add_document_suffix(identifier, docxml)
73
83
  update_direct_refs_to_docs(docxml, identifier)
@@ -106,12 +116,18 @@ module Metanorma
106
116
  # Preferably with anchor, and is a job to realise dynamic lookup
107
117
  # of localities.
108
118
  def update_direct_refs_to_docs(docxml, identifier)
119
+ erefs = docxml.xpath(ns("//eref"))
120
+ .each_with_object({ citeas: {}, bibitemid: {} }) do |i, m|
121
+ m[:citeas][i["citeas"]] = true
122
+ m[:bibitemid][i["bibitemid"]] = true
123
+ end
109
124
  docxml.xpath(ns("//bibitem[not(ancestor::bibitem)]")).each do |b|
110
125
  docid = b&.at(ns("./docidentifier[@type = 'repository']"))&.text
111
126
  next unless docid && %r{^current-metanorma-collection/}.match(docid)
112
127
 
113
128
  update_bibitem(b, identifier)
114
- update_anchors(b, docxml, docid)
129
+ docid = b&.at(ns("./docidentifier"))&.children&.to_xml or next
130
+ erefs[:citeas][docid] and update_anchors(b, docxml, docid)
115
131
  end
116
132
  end
117
133
 
@@ -128,6 +144,9 @@ module Metanorma
128
144
  def update_indirect_refs_to_docs1(docxml, schema, id, file)
129
145
  docxml.xpath(ns("//eref[@bibitemid = '#{schema}_#{id}']")).each do |e|
130
146
  e["citeas"] = file
147
+ if a = e.at(ns(".//locality[@type = 'anchor']/referenceFrom"))
148
+ a.children = "#{a.text}_#{Metanorma::Utils::to_ncname(file)}"
149
+ end
131
150
  end
132
151
  docid = docxml.at(ns("//bibitem[@id = '#{schema}_#{id}']/"\
133
152
  "docidentifier[@type = 'repository']")) or return
@@ -137,21 +156,19 @@ module Metanorma
137
156
 
138
157
  # update crossrefences to other documents, to include
139
158
  # disambiguating document suffix on id
140
- def update_anchors(bib, docxml, _id) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
141
- docid = bib&.at(ns("./docidentifier"))&.children.to_xml
159
+ def update_anchors(bib, docxml, docid) # rubocop:disable Metrics/AbcSize
142
160
  docxml.xpath("//xmlns:eref[@citeas = '#{docid}']").each do |e|
143
- if @files[docid]
144
- update_anchor_loc(bib, e, docid)
161
+ if @files[docid] then update_anchor_loc(bib, e, docid)
145
162
  else
146
- e << "<strong>** Unresolved reference to document #{docid}, "\
147
- "id #{e['bibitemid']}</strong>"
163
+ e << "<strong>** Unresolved reference to document #{docid} "\
164
+ "from eref</strong>"
148
165
  end
149
166
  end
150
167
  end
151
168
 
152
- def update_anchor_loc(bib, e, docid)
153
- loc = e.at(ns(".//locality[@type = 'anchor']")) or
154
- return update_anchor_create_loc(bib, e, docid)
169
+ def update_anchor_loc(bib, eref, docid)
170
+ loc = eref.at(ns(".//locality[@type = 'anchor']")) or
171
+ return update_anchor_create_loc(bib, eref, docid)
155
172
  document_suffix = Metanorma::Utils::to_ncname(docid)
156
173
  ref = loc.at(ns("./referenceFrom")) || return
157
174
  anchor = "#{ref.text}_#{document_suffix}"
@@ -164,18 +181,14 @@ module Metanorma
164
181
 
165
182
  # if there is a crossref to another document, with no anchor, retrieve the
166
183
  # anchor given the locality, and insert it into the crossref
167
- def update_anchor_create_loc(bib, eref, docid)
184
+ def update_anchor_create_loc(_bib, eref, docid)
168
185
  ins = eref.at(ns("./localityStack")) or return
169
186
  type = ins&.at(ns("./locality/@type"))&.text
170
187
  type = "clause" if type == "annex"
171
188
  ref = ins&.at(ns("./locality/referenceFrom"))&.text
172
189
  anchor = @files[docid][:anchors].dig(type, ref) or return
173
- ref_from = Nokogiri::XML::Node.new "referenceFrom", bib
174
- ref_from.content = anchor.sub(/^_/, "")
175
- locality = Nokogiri::XML::Node.new "locality", bib
176
- locality[:type] = "anchor"
177
- locality.add_child ref_from
178
- ins << locality
190
+ ins << "<locality type='anchor'><referenceFrom>#{anchor.sub(/^_/, '')}"\
191
+ "</referenceFrom></locality>"
179
192
  end
180
193
 
181
194
  # gather internal bibitem references
@@ -186,7 +199,7 @@ module Metanorma
186
199
  file, = targetfile(x, read: true)
187
200
  Nokogiri::XML(file)
188
201
  .xpath(ns("//bibitem[@type = 'internal']/"\
189
- "docidentifier[@type = 'repository']")).each do |d|
202
+ "docidentifier[@type = 'repository']")).each do |d|
190
203
  a = d.text.split(%r{/}, 2)
191
204
  a.size > 1 or next
192
205
  refs[a[0]] ||= {}
@@ -211,10 +224,11 @@ module Metanorma
211
224
 
212
225
  def locate_internal_refs1(refs, identifier, filedesc)
213
226
  file, _filename = targetfile(filedesc, read: true)
214
- docxml = Nokogiri::XML(file)
227
+ xml = Nokogiri::XML(file) { |config| config.huge }
228
+ t = xml.xpath("//*/@id").each_with_object({}) { |i, x| x[i.text] = true }
215
229
  refs.each do |schema, ids|
216
- ids.each_key do |id|
217
- n = docxml.at("//*[@id = '#{id}']") and
230
+ ids.keys.select { |id| t[id] }.each do |id|
231
+ n = xml.at("//*[@id = '#{id}']") and
218
232
  n.at("./ancestor-or-self::*[@type = '#{schema}']") and
219
233
  refs[schema][id] = identifier
220
234
  end
@@ -27,8 +27,69 @@ module Metanorma
27
27
  files[identifier][:anchors] = read_anchors(xml)
28
28
  files[identifier][:bibdata] = xml.at(ns("//bibdata"))
29
29
  end
30
+ files[identifier][:bibitem] = files[identifier][:bibdata].dup
31
+ files[identifier][:bibitem].name = "bibitem"
32
+ files[identifier][:bibitem]["hidden"] = "true"
33
+ files[identifier][:bibitem]&.at("./*[local-name() = 'ext']")&.remove
30
34
  end
31
- files
35
+ add_section_split(files)
36
+ end
37
+
38
+ def add_section_split(files)
39
+ files.keys.each_with_object({}) do |k, m|
40
+ if files[k][:sectionsplit] == "true" && !files[k]["attachment"]
41
+ s, manifest = sectionsplit(files[k][:ref])
42
+ s.each_with_index do |f1, i|
43
+ add_section_split_instance(f1, m, k, i, files)
44
+ end
45
+ m["#{k}:index.html"] = add_section_split_cover(files, manifest, k)
46
+ end
47
+ m[k] = files[k]
48
+ end
49
+ end
50
+
51
+ def add_section_split_cover(files, manifest, ident)
52
+ cover = section_split_cover(manifest, ident)
53
+ files[ident][:out_path] = cover
54
+ { attachment: true, index: false, out_path: cover,
55
+ ref: File.join(File.dirname(manifest.file), cover) }
56
+ end
57
+
58
+ def section_split_cover(col, ident)
59
+ dir = File.dirname(col.file)
60
+ @compile.collection_setup(nil, dir)
61
+ CollectionRenderer.new(col, dir,
62
+ output_folder: "#{ident}_collection",
63
+ format: %i(html),
64
+ coverpage: File.join(dir, "cover.html")).coverpage
65
+ FileUtils.mv "#{ident}_collection/index.html",
66
+ File.join(dir, "#{ident}_index.html")
67
+ FileUtils.rm_rf "#{ident}_collection"
68
+ "#{ident}_index.html"
69
+ end
70
+
71
+ def add_section_split_instance(file, manifest, key, idx, files)
72
+ dir = File.dirname(files[key][:ref])
73
+ presfile = File.join(dir, File.basename(file[:url]))
74
+ manifest["#{key} #{file[:title]}"] =
75
+ { parentid: key, presentationxml: true, type: "fileref",
76
+ rel_path: file[:url], out_path: File.basename(file[:url]),
77
+ anchors: read_anchors(Nokogiri::XML(File.read(presfile))),
78
+ bibdata: files[key][:bibdata], ref: presfile }
79
+ manifest["#{key} #{file[:title]}"][:bare] = true unless idx.zero?
80
+ end
81
+
82
+ def sectionsplit(file)
83
+ @compile.compile(
84
+ file, { format: :asciidoc, extension_keys: [:presentation] }
85
+ .merge(@compile_options)
86
+ )
87
+ r = file.sub(/\.xml$/, ".presentation.xml")
88
+ xml = Nokogiri::XML(File.read(r))
89
+ s = @compile.sectionsplit(xml, File.basename(r), File.dirname(r))
90
+ .sort_by { |f| f[:order] }
91
+ [s, @compile.collection_manifest(File.basename(r), s, xml, nil,
92
+ File.dirname(r))]
32
93
  end
33
94
 
34
95
  # rel_path is the source file address, determined relative to the YAML.
@@ -43,7 +104,11 @@ module Metanorma
43
104
  else
44
105
  { type: "id", ref: ref["id"] }
45
106
  end
46
- ret[:attachment] = ref["attachment"] if ref["attachment"]
107
+ %i(attachment sectionsplit index).each do |s|
108
+ ret[s] = ref[s.to_s] if ref[s.to_s]
109
+ end
110
+ ret[:presentationxml] = ref["presentation-xml"] if ref["presentation-xml"]
111
+ ret[:bareafterfirst] = ref["bare-after-first"] if ref["bare-after-first"]
47
112
  ret
48
113
  end
49
114
 
@@ -95,22 +160,51 @@ module Metanorma
95
160
  end
96
161
 
97
162
  # compile and output individual file in collection
163
+ # warn "metanorma compile -x html #{f.path}"
98
164
  def file_compile(file, filename, identifier)
99
- # warn "metanorma compile -x html #{f.path}"
100
- Array(@directives).include?("presentation-xml") and
101
- @compile_options.merge!(passthrough_presentation_xml: true)
102
- c = Compile.new
103
- c.compile file.path, { format: :asciidoc,
104
- extension_keys: @format }.merge(@compile_options)
165
+ return if @files[identifier][:sectionsplit] == "true"
166
+
167
+ @compile.compile file.path, { format: :asciidoc, extension_keys: @format }
168
+ .merge(compile_options(identifier))
105
169
  @files[identifier][:outputs] = {}
170
+ file_compile_formats(file, filename, identifier)
171
+ end
172
+
173
+ def compile_options(identifier)
174
+ ret = @compile_options.dup
175
+ Array(@directives).include?("presentation-xml") ||
176
+ @files[identifier][:presentationxml] and
177
+ ret.merge!(passthrough_presentation_xml: true)
178
+ @files[identifier][:sectionsplit] == "true" and
179
+ ret.merge!(sectionsplit: "true")
180
+ @files[identifier][:bare] == true and
181
+ ret.merge!(bare: true)
182
+ ret
183
+ end
184
+
185
+ def file_compile_formats(file, filename, identifier)
106
186
  @format.each do |e|
107
- ext = c.processor.output_formats[e]
108
- fn = File.basename(filename).sub(/(?<=\.)[^\.]+$/, ext.to_s)
109
- FileUtils.cp file.path.sub(/\.xml$/, ".#{ext}"), File.join(@outdir, fn)
110
- @files[identifier][:outputs][e] = File.join(@outdir, fn)
187
+ ext = @compile.processor.output_formats[e]
188
+ fn = File.basename(filename).sub(/(?<=\.)[^.]+$/, ext.to_s)
189
+ if /html$/.match?(ext) && @files[identifier][:sectionsplit]
190
+ # file_sectionsplit_copy(file, fn, identifier, ext, e)
191
+ else
192
+ FileUtils.cp file.path.sub(/\.xml$/, ".#{ext}"),
193
+ File.join(@outdir, fn)
194
+ @files[identifier][:outputs][e] = File.join(@outdir, fn)
195
+ end
111
196
  end
112
197
  end
113
198
 
199
+ def file_sectionsplit_copy(file, base, identifier, ext, format)
200
+ dir = file.path.sub(/\.xml$/, ".#{ext}_collection")
201
+ files = Dir.glob("#{dir}/*.#{ext}")
202
+ FileUtils.cp files, @outdir
203
+ cover = File.join(@outdir, base.sub(/\.html$/, ".index.html"))
204
+ FileUtils.cp File.join(dir, "index.html"), cover
205
+ @files[identifier][:outputs][format] = cover
206
+ end
207
+
114
208
  def copy_file_to_dest(fileref)
115
209
  dest = File.join(@outdir, fileref[:out_path])
116
210
  FileUtils.mkdir_p(File.dirname(dest))
@@ -120,6 +214,7 @@ module Metanorma
120
214
  # process each file in the collection
121
215
  # files are held in memory, and altered as postprocessing
122
216
  def files # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
217
+ warn "\n\n\n\n\nInternal Refs: #{DateTime.now.strftime('%H:%M:%S')}"
123
218
  internal_refs = locate_internal_refs
124
219
  @files.each_with_index do |(identifier, x), i|
125
220
  i.positive? && Array(@directives).include?("bare-after-first") and
@@ -127,6 +222,7 @@ module Metanorma
127
222
  if x[:attachment] then copy_file_to_dest(x)
128
223
  else
129
224
  file, filename = targetfile(x, read: true)
225
+ warn "\n\n\n\n\nProcess #{filename}: #{DateTime.now.strftime('%H:%M:%S')}"
130
226
  file = update_xrefs(file, identifier, internal_refs)
131
227
  Tempfile.open(["collection", ".xml"], encoding: "utf-8") do |f|
132
228
  f.write(file)
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "util"
3
4
 
4
5
  module Metanorma
@@ -46,8 +47,10 @@ module Metanorma
46
47
  def parse_docref(mnf)
47
48
  mnf.xpath("xmlns:docref").map do |dr|
48
49
  h = { "identifier" => dr.at("identifier").children.to_xml }
49
- dr[:fileref] and h["fileref"] = dr[:fileref]
50
- h["attachment"] = dr[:attachment] if dr[:attachment]
50
+ %i(fileref attachment sectionsplit index).each do |s|
51
+ h[s.to_s] = dr[s] if dr[s]
52
+ end
53
+ h["presentation-xml"] = dr[:presentationxml] if dr[:presentationxml]
51
54
  h
52
55
  end
53
56
  end
@@ -59,7 +62,7 @@ module Metanorma
59
62
  @manifest.each { |mnf| mnf.collection = col }
60
63
  end
61
64
 
62
- # @param dir [String] path to coolection
65
+ # @param dir [String] path to collection
63
66
  # @return [Hash<String, Metanorma::Document>]
64
67
  def documents(dir = "")
65
68
  docs = @docref.each_with_object({}) do |dr, m|
@@ -67,7 +70,7 @@ module Metanorma
67
70
 
68
71
  m[dr["identifier"]] = Document.parse_file(
69
72
  File.join(dir, dr["fileref"]),
70
- dr["attachment"], dr["identifier"]
73
+ dr["attachment"], dr["identifier"], dr["index"]
71
74
  )
72
75
  m
73
76
  end
@@ -115,7 +118,16 @@ module Metanorma
115
118
 
116
119
  def docref_to_xml_attrs(elem, docref)
117
120
  elem[:fileref] = @disambig.source2dest_filename(docref["fileref"])
118
- elem[:attachment] = docref["attachment"] if docref["attachment"]
121
+ %i(attachment sectionsplit).each do |i|
122
+ elem[i] = docref[i.to_s] if docref[i.to_s]
123
+ end
124
+ elem[:index] = docref.has_key?("index") ? docref["index"] : "true"
125
+ elem[:presentationxml] = "true" if docref["presentation-xml"] &&
126
+ [true, "true"].include?(docref["presentation-xml"])
127
+ docref_to_xml_attrs_id(elem, docref)
128
+ end
129
+
130
+ def docref_to_xml_attrs_id(elem, docref)
119
131
  if collection.directives.include?("documents-inline")
120
132
  id = collection.documents.find_index do |k, _|
121
133
  k == docref["identifier"]