metanorma 1.2.7 → 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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