metanorma 1.3.4 → 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "util"
4
+
3
5
  module Metanorma
4
6
  # Metanorma collection's manifest
5
7
  class CollectionManifest
@@ -15,6 +17,7 @@ module Metanorma
15
17
  @title = title
16
18
  @docref = docref
17
19
  @manifest = manifest
20
+ @disambig = Util::DisambigFiles.new
18
21
  end
19
22
 
20
23
  class << self
@@ -43,9 +46,11 @@ module Metanorma
43
46
  # @return [Hash{String=>String}]
44
47
  def parse_docref(mnf)
45
48
  mnf.xpath("xmlns:docref").map do |dr|
46
- h = { "identifier" => dr.at("identifier").text }
47
- dr[:fileref] and h["fileref"] = dr[:fileref]
48
- h["attachment"] = dr[:attachment] if dr[:attachment]
49
+ h = { "identifier" => dr.at("identifier").children.to_xml }
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]
49
54
  h
50
55
  end
51
56
  end
@@ -57,7 +62,7 @@ module Metanorma
57
62
  @manifest.each { |mnf| mnf.collection = col }
58
63
  end
59
64
 
60
- # @param dir [String] path to coolection
65
+ # @param dir [String] path to collection
61
66
  # @return [Hash<String, Metanorma::Document>]
62
67
  def documents(dir = "")
63
68
  docs = @docref.each_with_object({}) do |dr, m|
@@ -65,7 +70,7 @@ module Metanorma
65
70
 
66
71
  m[dr["identifier"]] = Document.parse_file(
67
72
  File.join(dir, dr["fileref"]),
68
- dr["attachment"], dr["identifier"]
73
+ dr["attachment"], dr["identifier"], dr["index"]
69
74
  )
70
75
  m
71
76
  end
@@ -100,14 +105,34 @@ module Metanorma
100
105
 
101
106
  # @param builder [Nokogiri::XML::Builder]
102
107
  def docref_to_xml(builder)
108
+ @disambig = Util::DisambigFiles.new
103
109
  @docref.each do |dr|
104
- drf = builder.docref { |b| b.identifier dr["identifier"] }
105
- drf[:fileref] = Util::source2dest_filename(dr["fileref"])
106
- drf[:attachment] = dr["attachment"] if dr["attachment"]
107
- if collection.directives.include?("documents-inline")
108
- id = collection.documents.find_index { |k, _| k == dr["identifier"] }
109
- drf[:id] = format("doc%<index>09d", index: id)
110
+ drf = builder.docref do |b|
111
+ b.identifier do |i|
112
+ i << dr["identifier"]
113
+ end
114
+ end
115
+ docref_to_xml_attrs(drf, dr)
116
+ end
117
+ end
118
+
119
+ def docref_to_xml_attrs(elem, docref)
120
+ elem[:fileref] = @disambig.source2dest_filename(docref["fileref"])
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)
131
+ if collection.directives.include?("documents-inline")
132
+ id = collection.documents.find_index do |k, _|
133
+ k == docref["identifier"]
110
134
  end
135
+ elem[:id] = format("doc%<index>09d", index: id)
111
136
  end
112
137
  end
113
138
  end
@@ -3,6 +3,7 @@
3
3
  require "isodoc"
4
4
  require_relative "collection_fileprocess"
5
5
  require_relative "fontist_utils"
6
+ require_relative "util"
6
7
 
7
8
  module Metanorma
8
9
  # XML collection renderer
@@ -34,13 +35,18 @@ module Metanorma
34
35
 
35
36
  @outdir = options[:output_folder]
36
37
  @coverpage = options[:coverpage]
37
- @format = options[:format]
38
+ @format = Util.sort_extensions_execution(options[:format])
38
39
  @compile_options = options[:compile] || {}
40
+ @compile_options[:no_install_fonts] = true if options[:no_install_fonts]
39
41
  @log = options[:log]
40
42
  @documents = collection.documents
43
+ @directives = collection.directives
44
+ @disambig = Util::DisambigFiles.new
45
+ @compile = Compile.new
41
46
 
42
47
  # list of files in the collection
43
48
  @files = read_files folder
49
+ isodoc_populate(@isodoc)
44
50
  FileUtils.rm_rf @outdir
45
51
  FileUtils.mkdir_p @outdir
46
52
  end
@@ -48,14 +54,20 @@ module Metanorma
48
54
  # @param col [Metanorma::Collection] XML collection
49
55
  # @param options [Hash]
50
56
  # @option options [String] :coverpage cover page HTML (Liquid template)
51
- # @option options [Array<Synbol>] :format list of formats
57
+ # @option options [Array<Symbol>] :format list of formats
52
58
  # @option options [Strong] :ourput_folder output directory
53
59
  def self.render(col, options = {})
54
60
  folder = File.dirname col.file
61
+ # require "byebug"; byebug
62
+ warn "\n\n\n\n\nRender Init: #{DateTime.now.strftime('%H:%M:%S')}"
55
63
  cr = new(col, folder, options)
64
+ warn "\n\n\n\n\nRender Files: #{DateTime.now.strftime('%H:%M:%S')}"
56
65
  cr.files
66
+ warn "\n\n\n\n\nConcatenate: #{DateTime.now.strftime('%H:%M:%S')}"
57
67
  cr.concatenate(col, options)
68
+ warn "\n\n\n\n\nCoverpage: #{DateTime.now.strftime('%H:%M:%S')}"
58
69
  cr.coverpage if options[:format]&.include?(:html)
70
+ warn "\n\n\n\n\nDone: #{DateTime.now.strftime('%H:%M:%S')}"
59
71
  end
60
72
 
61
73
  def concatenate(col, options)
@@ -66,13 +78,15 @@ module Metanorma
66
78
  ext = e == :presentation ? "presentation.xml" : e.to_s
67
79
  out = col.clone
68
80
  out.directives << "documents-inline"
69
- out.documents.keys.each do |id|
70
- next if @files[id][:attachment]
81
+ out.documents.each_key do |id|
82
+ next if @files[id][:attachment] || @files[id][:outputs].nil?
71
83
 
72
84
  filename = @files[id][:outputs][e]
73
85
  out.documents[id] = Metanorma::Document.raw_file(filename)
74
86
  end
75
- 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
76
90
  end
77
91
  options[:format].include?(:pdf) and
78
92
  pdfconv.convert(File.join(@outdir, "collection.presentation.xml"))
@@ -103,15 +117,21 @@ module Metanorma
103
117
  end
104
118
 
105
119
  # The isodoc class for the metanorma flavour we are using
106
- def isodoc # rubocop:disable Metrics/MethodLength
120
+ def isodoc
107
121
  x = Asciidoctor.load nil, backend: @doctype.to_sym
108
122
  isodoc = x.converter.html_converter(Dummy.new)
109
123
  isodoc.i18n_init(@lang, @script) # read in internationalisation
124
+ isodoc.metadata_init(@lang, @script, isodoc.i18n)
125
+ isodoc.info(@xml, nil)
126
+ isodoc
127
+ end
128
+
129
+ def isodoc_populate(isodoc)
110
130
  # create the @meta class of isodoc, with "navigation" set to the index bar
111
131
  # extracted from the manifest
112
132
  nav = indexfile(@xml.at(ns("//manifest")))
113
133
  i18n = isodoc.i18n
114
- i18n.set(:navigation, nav)
134
+ i18n.set("navigation", nav)
115
135
  isodoc.metadata_init(@lang, @script, i18n)
116
136
  # populate the @meta class of isodoc with the various metadata fields
117
137
  # native to the flavour; used to populate Liquid
@@ -159,7 +179,7 @@ module Metanorma
159
179
  # @param elm [Nokogiri::XML::Element]
160
180
  # @param builder [Nokogiri::XML::Builder]
161
181
  def indexfile_docref(elm, builder)
162
- return "" unless elm.at(ns("./docref"))
182
+ return "" unless elm.at(ns("./docref[@index = 'true']"))
163
183
 
164
184
  builder.ul { |b| docrefs(elm, b) }
165
185
  end
@@ -167,12 +187,20 @@ module Metanorma
167
187
  # @param elm [Nokogiri::XML::Element]
168
188
  # @param builder [Nokogiri::XML::Builder]
169
189
  def docrefs(elm, builder)
170
- elm.xpath(ns("./docref")).each do |d|
171
- identifier = d.at(ns("./identifier")).text
172
- link = if d["fileref"] then d["fileref"].sub(/\.xml$/, ".html")
173
- else d["id"] + ".html"
174
- end
175
- builder.li { builder.a identifier, href: link }
190
+ elm.xpath(ns("./docref[@index = 'true']")).each do |d|
191
+ ident = d.at(ns("./identifier")).children.to_xml
192
+ builder.li do |li|
193
+ li.a **{ href: index_link(d, ident) } do |a|
194
+ a << ident
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ def index_link(docref, ident)
201
+ if docref["fileref"]
202
+ @files[ident][:out_path].sub(/\.xml$/, ".html")
203
+ else "#{docref['id']}.html"
176
204
  end
177
205
  end
178
206
 
@@ -198,6 +226,14 @@ module Metanorma
198
226
 
199
227
  private
200
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
+
201
237
  # @param options [Hash]
202
238
  # @raise [ArgumentError]
203
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
@@ -53,7 +58,7 @@ module Metanorma
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
@@ -74,7 +77,9 @@ module Metanorma
74
77
  memo
75
78
  end
76
79
  end
77
- if !extensions.include?(:presentation) and extensions.any? { |e| @processor.use_presentation_xml(e) }
80
+ if !extensions.include?(:presentation) && extensions.any? do |e|
81
+ @processor.use_presentation_xml(e)
82
+ end
78
83
  extensions << :presentation
79
84
  end
80
85
  extensions
@@ -82,41 +87,50 @@ module Metanorma
82
87
 
83
88
  def process_input(filename, options)
84
89
  case extname = File.extname(filename)
85
- when ".adoc"
86
- Util.log("[metanorma] Processing: AsciiDoc input.", :info)
87
- file = read_file(filename)
88
- options[:asciimath] and
89
- file.sub!(/^(=[^\n]+\n)/, "\\1:mn-keep-asciimath:\n")
90
- dir = File.dirname(filename)
91
- dir != '.' and
92
- file.gsub!(/^include::/, "include::#{dir}/")
93
- [file, @processor.input_to_isodoc(file, filename, options)]
94
- when ".xml"
95
- Util.log("[metanorma] Processing: Metanorma XML input.", :info)
96
- # TODO NN: this is a hack -- we should provide/bridge the
97
- # document attributes in Metanorma XML
98
- ["", read_file(filename)]
90
+ when ".adoc" then process_input_adoc(filename, options)
91
+ when ".xml" then process_input_xml(filename, options)
99
92
  else
100
- Util.log("[metanorma] Error: file extension #{extname} is not supported.", :error)
93
+ Util.log("[metanorma] Error: file extension #{extname} "\
94
+ "is not supported.", :error)
101
95
  nil
102
96
  end
103
97
  end
104
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
+
105
117
  def read_file(filename)
106
118
  File.read(filename, encoding: "utf-8").gsub("\r\n", "\n")
107
119
  end
108
120
 
109
121
  def relaton_export(isodoc, options)
110
122
  return unless options[:relaton]
111
- xml = Nokogiri::XML(isodoc)
123
+
124
+ xml = Nokogiri::XML(isodoc) { |config| config.huge }
112
125
  bibdata = xml.at("//bibdata") || xml.at("//xmlns:bibdata")
113
- #docid = bibdata&.at("./xmlns:docidentifier")&.text || options[:filename]
114
- #outname = docid.sub(/^\s+/, "").sub(/\s+$/, "").gsub(/\s+/, "-") + ".xml"
126
+ # docid = bibdata&.at("./xmlns:docidentifier")&.text || options[:filename]
127
+ # outname = docid.sub(/^\s+/, "").sub(/\s+$/, "").gsub(/\s+/, "-") + ".xml"
115
128
  File.open(options[:relaton], "w:UTF-8") { |f| f.write bibdata.to_xml }
116
129
  end
117
130
 
118
131
  def clean_sourcecode(xml)
119
- xml.xpath(".//callout | .//annotation | .//xmlns:callout | .//xmlns:annotation").each do |x|
132
+ xml.xpath(".//callout | .//annotation | .//xmlns:callout | "\
133
+ ".//xmlns:annotation").each do |x|
120
134
  x.remove
121
135
  end
122
136
  xml.xpath(".//br | .//xmlns:br").each { |x| x.replace("\n") }
@@ -125,12 +139,13 @@ module Metanorma
125
139
 
126
140
  def extract(isodoc, dirname, extract_types)
127
141
  return unless dirname
142
+
128
143
  if extract_types.nil? || extract_types.empty?
129
- extract_types = [:sourcecode, :image, :requirement]
144
+ extract_types = %i[sourcecode image requirement]
130
145
  end
131
146
  FileUtils.rm_rf dirname
132
147
  FileUtils.mkdir_p dirname
133
- xml = Nokogiri::XML(isodoc)
148
+ xml = Nokogiri::XML(isodoc) { |config| config.huge }
134
149
  sourcecode_export(xml, dirname) if extract_types.include? :sourcecode
135
150
  image_export(xml, dirname) if extract_types.include? :image
136
151
  requirement_export(xml, dirname) if extract_types.include? :requirement
@@ -151,7 +166,7 @@ module Metanorma
151
166
  xml.at("//image | //xmlns:image") or return
152
167
  FileUtils.mkdir_p "#{dirname}/image"
153
168
  xml.xpath("//image | //xmlns:image").each_with_index do |s, i|
154
- next unless /^data:image/.match s["src"]
169
+ next unless /^data:image/.match? s["src"]
155
170
 
156
171
  %r{^data:image/(?<imgtype>[^;]+);base64,(?<imgdata>.+)$} =~ s["src"]
157
172
  filename = s["filename"] || sprintf("image-%04d.%s", i, imgtype)
@@ -176,17 +191,6 @@ module Metanorma
176
191
  end
177
192
  end
178
193
 
179
- # dependency ordering
180
- def sort_extensions_execution(ext)
181
- case ext
182
- when :xml then 0
183
- when :rxl then 1
184
- when :presentation then 2
185
- else
186
- 99
187
- end
188
- end
189
-
190
194
  def wrap_html(options, file_extension, outfilename)
191
195
  if options[:wrapper] && /html$/.match(file_extension)
192
196
  outfilename = outfilename.sub(/\.html$/, "")
@@ -201,34 +205,25 @@ module Metanorma
201
205
  f = change_output_dir options
202
206
  xml_name = f.sub(/\.[^.]+$/, ".xml")
203
207
  presentationxml_name = f.sub(/\.[^.]+$/, ".presentation.xml")
204
- extensions.sort do |a, b|
205
- sort_extensions_execution(a) <=> sort_extensions_execution(b)
206
- end.each do |ext|
207
- isodoc_options = @processor.extract_options(file)
208
- isodoc_options[:datauriimage] = true if options[:datauriimage]
209
- isodoc_options[:sourcefilename] = options[:filename]
208
+ Util.sort_extensions_execution(extensions).each do |ext|
210
209
  file_extension = @processor.output_formats[ext]
211
210
  outfilename = f.sub(/\.[^.]+$/, ".#{file_extension}")
212
- if ext == :pdf
213
- font_locations = FontistUtils.fontist_font_locations(@processor, options)
214
- font_locations and
215
- isodoc_options[:mn2pdf] = { font_manifest_file: font_locations.path }
216
- end
211
+ isodoc_options = get_isodoc_options(file, options, ext)
217
212
  if ext == :rxl
218
- options[:relaton] = outfilename
219
- relaton_export(isodoc, options)
213
+ relaton_export(isodoc, options.merge(relaton: outfilename))
214
+ elsif options[:passthrough_presentation_xml] && ext == :presentation
215
+ FileUtils.cp f, presentationxml_name
216
+ elsif ext == :html && options[:sectionsplit]
217
+ sectionsplit_convert(xml_name, isodoc, outfilename, isodoc_options)
220
218
  else
221
219
  begin
222
- @processor.use_presentation_xml(ext) ?
223
- @processor.output(nil, presentationxml_name, outfilename, ext, isodoc_options) :
224
- @processor.output(isodoc, xml_name, outfilename, ext, isodoc_options)
225
- rescue StandardError => e
226
- if e.message.include? "Fatal:"
227
- @errors << e.message
220
+ if @processor.use_presentation_xml(ext)
221
+ @processor.output(nil, presentationxml_name, outfilename, ext, isodoc_options)
228
222
  else
229
- puts e.message
230
- puts e.backtrace.join("\n")
223
+ @processor.output(isodoc, xml_name, outfilename, ext, isodoc_options)
231
224
  end
225
+ rescue StandardError => e
226
+ isodoc_error_process(e)
232
227
  end
233
228
  end
234
229
  wrap_html(options, file_extension, outfilename)
@@ -237,6 +232,29 @@ module Metanorma
237
232
 
238
233
  private
239
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
+
240
258
  # @param options [Hash]
241
259
  # @return [String]
242
260
  def change_output_dir(options)