metanorma 1.3.5 → 1.3.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -35,12 +35,14 @@ module Metanorma
35
35
 
36
36
  @outdir = options[:output_folder]
37
37
  @coverpage = options[:coverpage]
38
- @format = options[:format]
38
+ @format = Util.sort_extensions_execution(options[:format])
39
39
  @compile_options = options[:compile] || {}
40
+ @compile_options[:no_install_fonts] = true if options[:no_install_fonts]
40
41
  @log = options[:log]
41
42
  @documents = collection.documents
42
43
  @directives = collection.directives
43
44
  @disambig = Util::DisambigFiles.new
45
+ @compile = Compile.new
44
46
 
45
47
  # list of files in the collection
46
48
  @files = read_files folder
@@ -52,14 +54,20 @@ module Metanorma
52
54
  # @param col [Metanorma::Collection] XML collection
53
55
  # @param options [Hash]
54
56
  # @option options [String] :coverpage cover page HTML (Liquid template)
55
- # @option options [Array<Synbol>] :format list of formats
57
+ # @option options [Array<Symbol>] :format list of formats
56
58
  # @option options [Strong] :ourput_folder output directory
57
59
  def self.render(col, options = {})
58
60
  folder = File.dirname col.file
61
+ # require "byebug"; byebug
62
+ warn "\n\n\n\n\nRender Init: #{DateTime.now.strftime('%H:%M:%S')}"
59
63
  cr = new(col, folder, options)
64
+ warn "\n\n\n\n\nRender Files: #{DateTime.now.strftime('%H:%M:%S')}"
60
65
  cr.files
66
+ warn "\n\n\n\n\nConcatenate: #{DateTime.now.strftime('%H:%M:%S')}"
61
67
  cr.concatenate(col, options)
68
+ warn "\n\n\n\n\nCoverpage: #{DateTime.now.strftime('%H:%M:%S')}"
62
69
  cr.coverpage if options[:format]&.include?(:html)
70
+ warn "\n\n\n\n\nDone: #{DateTime.now.strftime('%H:%M:%S')}"
63
71
  end
64
72
 
65
73
  def concatenate(col, options)
@@ -70,13 +78,15 @@ module Metanorma
70
78
  ext = e == :presentation ? "presentation.xml" : e.to_s
71
79
  out = col.clone
72
80
  out.directives << "documents-inline"
73
- out.documents.keys.each do |id|
74
- next if @files[id][:attachment]
81
+ out.documents.each_key do |id|
82
+ next if @files[id][:attachment] || @files[id][:outputs].nil?
75
83
 
76
84
  filename = @files[id][:outputs][e]
77
85
  out.documents[id] = Metanorma::Document.raw_file(filename)
78
86
  end
79
- File.open(File.join(@outdir, "collection.#{ext}"), "w:UTF-8") { |f| f.write(out.to_xml) }
87
+ File.open(File.join(@outdir, "collection.#{ext}"), "w:UTF-8") do |f|
88
+ f.write(out.to_xml)
89
+ end
80
90
  end
81
91
  options[:format].include?(:pdf) and
82
92
  pdfconv.convert(File.join(@outdir, "collection.presentation.xml"))
@@ -169,7 +179,7 @@ module Metanorma
169
179
  # @param elm [Nokogiri::XML::Element]
170
180
  # @param builder [Nokogiri::XML::Builder]
171
181
  def indexfile_docref(elm, builder)
172
- return "" unless elm.at(ns("./docref"))
182
+ return "" unless elm.at(ns("./docref[@index = 'true']"))
173
183
 
174
184
  builder.ul { |b| docrefs(elm, b) }
175
185
  end
@@ -177,7 +187,7 @@ module Metanorma
177
187
  # @param elm [Nokogiri::XML::Element]
178
188
  # @param builder [Nokogiri::XML::Builder]
179
189
  def docrefs(elm, builder)
180
- elm.xpath(ns("./docref")).each do |d|
190
+ elm.xpath(ns("./docref[@index = 'true']")).each do |d|
181
191
  ident = d.at(ns("./identifier")).children.to_xml
182
192
  builder.li do |li|
183
193
  li.a **{ href: index_link(d, ident) } do |a|
@@ -216,6 +226,14 @@ module Metanorma
216
226
 
217
227
  private
218
228
 
229
+ def format_sort(formats)
230
+ ret = []
231
+ formats.include?(:xml) and ret << :xml
232
+ formats.include?(:presentation) and ret << :presentation
233
+ a = %i(presentation xml)
234
+ ret + formats.reject { |i| a.include? i }
235
+ end
236
+
219
237
  # @param options [Hash]
220
238
  # @raise [ArgumentError]
221
239
  def check_options(options)
@@ -6,6 +6,8 @@ require "fontist"
6
6
  require "fontist/manifest/install"
7
7
  require_relative "compile_validate"
8
8
  require_relative "fontist_utils"
9
+ require_relative "util"
10
+ require_relative "sectionsplit"
9
11
 
10
12
  module Metanorma
11
13
  class Compile
@@ -15,6 +17,8 @@ module Metanorma
15
17
  def initialize
16
18
  @registry = Metanorma::Registry.instance
17
19
  @errors = []
20
+ @isodoc = IsoDoc::Convert.new({})
21
+ @fontist_installed = false
18
22
  end
19
23
 
20
24
  def compile(filename, options = {})
@@ -26,7 +30,8 @@ module Metanorma
26
30
  (file, isodoc = process_input(filename, options)) or return nil
27
31
  relaton_export(isodoc, options)
28
32
  extract(isodoc, options[:extract], options[:extract_type])
29
- FontistUtils.install_fonts(@processor, options)
33
+ FontistUtils.install_fonts(@processor, options) unless @fontist_installed
34
+ @fontist_installed = true
30
35
  process_extensions(extensions, file, isodoc, options)
31
36
  end
32
37
 
@@ -35,7 +40,7 @@ module Metanorma
35
40
  end
36
41
 
37
42
  def xml_options_extract(file)
38
- xml = Nokogiri::XML(file)
43
+ xml = Nokogiri::XML(file) { |config| config.huge }
39
44
  if xml.root
40
45
  @registry.root_tags.each do |k, v|
41
46
  return { type: k } if v == xml.root.name
@@ -47,13 +52,13 @@ module Metanorma
47
52
  def options_extract(filename, options)
48
53
  content = read_file(filename)
49
54
  o = Metanorma::Input::Asciidoc.new.extract_metanorma_options(content)
50
- o = o.merge(xml_options_extract(content))
55
+ .merge(xml_options_extract(content))
51
56
  options[:type] ||= o[:type]&.to_sym
52
57
  t = @registry.alias(options[:type]) and options[:type] = t
53
58
  dir = filename.sub(%r(/[^/]+$), "/")
54
59
  options[:relaton] ||= "#{dir}/#{o[:relaton]}" if o[:relaton]
55
60
  options[:sourcecode] ||= "#{dir}/#{o[:sourcecode]}" if o[:sourcecode]
56
- options[:extension_keys] ||= o[:extensions]&.split(/,[ ]*/)&.map(&:to_sym)
61
+ options[:extension_keys] ||= o[:extensions]&.split(/, */)&.map(&:to_sym)
57
62
  options[:extension_keys] = nil if options[:extension_keys] == [:all]
58
63
  options[:format] ||= :asciidoc
59
64
  options[:filename] = filename
@@ -61,12 +66,10 @@ module Metanorma
61
66
  end
62
67
 
63
68
  def get_extensions(options)
64
- options[:extension_keys] ||= @processor.output_formats.reduce([]) do |memo, (k, _)|
65
- memo << k
66
- end
69
+ options[:extension_keys] ||=
70
+ @processor.output_formats.reduce([]) { |memo, (k, _)| memo << k }
67
71
  extensions = options[:extension_keys].reduce([]) do |memo, e|
68
- if @processor.output_formats[e]
69
- memo << e
72
+ if @processor.output_formats[e] then memo << e
70
73
  else
71
74
  message = "[metanorma] Error: #{e} format is not supported for this standard."
72
75
  @errors << message
@@ -84,26 +87,33 @@ module Metanorma
84
87
 
85
88
  def process_input(filename, options)
86
89
  case extname = File.extname(filename)
87
- when ".adoc"
88
- Util.log("[metanorma] Processing: AsciiDoc input.", :info)
89
- file = read_file(filename)
90
- options[:asciimath] and
91
- file.sub!(/^(=[^\n]+\n)/, "\\1:mn-keep-asciimath:\n")
92
- dir = File.dirname(filename)
93
- dir != "." and
94
- file.gsub!(/^include::/, "include::#{dir}/")
95
- [file, @processor.input_to_isodoc(file, filename, options)]
96
- when ".xml"
97
- Util.log("[metanorma] Processing: Metanorma XML input.", :info)
98
- # TODO NN: this is a hack -- we should provide/bridge the
99
- # document attributes in Metanorma XML
100
- ["", read_file(filename)]
90
+ when ".adoc" then process_input_adoc(filename, options)
91
+ when ".xml" then process_input_xml(filename, options)
101
92
  else
102
- Util.log("[metanorma] Error: file extension #{extname} is not supported.", :error)
93
+ Util.log("[metanorma] Error: file extension #{extname} "\
94
+ "is not supported.", :error)
103
95
  nil
104
96
  end
105
97
  end
106
98
 
99
+ def process_input_adoc(filename, options)
100
+ Util.log("[metanorma] Processing: AsciiDoc input.", :info)
101
+ file = read_file(filename)
102
+ options[:asciimath] and
103
+ file.sub!(/^(=[^\n]+\n)/, "\\1:mn-keep-asciimath:\n")
104
+ dir = File.dirname(filename)
105
+ dir != "." and
106
+ file.gsub!(/^include::/, "include::#{dir}/")
107
+ [file, @processor.input_to_isodoc(file, filename, options)]
108
+ end
109
+
110
+ def process_input_xml(filename, _options)
111
+ Util.log("[metanorma] Processing: Metanorma XML input.", :info)
112
+ # TODO NN: this is a hack -- we should provide/bridge the
113
+ # document attributes in Metanorma XML
114
+ ["", read_file(filename)]
115
+ end
116
+
107
117
  def read_file(filename)
108
118
  File.read(filename, encoding: "utf-8").gsub("\r\n", "\n")
109
119
  end
@@ -111,7 +121,7 @@ module Metanorma
111
121
  def relaton_export(isodoc, options)
112
122
  return unless options[:relaton]
113
123
 
114
- xml = Nokogiri::XML(isodoc)
124
+ xml = Nokogiri::XML(isodoc) { |config| config.huge }
115
125
  bibdata = xml.at("//bibdata") || xml.at("//xmlns:bibdata")
116
126
  # docid = bibdata&.at("./xmlns:docidentifier")&.text || options[:filename]
117
127
  # outname = docid.sub(/^\s+/, "").sub(/\s+$/, "").gsub(/\s+/, "-") + ".xml"
@@ -120,9 +130,7 @@ module Metanorma
120
130
 
121
131
  def clean_sourcecode(xml)
122
132
  xml.xpath(".//callout | .//annotation | .//xmlns:callout | "\
123
- ".//xmlns:annotation").each do |x|
124
- x.remove
125
- end
133
+ ".//xmlns:annotation").each(&:remove)
126
134
  xml.xpath(".//br | .//xmlns:br").each { |x| x.replace("\n") }
127
135
  HTMLEntities.new.decode(xml.children.to_xml)
128
136
  end
@@ -131,11 +139,11 @@ module Metanorma
131
139
  return unless dirname
132
140
 
133
141
  if extract_types.nil? || extract_types.empty?
134
- extract_types = [:sourcecode, :image, :requirement]
142
+ extract_types = %i[sourcecode image requirement]
135
143
  end
136
144
  FileUtils.rm_rf dirname
137
145
  FileUtils.mkdir_p dirname
138
- xml = Nokogiri::XML(isodoc)
146
+ xml = Nokogiri::XML(isodoc) { |config| config.huge }
139
147
  sourcecode_export(xml, dirname) if extract_types.include? :sourcecode
140
148
  image_export(xml, dirname) if extract_types.include? :image
141
149
  requirement_export(xml, dirname) if extract_types.include? :requirement
@@ -156,7 +164,7 @@ module Metanorma
156
164
  xml.at("//image | //xmlns:image") or return
157
165
  FileUtils.mkdir_p "#{dirname}/image"
158
166
  xml.xpath("//image | //xmlns:image").each_with_index do |s, i|
159
- next unless /^data:image/.match s["src"]
167
+ next unless /^data:image/.match? s["src"]
160
168
 
161
169
  %r{^data:image/(?<imgtype>[^;]+);base64,(?<imgdata>.+)$} =~ s["src"]
162
170
  filename = s["filename"] || sprintf("image-%04d.%s", i, imgtype)
@@ -167,8 +175,8 @@ module Metanorma
167
175
  end
168
176
 
169
177
  REQUIREMENT_XPATH = "//requirement | //xmlns:requirement | "\
170
- "//recommendation | //xmlns:recommendation | //permission | "\
171
- "//xmlns:permission".freeze
178
+ "//recommendation | //xmlns:recommendation | //permission | "\
179
+ "//xmlns:permission".freeze
172
180
 
173
181
  def requirement_export(xml, dirname)
174
182
  xml.at(REQUIREMENT_XPATH) or return
@@ -181,17 +189,6 @@ module Metanorma
181
189
  end
182
190
  end
183
191
 
184
- # dependency ordering
185
- def sort_extensions_execution(ext)
186
- case ext
187
- when :xml then 0
188
- when :rxl then 1
189
- when :presentation then 2
190
- else
191
- 99
192
- end
193
- end
194
-
195
192
  def wrap_html(options, file_extension, outfilename)
196
193
  if options[:wrapper] && /html$/.match(file_extension)
197
194
  outfilename = outfilename.sub(/\.html$/, "")
@@ -206,37 +203,27 @@ module Metanorma
206
203
  f = change_output_dir options
207
204
  xml_name = f.sub(/\.[^.]+$/, ".xml")
208
205
  presentationxml_name = f.sub(/\.[^.]+$/, ".presentation.xml")
209
- extensions.sort do |a, b|
210
- sort_extensions_execution(a) <=> sort_extensions_execution(b)
211
- end.each do |ext|
212
- isodoc_options = @processor.extract_options(file)
213
- isodoc_options[:datauriimage] = true if options[:datauriimage]
214
- isodoc_options[:sourcefilename] = options[:filename]
215
- isodoc_options[:bare] = options[:bare]
206
+ Util.sort_extensions_execution(extensions).each do |ext|
216
207
  file_extension = @processor.output_formats[ext]
217
208
  outfilename = f.sub(/\.[^.]+$/, ".#{file_extension}")
218
- if ext == :pdf
219
- font_locations = FontistUtils.fontist_font_locations(@processor, options)
220
- font_locations and
221
- isodoc_options[:mn2pdf] = { font_manifest_file: font_locations.path }
222
- end
209
+ isodoc_options = get_isodoc_options(file, options, ext)
223
210
  if ext == :rxl
224
- options[:relaton] = outfilename
225
- relaton_export(isodoc, options)
211
+ relaton_export(isodoc, options.merge(relaton: outfilename))
226
212
  elsif options[:passthrough_presentation_xml] && ext == :presentation
227
213
  FileUtils.cp f, presentationxml_name
214
+ elsif ext == :html && options[:sectionsplit]
215
+ sectionsplit_convert(xml_name, isodoc, outfilename, isodoc_options)
228
216
  else
229
217
  begin
230
- @processor.use_presentation_xml(ext) ?
231
- @processor.output(nil, presentationxml_name, outfilename, ext, isodoc_options) :
232
- @processor.output(isodoc, xml_name, outfilename, ext, isodoc_options)
233
- rescue StandardError => e
234
- if e.message.include? "Fatal:"
235
- @errors << e.message
218
+ if @processor.use_presentation_xml(ext)
219
+ @processor.output(nil, presentationxml_name, outfilename, ext,
220
+ isodoc_options)
236
221
  else
237
- puts e.message
238
- puts e.backtrace.join("\n")
222
+ @processor.output(isodoc, xml_name, outfilename, ext,
223
+ isodoc_options)
239
224
  end
225
+ rescue StandardError => e
226
+ isodoc_error_process(e)
240
227
  end
241
228
  end
242
229
  wrap_html(options, file_extension, outfilename)
@@ -245,6 +232,29 @@ module Metanorma
245
232
 
246
233
  private
247
234
 
235
+ def isodoc_error_process(err)
236
+ if err.message.include? "Fatal:"
237
+ @errors << err.message
238
+ else
239
+ puts err.message
240
+ puts err.backtrace.join("\n")
241
+ end
242
+ end
243
+
244
+ def get_isodoc_options(file, options, ext)
245
+ isodoc_options = @processor.extract_options(file)
246
+ isodoc_options[:datauriimage] = true if options[:datauriimage]
247
+ isodoc_options[:sourcefilename] = options[:filename]
248
+ %i(bare sectionsplit no_install_fonts).each do |x|
249
+ isodoc_options[x] ||= options[x]
250
+ end
251
+ if ext == :pdf
252
+ floc = FontistUtils.fontist_font_locations(@processor, options) and
253
+ isodoc_options[:mn2pdf] = { font_manifest_file: floc.path }
254
+ end
255
+ isodoc_options
256
+ end
257
+
248
258
  # @param options [Hash]
249
259
  # @return [String]
250
260
  def change_output_dir(options)
@@ -1,13 +1,15 @@
1
1
  module Metanorma
2
2
  class Document
3
3
  # @return [Strin]
4
- attr_reader :file, :attachment, :bibitem
4
+ attr_reader :file, :attachment, :bibitem, :index
5
5
 
6
6
  # @param bibitem [RelatonBib::BibliographicItem]
7
7
  def initialize(bibitem, file, options = {})
8
8
  @bibitem = bibitem
9
9
  @file = file
10
10
  @attachment = options[:attachment]
11
+ @index = options[:index]
12
+ @index = true if @index.nil?
11
13
  @raw = options[:raw]
12
14
  end
13
15
 
@@ -16,10 +18,11 @@ module Metanorma
16
18
  # @param attachment [Bool] is an attachment
17
19
  # @param identifier [String] is the identifier assigned the file
18
20
  # in the collection file
21
+ # @param index [Bool] is indication on whether to index this file in coverpage
19
22
  # @return [Metanorma::Document]
20
- def parse_file(file, attachment, identifier = nil)
23
+ def parse_file(file, attachment, identifier = nil, index = true)
21
24
  new(bibitem(file, attachment, identifier), file,
22
- { attachment: attachment })
25
+ { attachment: attachment, index: index })
23
26
  end
24
27
 
25
28
  # #param xml [Nokogiri::XML::Document, Nokogiri::XML::Element]
@@ -30,7 +33,9 @@ module Metanorma
30
33
 
31
34
  # raw XML file, can be used to put in entire file instead of just bibitem
32
35
  def raw_file(filename)
33
- doc = Nokogiri::XML(File.read(filename, encoding: "UTF-8"))
36
+ doc = Nokogiri::XML(File.read(filename, encoding: "UTF-8")) do |config|
37
+ config.huge
38
+ end
34
39
  new(doc, filename, raw: true)
35
40
  end
36
41
 
@@ -100,7 +105,9 @@ module Metanorma
100
105
  if @raw
101
106
  builder << @bibitem.root.to_xml
102
107
  else
103
- builder.send(type + "-standard") { |b| b << @bibitem.to_xml(bibdata: true) }
108
+ builder.send("#{type}-standard") do |b|
109
+ b << @bibitem.to_xml(bibdata: true)
110
+ end
104
111
  end
105
112
  end
106
113
  end
@@ -57,9 +57,9 @@ module Metanorma
57
57
  end
58
58
 
59
59
  ADOC_OPTIONS = %w(htmlstylesheet htmlcoverpage htmlintropage scripts
60
- scripts-override scripts-pdf wordstylesheet bare i18nyaml
60
+ scripts-override scripts-pdf wordstylesheet i18nyaml
61
61
  standardstylesheet header wordcoverpage wordintropage
62
- ulstyle olstyle htmlstylesheet-override
62
+ ulstyle olstyle htmlstylesheet-override bare
63
63
  htmltoclevels doctoclevels sectionsplit
64
64
  body-font header-font monospace-font title-font
65
65
  wordstylesheet-override).freeze
@@ -3,10 +3,7 @@
3
3
 
4
4
  module Metanorma
5
5
  class Processor
6
-
7
- attr_reader :short
8
- attr_reader :input_format
9
- attr_reader :asciidoctor_backend
6
+ attr_reader :short, :input_format, :asciidoctor_backend
10
7
 
11
8
  def initialize
12
9
  raise "This is an abstract class!"
@@ -16,7 +13,7 @@ module Metanorma
16
13
  {
17
14
  xml: "xml",
18
15
  presentation: "presentation.xml",
19
- rxl: "rxl"
16
+ rxl: "rxl",
20
17
  }
21
18
  end
22
19
 
@@ -36,7 +33,7 @@ module Metanorma
36
33
  end
37
34
  end
38
35
 
39
- def output(isodoc_node, inname, outname, format, options={})
36
+ def output(isodoc_node, _inname, outname, _format, _options = {})
40
37
  File.open(outname, "w:UTF-8") { |f| f.write(isodoc_node) }
41
38
  end
42
39
 
@@ -0,0 +1,254 @@
1
+ require "yaml"
2
+
3
+ module Metanorma
4
+ class Compile
5
+ # assume we pass in Presentation XML, but we want to recover Semantic XML
6
+ def sectionsplit_convert(input_filename, file, output_filename = nil,
7
+ opts = {})
8
+ @isodoc = IsoDoc::Convert.new({})
9
+ input_filename += ".xml" unless input_filename.match?(/\.xml$/)
10
+ File.exist?(input_filename) or
11
+ File.open(input_filename, "w:UTF-8") { |f| f.write(file) }
12
+ presxml = File.read(input_filename, encoding: "utf-8")
13
+ @openmathdelim, @closemathdelim = @isodoc.extract_delims(presxml)
14
+ xml, filename, dir = @isodoc.convert_init(presxml, input_filename, false)
15
+ build_collection(xml, presxml, output_filename || filename, dir, opts)
16
+ end
17
+
18
+ def ns(xpath)
19
+ @isodoc.ns(xpath)
20
+ end
21
+
22
+ def build_collection(xml, presxml, filename, dir, opts = {})
23
+ base = File.basename(filename)
24
+ collection_setup(base, dir)
25
+ files = sectionsplit(xml, base, dir)
26
+ collection_manifest(base, files, xml, presxml, dir).render(
27
+ { format: %i(html), output_folder: "#{filename}_collection",
28
+ coverpage: File.join(dir, "cover.html") }.merge(opts),
29
+ )
30
+ end
31
+
32
+ def collection_manifest(filename, files, origxml, _presxml, dir)
33
+ File.open(File.join(dir, "#{filename}.html.yaml"), "w:UTF-8") do |f|
34
+ f.write(collectionyaml(files, origxml))
35
+ end
36
+ Metanorma::Collection.parse File.join(dir, "#{filename}.html.yaml")
37
+ end
38
+
39
+ def collection_setup(filename, dir)
40
+ FileUtils.mkdir_p "#{filename}_collection" if filename
41
+ FileUtils.mkdir_p dir
42
+ File.open(File.join(dir, "cover.html"), "w:UTF-8") do |f|
43
+ f.write(coll_cover)
44
+ end
45
+ end
46
+
47
+ def coll_cover
48
+ <<~COVER
49
+ <html><head/>
50
+ <body>
51
+ <h1>{{ doctitle }}</h1>
52
+ <h2>{{ docnumber }}</h2>
53
+ <nav>{{ labels["navigation"] }}</nav>
54
+ </body>
55
+ </html>
56
+ COVER
57
+ end
58
+
59
+ SPLITSECTIONS =
60
+ [["//preface/*", "preface"], ["//sections/*", "sections"],
61
+ ["//annex", nil],
62
+ ["//bibliography/*[not(@hidden = 'true')]", "bibliography"],
63
+ ["//indexsect", nil]].freeze
64
+
65
+ def sectionsplit(xml, filename, dir)
66
+ @key = xref_preprocess(xml)
67
+ @splitdir = dir
68
+ out = emptydoc(xml)
69
+ SPLITSECTIONS.each_with_object([]) do |n, ret|
70
+ xml.xpath(ns(n[0])).each do |s|
71
+ ret << sectionfile(xml, out, "#{filename}.#{ret.size}", s, n[1])
72
+ end
73
+ end
74
+ end
75
+
76
+ def emptydoc(xml)
77
+ out = xml.dup
78
+ out.xpath(
79
+ ns("//preface | //sections | //annex | //bibliography/clause | "\
80
+ "//bibliography/references[not(@hidden = 'true')] | //indexsect"),
81
+ ).each(&:remove)
82
+ out
83
+ end
84
+
85
+ def sectionfile(fulldoc, xml, file, chunk, parentnode)
86
+ fname = create_sectionfile(fulldoc, xml.dup, file, chunk, parentnode)
87
+ { order: chunk["displayorder"].to_i, url: fname,
88
+ title: titlerender(chunk) }
89
+ end
90
+
91
+ def create_sectionfile(xml, out, file, chunk, parentnode)
92
+ ins = out.at(ns("//misccontainer")) || out.at(ns("//bibdata"))
93
+ if parentnode
94
+ ins.next = "<#{parentnode}/>"
95
+ ins.next.add_child(chunk.dup)
96
+ else ins.next = chunk.dup
97
+ end
98
+ xref_process(out, xml, @key)
99
+ outname = "#{file}.xml"
100
+ File.open(File.join(@splitdir, outname), "w:UTF-8") { |f| f.write(out) }
101
+ outname
102
+ end
103
+
104
+ def xref_preprocess(xml)
105
+ svg_preprocess(xml)
106
+ key = (0...8).map { rand(65..90).chr }.join # random string
107
+ xml.root["type"] = key # to force recognition of internal refs
108
+ key
109
+ end
110
+
111
+ def xref_process(section, xml, key)
112
+ refs = eref_to_internal_eref(section, xml, key)
113
+ refs += xref_to_internal_eref(section, key)
114
+ ins = new_hidden_ref(section)
115
+ copied_refs = copy_repo_items_biblio(ins, section, xml)
116
+ insert_indirect_biblio(ins, refs - copied_refs, key)
117
+ end
118
+
119
+ def svg_preprocess(xml)
120
+ xml.xpath("//m:svg", "m" => "http://www.w3.org/2000/svg").each do |s|
121
+ m = svgmap_wrap(s)
122
+ s.xpath(".//m:a", "m" => "http://www.w3.org/2000/svg").each do |a|
123
+ next unless /^#/.match? a["href"]
124
+
125
+ a["href"] = a["href"].sub(/^#/, "")
126
+ m << "<target href='#{a['href']}'>"\
127
+ "<xref target='#{a['href']}'/></target>"
128
+ end
129
+ end
130
+ end
131
+
132
+ def svgmap_wrap(svg)
133
+ ret = svg.at("./ancestor::xmlns:svgmap") and return ret
134
+ ret = svg.at("./ancestor::xmlns:figure")
135
+ ret.wrap("<svgmap/>")
136
+ svg.at("./ancestor::xmlns:svgmap")
137
+ end
138
+
139
+ def make_anchor(anchor)
140
+ "<localityStack><locality type='anchor'><referenceFrom>"\
141
+ "#{anchor}</referenceFrom></locality></localityStack>"
142
+ end
143
+
144
+ def xref_to_internal_eref(xml, key)
145
+ xml.xpath(ns("//xref")).each_with_object({}) do |x, m|
146
+ x["bibitemid"] = "#{key}_#{x['target']}"
147
+ x << make_anchor(x["target"])
148
+ m[x["bibitemid"]] = true
149
+ x.delete("target")
150
+ x["type"] = key
151
+ x.name = "eref"
152
+ end.keys
153
+ end
154
+
155
+ def eref_to_internal_eref(section, xml, key)
156
+ eref_to_internal_eref_select(section, xml).each_with_object([]) do |x, m|
157
+ url = xml.at(ns("//bibitem[@id = '#{x}']/url[@type = 'citation']"))
158
+ section.xpath(("//*[@bibitemid = '#{x}']")).each do |e|
159
+ id = eref_to_internal_eref1(e, key, url)
160
+ id and m << id
161
+ end
162
+ end
163
+ end
164
+
165
+ def eref_to_internal_eref1(elem, key, url)
166
+ if url
167
+ elem.name = "link"
168
+ elem["target"] = url
169
+ nil
170
+ else
171
+ elem["bibitemid"] = "#{key}_#{elem['bibitemid']}"
172
+ elem << make_anchor(elem["bibitemid"])
173
+ elem["type"] = key
174
+ elem["bibitemid"]
175
+ end
176
+ end
177
+
178
+ def eref_to_internal_eref_select(section, xml)
179
+ refs = section.xpath(("//*/@bibitemid")).map { |x| x.text } # rubocop:disable Style/SymbolProc
180
+ refs.uniq.reject do |x|
181
+ xml.at(ns("//bibitem[@id = '#{x}'][@type = 'internal']")) ||
182
+ xml.at(ns("//bibitem[@id = '#{x}']"\
183
+ "[docidentifier/@type = 'repository']"))
184
+ end
185
+ end
186
+
187
+ # from standoc
188
+ def new_hidden_ref(xmldoc)
189
+ ins = xmldoc.at("bibliography") or
190
+ xmldoc.root << "<bibliography/>" and ins = xmldoc.at("bibliography")
191
+ ins.add_child("<references hidden='true' normative='false'/>").first
192
+ end
193
+
194
+ def copy_repo_items_biblio(ins, section, xml)
195
+ xml.xpath(ns("//references/bibitem[docidentifier/@type = 'repository']"))
196
+ .each_with_object([]) do |b, m|
197
+ section.at("//*[@bibitemid = '#{b['id']}']") or next
198
+ ins << b.dup
199
+ m << b["id"]
200
+ end
201
+ end
202
+
203
+ def insert_indirect_biblio(ins, refs, prefix)
204
+ refs.each do |x|
205
+ ins << <<~BIBENTRY
206
+ <bibitem id="#{x}" type="internal">
207
+ <docidentifier type="repository">#{x.sub(/^#{prefix}_/, "#{prefix}/")}</docidentifier>
208
+ </bibitem>
209
+ BIBENTRY
210
+ end
211
+ end
212
+
213
+ def recursive_string_keys(hash)
214
+ case hash
215
+ when Hash then hash.map { |k, v| [k.to_s, recursive_string_keys(v)] }.to_h
216
+ when Enumerable then hash.map { |v| recursive_string_keys(v) }
217
+ else
218
+ hash
219
+ end
220
+ end
221
+
222
+ def titlerender(section)
223
+ title = section.at(ns("./title")) or return "[Untitled]"
224
+ t = title.dup
225
+ t.xpath(ns(".//tab | .//br")).each { |x| x.replace(" ") }
226
+ t.xpath(ns(".//strong")).each { |x| x.replace(x.children) }
227
+ t.children.to_xml
228
+ end
229
+
230
+ def collectionyaml(files, xml)
231
+ ret = {
232
+ directives: ["presentation-xml", "bare-after-first"],
233
+ bibdata: {
234
+ title: {
235
+ type: "title-main", language: @lang,
236
+ content: xml.at(ns("//bibdata/title")).text
237
+ },
238
+ type: "collection",
239
+ docid: {
240
+ type: xml.at(ns("//bibdata/docidentifier/@type")).text,
241
+ id: xml.at(ns("//bibdata/docidentifier")).text,
242
+ },
243
+ },
244
+ manifest: {
245
+ level: "collection", title: "Collection",
246
+ docref: files.sort_by { |f| f[:order] }.each.map do |f|
247
+ { fileref: f[:url], identifier: f[:title] }
248
+ end
249
+ },
250
+ }
251
+ recursive_string_keys(ret).to_yaml
252
+ end
253
+ end
254
+ end