metanorma 1.2.7 → 1.3.3

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.
@@ -44,7 +44,8 @@ module Metanorma
44
44
  def parse_docref(mnf)
45
45
  mnf.xpath("xmlns:docref").map do |dr|
46
46
  h = { "identifier" => dr.at("identifier").text }
47
- h["fileref"] = dr[:fileref] if dr[:fileref]
47
+ dr[:fileref] and h["fileref"] = dr[:fileref]
48
+ h["attachment"] = dr[:attachment] if dr[:attachment]
48
49
  h
49
50
  end
50
51
  end
@@ -62,12 +63,13 @@ module Metanorma
62
63
  docs = @docref.each_with_object({}) do |dr, m|
63
64
  next m unless dr["fileref"]
64
65
 
65
- m[dr["identifier"]] = Document.parse_file File.join(dir, dr["fileref"])
66
+ m[dr["identifier"]] = Document.parse_file(
67
+ File.join(dir, dr["fileref"]),
68
+ dr["attachment"], dr["identifier"]
69
+ )
66
70
  m
67
71
  end
68
- @manifest.reduce(docs) do |mem, mnf|
69
- mem.merge mnf.documents(dir)
70
- end
72
+ @manifest.reduce(docs) { |mem, mnf| mem.merge mnf.documents(dir) }
71
73
  end
72
74
 
73
75
  # @param builder [Nokogiri::XML::Builder]
@@ -100,7 +102,8 @@ module Metanorma
100
102
  def docref_to_xml(builder)
101
103
  @docref.each do |dr|
102
104
  drf = builder.docref { |b| b.identifier dr["identifier"] }
103
- drf[:fileref] = dr["fileref"]
105
+ drf[:fileref] = Util::source2dest_filename(dr["fileref"])
106
+ drf[:attachment] = dr["attachment"] if dr["attachment"]
104
107
  if collection.directives.include?("documents-inline")
105
108
  id = collection.documents.find_index { |k, _| k == dr["identifier"] }
106
109
  drf[:id] = format("doc%<index>09d", index: id)
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "isodoc"
4
- require_relative "./collection_fileprocess"
4
+ require_relative "collection_fileprocess"
5
+ require_relative "fontist_utils"
5
6
 
6
7
  module Metanorma
7
8
  # XML collection renderer
@@ -20,9 +21,9 @@ module Metanorma
20
21
  # the collection, and that the flavour gem can sensibly process it. We may
21
22
  # need to enhance metadata in the flavour gems isodoc/metadata.rb with
22
23
  # collection metadata
23
- def initialize(xml, folder, options = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
24
+ def initialize(collection, folder, options = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
24
25
  check_options options
25
- @xml = Nokogiri::XML xml # @xml is the collection manifest
26
+ @xml = Nokogiri::XML collection.to_xml # @xml is the collection manifest
26
27
  @lang = @xml&.at(ns("//bibdata/language"))&.text || "en"
27
28
  @script = @xml&.at(ns("//bibdata/script"))&.text || "Latn"
28
29
  @doctype = doctype
@@ -36,6 +37,7 @@ module Metanorma
36
37
  @format = options[:format]
37
38
  @compile_options = options[:compile] || {}
38
39
  @log = options[:log]
40
+ @documents = collection.documents
39
41
 
40
42
  # list of files in the collection
41
43
  @files = read_files folder
@@ -50,7 +52,7 @@ module Metanorma
50
52
  # @option options [Strong] :ourput_folder output directory
51
53
  def self.render(col, options = {})
52
54
  folder = File.dirname col.file
53
- cr = new(col.to_xml, folder, options)
55
+ cr = new(col, folder, options)
54
56
  cr.files
55
57
  cr.concatenate(col, options)
56
58
  cr.coverpage if options[:format]&.include?(:html)
@@ -60,10 +62,13 @@ module Metanorma
60
62
  options[:format] << :presentation if options[:format].include?(:pdf)
61
63
  options[:format].uniq.each do |e|
62
64
  next unless %i(presentation xml).include?(e)
65
+
63
66
  ext = e == :presentation ? "presentation.xml" : e.to_s
64
67
  out = col.clone
65
68
  out.directives << "documents-inline"
66
69
  out.documents.keys.each do |id|
70
+ next if @files[id][:attachment]
71
+
67
72
  filename = @files[id][:outputs][e]
68
73
  out.documents[id] = Metanorma::Document.raw_file(filename)
69
74
  end
@@ -74,13 +79,27 @@ module Metanorma
74
79
  end
75
80
 
76
81
  def pdfconv
77
- x = Asciidoctor.load nil, backend: @doctype.to_sym
78
- x.converter.pdf_converter(Dummy.new)
82
+ doctype = @doctype.to_sym
83
+ x = Asciidoctor.load nil, backend: doctype
84
+ x.converter.pdf_converter(PdfOptionsNode.new(doctype, @compile_options))
85
+ end
86
+
87
+ class PdfOptionsNode
88
+ def initialize(doctype, options)
89
+ doc_proc = Metanorma::Registry.instance.find_processor(doctype)
90
+ @font_locations = FontistUtils.fontist_font_locations(doc_proc, options)
91
+ end
92
+
93
+ def attr(key)
94
+ if key == "mn2pdf-font-manifest-file" && @font_locations
95
+ @font_locations.path
96
+ end
97
+ end
79
98
  end
80
99
 
81
100
  # Dummy class
82
101
  class Dummy
83
- def attr(_xyz); end
102
+ def attr(_key); end
84
103
  end
85
104
 
86
105
  # The isodoc class for the metanorma flavour we are using
@@ -119,6 +138,8 @@ module Metanorma
119
138
  # populate liquid template of ARGV[1] with metadata extracted from
120
139
  # collection manifest
121
140
  def coverpage
141
+ return unless @coverpage
142
+
122
143
  File.open(File.join(@outdir, "index.html"), "w:UTF-8") do |f|
123
144
  f.write @isodoc.populate_template(File.read(@coverpage))
124
145
  end
@@ -183,8 +204,6 @@ module Metanorma
183
204
  unless options[:format].is_a?(Array) && (FORMATS & options[:format]).any?
184
205
  raise ArgumentError, "Need to specify formats (xml,html,pdf,doc)"
185
206
  end
186
- return if !options[:format].include?(:html) || options[:coverpage]
187
- raise ArgumentError, "Need to specify a coverpage to render HTML"
188
207
  end
189
208
  end
190
209
  end
@@ -1,9 +1,11 @@
1
1
  require "fileutils"
2
2
  require "nokogiri"
3
3
  require "htmlentities"
4
-
4
+ require "yaml"
5
5
  require "fontist"
6
6
  require "fontist/manifest/install"
7
+ require_relative "compile_validate"
8
+ require_relative "fontist_utils"
7
9
 
8
10
  module Metanorma
9
11
  class Compile
@@ -18,29 +20,25 @@ module Metanorma
18
20
  def compile(filename, options = {})
19
21
  require_libraries(options)
20
22
  options = options_extract(filename, options)
21
- validate_type(options) && validate_format(options) || (return nil)
23
+ validate_options(options)
22
24
  @processor = @registry.find_processor(options[:type].to_sym)
23
25
  extensions = get_extensions(options) or return nil
24
26
  (file, isodoc = process_input(filename, options)) or return nil
25
27
  relaton_export(isodoc, options)
26
28
  extract(isodoc, options[:extract], options[:extract_type])
27
- install_fonts(options)
29
+ FontistUtils.install_fonts(@processor, options)
28
30
  process_extensions(extensions, file, isodoc, options)
29
31
  end
30
32
 
31
33
  def require_libraries(options)
32
- if options[:require]
33
- options[:require].each do |r|
34
- require r
35
- end
36
- end
34
+ options&.dig(:require)&.each { |r| require r }
37
35
  end
38
36
 
39
37
  def xml_options_extract(file)
40
38
  xml = Nokogiri::XML(file)
41
39
  if xml.root
42
40
  @registry.root_tags.each do |k, v|
43
- return { type: k } if v == xml.root.name
41
+ return { type: k } if v == xml.root.name
44
42
  end
45
43
  end
46
44
  {}
@@ -62,48 +60,6 @@ module Metanorma
62
60
  options
63
61
  end
64
62
 
65
- def validate_type(options)
66
- unless options[:type]
67
- Util.log("[metanorma] Error: Please specify a standard type: #{@registry.supported_backends}.", :error)
68
- return nil
69
- end
70
-
71
- stdtype = options[:type].to_sym
72
- metanorma_flavor = "metanorma-#{stdtype}"
73
-
74
- unless @registry.supported_backends.include? stdtype
75
- Util.log("[metanorma] Info: Loading `#{metanorma_flavor}` gem for standard type `#{stdtype}`.", :info)
76
- end
77
-
78
- begin
79
- require "metanorma-#{stdtype}"
80
- Util.log("[metanorma] Info: gem `#{metanorma_flavor}` loaded.", :info)
81
-
82
- rescue Gem::ConflictError
83
- Util.log("[metanorma] Error: Couldn't resolve dependencies for `metanorma-#{stdtype}`, Please add it to your Gemfile and run bundle install first", :error)
84
- return false
85
-
86
- rescue LoadError
87
- Util.log("[metanorma] Error: loading gem `#{metanorma_flavor}` failed. Exiting.", :error)
88
- return false
89
- end
90
-
91
- unless @registry.supported_backends.include? stdtype
92
- Util.log("[metanorma] Error: The `#{metanorma_flavor}` gem still doesn't support `#{stdtype}`. Exiting.", :error)
93
- return false
94
- end
95
-
96
- true
97
- end
98
-
99
- def validate_format(options)
100
- unless options[:format] == :asciidoc
101
- Util.log("[metanorma] Error: Only source file format currently supported is 'asciidoc'.", :error)
102
- return false
103
- end
104
- true
105
- end
106
-
107
63
  def get_extensions(options)
108
64
  options[:extension_keys] ||= @processor.output_formats.reduce([]) do |memo, (k, _)|
109
65
  memo << k
@@ -120,7 +76,7 @@ module Metanorma
120
76
  end
121
77
  if !extensions.include?(:presentation) and extensions.any? { |e| @processor.use_presentation_xml(e) }
122
78
  extensions << :presentation
123
- end
79
+ end
124
80
  extensions
125
81
  end
126
82
 
@@ -186,7 +142,7 @@ module Metanorma
186
142
  xml.xpath("//sourcecode | //xmlns:sourcecode").each_with_index do |s, i|
187
143
  filename = s["filename"] || sprintf("sourcecode-%04d.txt", i)
188
144
  File.open("#{dirname}/sourcecode/#{filename}", "w:UTF-8") do |f|
189
- f.write clean_sourcecode(s.dup)
145
+ f.write clean_sourcecode(s.dup)
190
146
  end
191
147
  end
192
148
  end
@@ -196,6 +152,7 @@ module Metanorma
196
152
  FileUtils.mkdir_p "#{dirname}/image"
197
153
  xml.xpath("//image | //xmlns:image").each_with_index do |s, i|
198
154
  next unless /^data:image/.match s["src"]
155
+
199
156
  %r{^data:image/(?<imgtype>[^;]+);base64,(?<imgdata>.+)$} =~ s["src"]
200
157
  filename = s["filename"] || sprintf("image-%04d.%s", i, imgtype)
201
158
  File.open("#{dirname}/image/#{filename}", "wb") do |f|
@@ -221,22 +178,22 @@ module Metanorma
221
178
 
222
179
  # dependency ordering
223
180
  def sort_extensions_execution(ext)
224
- case ext
225
- when :xml then 0
226
- when :rxl then 1
227
- when :presentation then 2
228
- else
229
- 99
230
- end
181
+ case ext
182
+ when :xml then 0
183
+ when :rxl then 1
184
+ when :presentation then 2
185
+ else
186
+ 99
187
+ end
231
188
  end
232
189
 
233
190
  def wrap_html(options, file_extension, outfilename)
234
- if options[:wrapper] and /html$/.match file_extension
235
- outfilename = outfilename.sub(/\.html$/, "")
236
- FileUtils.mkdir_p outfilename
237
- FileUtils.mv "#{outfilename}.html", outfilename
238
- FileUtils.mv "#{outfilename}_images", outfilename, force: true
239
- end
191
+ if options[:wrapper] && /html$/.match(file_extension)
192
+ outfilename = outfilename.sub(/\.html$/, "")
193
+ FileUtils.mkdir_p outfilename
194
+ FileUtils.mv "#{outfilename}.html", outfilename
195
+ FileUtils.mv "#{outfilename}_images", outfilename, force: true
196
+ end
240
197
  end
241
198
 
242
199
  # isodoc is Raw Metanorma XML
@@ -249,8 +206,14 @@ module Metanorma
249
206
  end.each do |ext|
250
207
  isodoc_options = @processor.extract_options(file)
251
208
  isodoc_options[:datauriimage] = true if options[:datauriimage]
209
+ isodoc_options[:sourcefilename] = options[:filename]
252
210
  file_extension = @processor.output_formats[ext]
253
211
  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
254
217
  if ext == :rxl
255
218
  options[:relaton] = outfilename
256
219
  relaton_export(isodoc, options)
@@ -260,66 +223,20 @@ module Metanorma
260
223
  @processor.output(nil, presentationxml_name, outfilename, ext, isodoc_options) :
261
224
  @processor.output(isodoc, xml_name, outfilename, ext, isodoc_options)
262
225
  rescue StandardError => e
263
- puts e.message
264
- puts e.backtrace.join("\n")
226
+ if e.message.include? "Fatal:"
227
+ @errors << e.message
228
+ else
229
+ puts e.message
230
+ puts e.backtrace.join("\n")
231
+ end
265
232
  end
266
233
  end
267
234
  wrap_html(options, file_extension, outfilename)
268
235
  end
269
236
  end
270
237
 
271
- def install_fonts(options)
272
- if options[:no_install_fonts]
273
- Util.log("[fontist] Skip font installation because" \
274
- " --no-install-fonts argument passed", :debug)
275
- return
276
- end
277
-
278
- if !@processor.respond_to?(:fonts_manifest) || @processor.fonts_manifest.nil?
279
- Util.log("[fontist] Skip font installation because font_manifest is missing", :debug)
280
- return
281
- end
282
-
283
- @updated_formulas_repo = false
284
-
285
- manifest = @processor.fonts_manifest
286
- agree_to_terms = options[:agree_to_terms] || false
287
- continue_without_fonts = options[:continue_without_fonts] || false
288
-
289
- install_fonts_safe(manifest, agree_to_terms, continue_without_fonts)
290
- end
291
-
292
238
  private
293
239
 
294
- def install_fonts_safe(manifest, agree, continue)
295
- fontist_install(manifest, agree)
296
- rescue Fontist::Errors::LicensingError
297
- if continue
298
- Util.log("[fontist] Processing will continue without fonts installed", :debug)
299
- else
300
- Util.log("[fontist] Aborting without proper fonts installed," \
301
- " make sure that you have set option --agree-to-terms", :fatal)
302
- end
303
- rescue Fontist::Errors::FontError => e
304
- log_level = continue ? :warning : :fatal
305
- Util.log("[fontist] '#{e.font}' font is not supported. " \
306
- "Please report this issue at github.com/metanorma/metanorma-#{@processor.short}/issues" \
307
- " to report this issue.", log_level)
308
- rescue Fontist::Errors::FormulaIndexNotFoundError
309
- Util.log("[fontist] Bug: formula index not found after 'fontist update'", :fatal) if @updated_formulas_repo
310
- Util.log("[fontist] Missing formula index. Fetching it...", :debug)
311
- Fontist::Formula.update_formulas_repo
312
- @updated_formulas_repo = true
313
- install_fonts_safe(manifest, agree, continue)
314
- end
315
-
316
- def fontist_install(manifest, agree)
317
- Fontist::Manifest::Install.from_hash(
318
- manifest,
319
- confirmation: agree ? "yes" : "no"
320
- )
321
- end
322
-
323
240
  # @param options [Hash]
324
241
  # @return [String]
325
242
  def change_output_dir(options)
@@ -0,0 +1,51 @@
1
+ module Metanorma
2
+ class Compile
3
+ def validate_options(options)
4
+ validate_type(options)
5
+ validate_format(options)
6
+ end
7
+
8
+ def validate_type(options)
9
+ unless options[:type]
10
+ Util.log("[metanorma] Error: Please specify a standard type: "\
11
+ "#{@registry.supported_backends}.", :fatal)
12
+ end
13
+ stdtype = options[:type].to_sym
14
+ load_flavor(stdtype)
15
+ end
16
+
17
+ def validate_format(options)
18
+ unless options[:format] == :asciidoc
19
+ Util.log("[metanorma] Error: Only source file format currently "\
20
+ "supported is 'asciidoc'.", :fatal)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def load_flavor(stdtype)
27
+ flavor = "metanorma-#{stdtype}"
28
+ unless @registry.supported_backends.include? stdtype
29
+ Util.log("[metanorma] Info: Loading `#{flavor}` gem "\
30
+ "for standard type `#{stdtype}`.", :info)
31
+ end
32
+ require_flavor(flavor, stdtype)
33
+ unless @registry.supported_backends.include? stdtype
34
+ Util.log("[metanorma] Error: The `#{flavor}` gem "\
35
+ "still doesn't support `#{stdtype}`. Exiting.", :fatal)
36
+ end
37
+ end
38
+
39
+ def require_flavor(flavor, stdtype)
40
+ require flavor
41
+ Util.log("[metanorma] Info: gem `#{flavor}` loaded.", :info)
42
+ rescue Gem::ConflictError
43
+ Util.log("[metanorma] Error: Couldn't resolve dependencies for "\
44
+ "`metanorma-#{stdtype}`, Please add it to your Gemfile "\
45
+ "and run bundle install first", :fatal)
46
+ rescue LoadError
47
+ Util.log("[metanorma] Error: loading gem `#{flavor}` "\
48
+ "failed. Exiting.", :fatal)
49
+ end
50
+ end
51
+ end
@@ -15,7 +15,7 @@ module Metanorma
15
15
  attr_accessor :logs
16
16
 
17
17
  def initialize
18
- @logs ||= [:warning, :error, :fatal]
18
+ @logs ||= %i[warning error fatal]
19
19
  end
20
20
  end
21
21
 
@@ -1,20 +1,25 @@
1
1
  module Metanorma
2
2
  class Document
3
3
  # @return [Strin]
4
- attr_reader :file
4
+ attr_reader :file, :attachment, :bibitem
5
5
 
6
6
  # @param bibitem [RelatonBib::BibliographicItem]
7
7
  def initialize(bibitem, file, options = {})
8
8
  @bibitem = bibitem
9
9
  @file = file
10
+ @attachment = options[:attachment]
10
11
  @raw = options[:raw]
11
12
  end
12
13
 
13
14
  class << self
14
15
  # @param file [String] file path
16
+ # @param attachment [Bool] is an attachment
17
+ # @param identifier [String] is the identifier assigned the file
18
+ # in the collection file
15
19
  # @return [Metanorma::Document]
16
- def parse_file(file)
17
- new bibitem(file), file
20
+ def parse_file(file, attachment, identifier = nil)
21
+ new(bibitem(file, attachment, identifier), file,
22
+ { attachment: attachment })
18
23
  end
19
24
 
20
25
  # #param xml [Nokogiri::XML::Document, Nokogiri::XML::Element]
@@ -29,6 +34,12 @@ module Metanorma
29
34
  new(doc, filename, raw: true)
30
35
  end
31
36
 
37
+ def attachment_bibitem(identifier)
38
+ Nokogiri::XML(
39
+ "<bibdata><docidentifier>#{identifier}</docidentifier></bibdata>",
40
+ )
41
+ end
42
+
32
43
  private
33
44
 
34
45
  # #param xml [Nokogiri::XML::Document, Nokogiri::XML::Element]
@@ -49,13 +60,16 @@ module Metanorma
49
60
  # @param file [String]
50
61
  # @return [RelatonBib::BibliographicItem,
51
62
  # RelatonIso::IsoBibliographicItem]
52
- def bibitem(file)
53
- case format(file)
54
- when :xml
55
- from_xml Nokogiri::XML(File.read(file, encoding: "UTF-8"))
56
- when :yaml
57
- yaml = File.read(file, encoding: "UTF-8")
58
- Relaton::Cli::YAMLConvertor.convert_single_file(yaml)
63
+ def bibitem(file, attachment, identifier)
64
+ if attachment then attachment_bibitem(identifier)
65
+ else
66
+ case format(file)
67
+ when :xml
68
+ from_xml Nokogiri::XML(File.read(file, encoding: "UTF-8"))
69
+ when :yaml
70
+ yaml = File.read(file, encoding: "UTF-8")
71
+ Relaton::Cli::YAMLConvertor.convert_single_file(yaml)
72
+ end
59
73
  end
60
74
  end
61
75
  end