metanorma 2.1.4 → 2.2.0

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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/lib/metanorma/collection/collection.rb +10 -4
  4. data/lib/metanorma/collection/config/config.rb +21 -0
  5. data/lib/metanorma/collection/config/manifest.rb +4 -1
  6. data/lib/metanorma/collection/document/document.rb +2 -0
  7. data/lib/metanorma/collection/filelookup/filelookup.rb +15 -6
  8. data/lib/metanorma/collection/filelookup/filelookup_sectionsplit.rb +4 -3
  9. data/lib/metanorma/collection/manifest/manifest.rb +7 -11
  10. data/lib/metanorma/collection/renderer/fileparse.rb +12 -39
  11. data/lib/metanorma/collection/renderer/fileprocess.rb +26 -10
  12. data/lib/metanorma/collection/renderer/navigation.rb +1 -1
  13. data/lib/metanorma/collection/renderer/renderer.rb +14 -1
  14. data/lib/metanorma/collection/renderer/utils.rb +8 -10
  15. data/lib/metanorma/collection/sectionsplit/collection.rb +1 -0
  16. data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +84 -49
  17. data/lib/metanorma/collection/util/util.rb +24 -11
  18. data/lib/metanorma/collection/xrefprocess/xrefprocess.rb +2 -3
  19. data/lib/metanorma/compile/assets/icc-boilerplate.adoc +41 -0
  20. data/lib/metanorma/compile/compile.rb +148 -172
  21. data/lib/metanorma/compile/compile_options.rb +108 -82
  22. data/lib/metanorma/compile/extract.rb +76 -56
  23. data/lib/metanorma/compile/flavor.rb +54 -0
  24. data/lib/metanorma/compile/output_filename.rb +75 -0
  25. data/lib/metanorma/compile/output_filename_config.rb +27 -0
  26. data/lib/metanorma/compile/relaton_drop.rb +56 -0
  27. data/lib/metanorma/compile/render.rb +178 -0
  28. data/lib/metanorma/compile/validator.rb +26 -0
  29. data/lib/metanorma/compile/writeable.rb +12 -0
  30. data/lib/metanorma/input/asciidoc.rb +13 -6
  31. data/lib/metanorma/registry/registry.rb +11 -8
  32. data/lib/metanorma/version.rb +1 -1
  33. data/metanorma.gemspec +2 -1
  34. metadata +24 -3
  35. data/lib/metanorma/compile/compile_validate.rb +0 -68
@@ -1,68 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "writeable"
4
+
1
5
  module Metanorma
2
6
  class Compile
3
- def relaton_export(isodoc, options)
4
- options[:relaton] or return
5
- xml = Nokogiri::XML(isodoc, &:huge)
6
- bibdata = xml.at("//bibdata") || xml.at("//xmlns:bibdata")
7
- File.open(options[:relaton], "w:UTF-8") { |f| f.write bibdata.to_xml }
8
- end
7
+ module Extract
8
+ # @param isodoc [String] the XML document
9
+ # @param dirname [String, nil] the directory to extract to
10
+ # @param extract_types [Array<Symbol>, nil] the types to extract
11
+ # @return [void]
12
+ def self.extract(isodoc, dirname, extract_types)
13
+ dirname or return
14
+ extract_types.nil? || extract_types.empty? and
15
+ extract_types = %i[sourcecode image requirement]
16
+ FileUtils.rm_rf dirname
17
+ FileUtils.mkdir_p dirname
18
+ xml = Nokogiri::XML(isodoc, &:huge)
19
+ extract_types.each do |type|
20
+ case type
21
+ when :sourcecode
22
+ export_sourcecode(xml, dirname)
23
+ when :image
24
+ export_image(xml, dirname)
25
+ when :requirement
26
+ export_requirement(xml, dirname)
27
+ end
28
+ end
29
+ end
9
30
 
10
- def clean_sourcecode(xml)
11
- xml.xpath(".//callout | .//annotation | .//xmlns:callout | "\
12
- ".//xmlns:annotation").each(&:remove)
13
- xml.xpath(".//br | .//xmlns:br").each { |x| x.replace("\n") }
14
- a = xml.at("./body | ./xmlns:body") and xml = a
15
- HTMLEntities.new.decode(xml.children.to_xml)
16
- end
31
+ class << self
32
+ include Writeable
17
33
 
18
- def extract(isodoc, dirname, extract_types)
19
- dirname or return
20
- extract_types.nil? || extract_types.empty? and
21
- extract_types = %i[sourcecode image requirement]
22
- FileUtils.rm_rf dirname
23
- FileUtils.mkdir_p dirname
24
- xml = Nokogiri::XML(isodoc, &:huge)
25
- sourcecode_export(xml, dirname) if extract_types.include? :sourcecode
26
- image_export(xml, dirname) if extract_types.include? :image
27
- extract_types.include?(:requirement) and
28
- requirement_export(xml, dirname)
29
- end
34
+ private
30
35
 
31
- def sourcecode_export(xml, dirname)
32
- xml.at("//sourcecode | //xmlns:sourcecode") or return
33
- FileUtils.mkdir_p "#{dirname}/sourcecode"
34
- xml.xpath("//sourcecode | //xmlns:sourcecode").each_with_index do |s, i|
35
- filename = s["filename"] || sprintf("sourcecode-%04d.txt", i)
36
- export_output("#{dirname}/sourcecode/#{filename}",
37
- clean_sourcecode(s.dup))
38
- end
39
- end
36
+ # @param xml [Nokogiri::XML::Document] the XML document
37
+ # @return [String] the cleaned sourcecode
38
+ def clean_sourcecode(xml)
39
+ xml.xpath(".//callout | .//annotation | .//xmlns:callout | "\
40
+ ".//xmlns:annotation").each(&:remove)
41
+ xml.xpath(".//br | .//xmlns:br").each { |x| x.replace("\n") }
42
+ a = xml.at("./body | ./xmlns:body") and xml = a
43
+ HTMLEntities.new.decode(xml.children.to_xml)
44
+ end
40
45
 
41
- def image_export(xml, dirname)
42
- xml.at("//image | //xmlns:image") or return
43
- FileUtils.mkdir_p "#{dirname}/image"
44
- xml.xpath("//image | //xmlns:image").each_with_index do |s, i|
45
- next unless /^data:image/.match? s["src"]
46
+ def export_sourcecode(xml, dirname)
47
+ xml.at("//sourcecode | //xmlns:sourcecode") or return
48
+ FileUtils.mkdir_p "#{dirname}/sourcecode"
49
+ xml.xpath("//sourcecode | //xmlns:sourcecode").each_with_index do |s, i|
50
+ filename = s["filename"] || sprintf("sourcecode-%04d.txt", i)
51
+ export_output("#{dirname}/sourcecode/#{filename}",
52
+ clean_sourcecode(s.dup))
53
+ end
54
+ end
46
55
 
47
- %r{^data:image/(?<imgtype>[^;]+);base64,(?<imgdata>.+)$} =~ s["src"]
48
- fn = s["filename"] || sprintf("image-%<num>04d.%<name>s",
49
- num: i, name: imgtype)
50
- export_output("#{dirname}/image/#{fn}", Base64.strict_decode64(imgdata),
51
- binary: true)
52
- end
53
- end
56
+ def export_image(xml, dirname)
57
+ xml.at("//image | //xmlns:image") or return
58
+ FileUtils.mkdir_p "#{dirname}/image"
59
+ xml.xpath("//image | //xmlns:image").each_with_index do |s, i|
60
+ next unless /^data:image/.match? s["src"]
61
+
62
+ %r{^data:image/(?<imgtype>[^;]+);base64,(?<imgdata>.+)$} =~ s["src"]
63
+ fn = s["filename"] || sprintf("image-%<num>04d.%<name>s",
64
+ num: i, name: imgtype)
65
+ export_output(
66
+ "#{dirname}/image/#{fn}",
67
+ Base64.strict_decode64(imgdata),
68
+ binary: true,
69
+ )
70
+ end
71
+ end
54
72
 
55
- REQUIREMENT_XPATH =
56
- "//requirement | //xmlns:requirement | //recommendation | "\
57
- "//xmlns:recommendation | //permission | //xmlns:permission".freeze
73
+ REQUIREMENT_XPATH =
74
+ "//requirement | //xmlns:requirement | //recommendation | "\
75
+ "//xmlns:recommendation | //permission | //xmlns:permission"
58
76
 
59
- def requirement_export(xml, dirname)
60
- xml.at(REQUIREMENT_XPATH) or return
61
- FileUtils.mkdir_p "#{dirname}/requirement"
62
- xml.xpath(REQUIREMENT_XPATH).each_with_index do |s, i|
63
- fn = s["filename"] ||
64
- sprintf("%<name>s-%<num>04d.xml", name: s.name, num: i)
65
- export_output("#{dirname}/requirement/#{fn}", s)
77
+ def export_requirement(xml, dirname)
78
+ xml.at(REQUIREMENT_XPATH) or return
79
+ FileUtils.mkdir_p "#{dirname}/requirement"
80
+ xml.xpath(REQUIREMENT_XPATH).each_with_index do |s, i|
81
+ fn = s["filename"] ||
82
+ sprintf("%<name>s-%<num>04d.xml", name: s.name, num: i)
83
+ export_output("#{dirname}/requirement/#{fn}", s)
84
+ end
85
+ end
66
86
  end
67
87
  end
68
88
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ module Flavor
6
+ # Load the flavor gem for the given standard type
7
+ # @param stdtype [Symbol] the standard type
8
+ # @return [void]
9
+ def load_flavor(stdtype)
10
+ stdtype = stdtype.to_sym
11
+ flavor = stdtype2flavor_gem(stdtype)
12
+ @registry.supported_backends.include? stdtype or
13
+ Util.log("[metanorma] Info: Loading `#{flavor}` gem "\
14
+ "for standard type `#{stdtype}`.", :info)
15
+ require_flavor(flavor)
16
+ @registry.supported_backends.include? stdtype or
17
+ Util.log("[metanorma] Error: The `#{flavor}` gem does not "\
18
+ "support the standard type #{stdtype}. Exiting.", :fatal)
19
+ end
20
+
21
+ # Convert the standard type to the flavor gem name
22
+ # @param stdtype [Symbol] the standard type
23
+ # @return [String] the flavor gem name
24
+ def stdtype2flavor_gem(stdtype)
25
+ "metanorma-#{stdtype}"
26
+ end
27
+
28
+ private
29
+
30
+ def require_flavor(flavor)
31
+ require flavor
32
+ Util.log("[metanorma] Info: gem `#{flavor}` loaded.", :info)
33
+ rescue LoadError => e
34
+ error_log = "#{Date.today}-error.log"
35
+ File.write(error_log, e)
36
+
37
+ msg = <<~MSG
38
+ Error: #{e.message}
39
+ Metanorma has encountered an exception.
40
+
41
+ If this problem persists, please report this issue at the following link:
42
+
43
+ * https://github.com/metanorma/metanorma/issues/new
44
+
45
+ Please attach the #{error_log} file.
46
+ Your valuable feedback is very much appreciated!
47
+
48
+ - The Metanorma team
49
+ MSG
50
+ Util.log(msg, :fatal)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ class OutputFilename
6
+ # Returns an instance of OutputFilename from the source filename
7
+ # @param source_filename [String] the source filename
8
+ # @param output_dir [String, nil] the output directory
9
+ # @param processor [Metanorma::Processor, nil] the processor
10
+ # @return [OutputFilename] the instance of OutputFilename
11
+ def self.from_filename(source_filename, output_dir = nil, processor = nil)
12
+ new(strip_ext(source_filename), output_dir, processor)
13
+ end
14
+
15
+ class << self
16
+ private
17
+
18
+ def strip_ext(filename)
19
+ filename.sub(/\.[^.]*$/, "")
20
+ end
21
+ end
22
+
23
+ # @param noext_filename [String] the path (absolute/relative) of the source file, without extension (e.g., "/a/b/c/test")
24
+ # @param output_dir [String, nil] the output directory
25
+ # @param processor [Metanorma::Processor, nil] the processor
26
+ # @return [OutputFilename] the instance of OutputFilename
27
+ def initialize(noext_filename, output_dir = nil, processor = nil)
28
+ @noext_filename = noext_filename
29
+ @output_dir = output_dir
30
+ @processor = processor
31
+ end
32
+
33
+ # Returns the full file path name with the semantic XML extension
34
+ # @return [String] the full file path name with the semantic XML extension
35
+ def semantic_xml
36
+ with_extension("xml")
37
+ end
38
+
39
+ # Returns the full file path name with the presentation XML extension
40
+ # @return [String] the full file path name with the presentation XML extension
41
+ def presentation_xml
42
+ with_extension("presentation.xml")
43
+ end
44
+
45
+ # Returns the full file path name with the given format extension
46
+ # @param format [Symbol] the format
47
+ # @return [String, nil] the full file path name with the format extension
48
+ def for_format(format)
49
+ ext = @processor&.output_formats&.[](format)
50
+ ext ? with_extension(ext) : nil
51
+ end
52
+
53
+ # Returns the full file path name with the given extension
54
+ # @param ext [String] the extension
55
+ # @return [String] the full file path name with the extension
56
+ def with_extension(ext)
57
+ file = change_output_dir
58
+ "#{file}.#{ext}"
59
+ end
60
+
61
+ private
62
+
63
+ def change_output_dir
64
+ File.expand_path(if !@output_dir.nil?
65
+ File.join(
66
+ @output_dir,
67
+ File.basename(@noext_filename),
68
+ )
69
+ else
70
+ @noext_filename
71
+ end)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ class OutputFilenameConfig
6
+ DEFAULT_TEMPLATE =
7
+ "{{ document.docidentifier | downcase" \
8
+ " | replace: '/' , '-'" \
9
+ " | replace: ' ' , '-' }}"
10
+
11
+ attr_reader :template
12
+
13
+ def initialize(template)
14
+ @template = if template.nil? || template.empty?
15
+ DEFAULT_TEMPLATE
16
+ else
17
+ template
18
+ end
19
+ end
20
+
21
+ def generate_filename(relaton_data)
22
+ template = Liquid::Template.parse(@template)
23
+ template.render("document" => relaton_data)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "liquid"
4
+
5
+ module Metanorma
6
+ class Compile
7
+ class RelatonDrop < Liquid::Drop
8
+ def initialize(relaton_data)
9
+ @relaton = relaton_data
10
+ end
11
+
12
+ def docidentifier
13
+ at("./docidentifier")
14
+ end
15
+
16
+ def title
17
+ at("./title")
18
+ end
19
+
20
+ def date
21
+ at("./date/on")
22
+ end
23
+
24
+ def publisher
25
+ at("./contributor[role/@type = 'publisher']/organization/name")
26
+ end
27
+
28
+ def language
29
+ at("./language")
30
+ end
31
+
32
+ def script
33
+ at("./script")
34
+ end
35
+
36
+ def version
37
+ at("./version")
38
+ end
39
+
40
+ def slugify
41
+ docidentifier&.downcase
42
+ &.gsub(/[^a-z0-9]+/, "-")
43
+ &.gsub(/-+/, "-")
44
+ &.gsub(/^-|-$/, "")
45
+ end
46
+
47
+ private
48
+
49
+ def at(xpath)
50
+ @relaton.at(xpath)&.text
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+
@@ -0,0 +1,178 @@
1
+ module Metanorma
2
+ class Compile
3
+ # Generate presentation XML from semantic XML
4
+ def generate_presentation_xml(source_file, xml, bibdata, output_paths, opt)
5
+ process_ext(:presentation, source_file, xml, bibdata, output_paths, opt)
6
+ end
7
+
8
+ # Generate multiple output formats with parallel processing
9
+ def generate_outputs_parallel(
10
+ source_file, semantic_xml, bibdata, extensions, output_paths, options
11
+ )
12
+ @queue = ::Metanorma::Util::WorkersPool.new(
13
+ ENV["METANORMA_PARALLEL"]&.to_i || DEFAULT_NUM_WORKERS,
14
+ )
15
+ # Install required fonts for all extensions
16
+ gather_and_install_fonts(source_file, options.dup, extensions)
17
+ # Process each extension in order
18
+ process_extensions_in_order(
19
+ source_file, semantic_xml, bibdata, extensions, output_paths, options
20
+ )
21
+ @queue.shutdown
22
+ end
23
+
24
+ def process_extensions_in_order(
25
+ source_file, semantic_xml, bibdata, extensions, output_paths, options
26
+ )
27
+ Util.sort_extensions_execution(extensions).each do |ext|
28
+ process_ext(
29
+ ext, source_file, semantic_xml, bibdata, output_paths, options
30
+ ) or break
31
+ end
32
+ end
33
+
34
+ # Export given bibliographic data to Relaton XML on disk
35
+ # @param bibdata [Nokogiri::XML::Element] the bibliographic data element
36
+ # @param options [Hash] compilation options
37
+ def export_relaton_from_bibdata(bibdata, options)
38
+ options[:relaton] or return
39
+ export_output(options[:relaton], bibdata.to_xml)
40
+ end
41
+
42
+ # @param xml [Nokogiri::XML::Document] the XML document
43
+ # @return [Nokogiri::XML::Element] the bibliographic data element
44
+ def extract_relaton_metadata(xml)
45
+ xml.at("//bibdata") || xml.at("//xmlns:bibdata")
46
+ end
47
+
48
+ def wrap_html(options, file_extension, outfilename)
49
+ if options[:wrapper] && /html$/.match(file_extension)
50
+ outfilename = outfilename.sub(/\.html$/, "")
51
+ FileUtils.mkdir_p outfilename
52
+ FileUtils.mv "#{outfilename}.html", outfilename
53
+ FileUtils.mv "#{outfilename}_images", outfilename, force: true
54
+ end
55
+ end
56
+
57
+ # isodoc is Raw Metanorma XML
58
+ def gather_and_install_fonts(source_file, options, extensions)
59
+ Util.sort_extensions_execution(extensions).each do |ext|
60
+ isodoc_options = get_isodoc_options(source_file, options, ext)
61
+ font_install(isodoc_options.merge(options))
62
+ end
63
+ end
64
+
65
+ # Process a single extension (output format)
66
+ def process_ext(ext, source_file, semantic_xml, bibdata, output_paths,
67
+ options)
68
+ output_paths[:ext] = @processor.output_formats[ext]
69
+ output_paths[:out] = @output_filename.for_format(ext) ||
70
+ output_paths[:xml].sub(/\.[^.]+$/, ".#{output_paths[:ext]}")
71
+ isodoc_options = get_isodoc_options(source_file, options, ext)
72
+
73
+ # Handle special cases first
74
+ return true if process_ext_special(
75
+ ext, semantic_xml, bibdata, output_paths, options, isodoc_options
76
+ )
77
+
78
+ # Otherwise, determine if it uses presentation XML
79
+ if @processor.use_presentation_xml(ext)
80
+ # Format requires presentation XML first, then convert to final format
81
+ process_via_presentation_xml(ext, output_paths, options, isodoc_options)
82
+ else
83
+ # Format can be generated directly from semantic XML
84
+ process_from_semantic_xml(
85
+ ext, output_paths, semantic_xml, isodoc_options
86
+ )
87
+ end
88
+ end
89
+
90
+ # Process special extensions with custom handling
91
+ def process_ext_special(
92
+ ext, sem_xml, bibdata, output_paths, options, isodoc_options
93
+ )
94
+ if ext == :rxl
95
+ # Special case: Relaton export
96
+ export_relaton_from_bibdata(
97
+ bibdata,
98
+ options.merge(relaton: output_paths[:out]),
99
+ )
100
+ true
101
+ elsif ext == :presentation && options[:passthrough_presentation_xml]
102
+ # Special case: Pass through presentation XML
103
+ f = if File.exist?(output_paths[:orig_filename])
104
+ output_paths[:orig_filename]
105
+ else
106
+ output_paths[:xml]
107
+ end
108
+ FileUtils.cp f, output_paths[:presentationxml]
109
+ true
110
+ elsif ext == :html && options[:sectionsplit]
111
+ # Special case: Split HTML into sections
112
+ sectionsplit_convert(
113
+ output_paths[:xml], sem_xml, output_paths[:out], isodoc_options
114
+ )
115
+ true
116
+ else
117
+ false
118
+ end
119
+ end
120
+
121
+ # Process format that requires presentation XML
122
+ def process_via_presentation_xml(ext, output_paths, options, isodoc_options)
123
+ @queue.schedule(ext, output_paths.dup, options.dup,
124
+ isodoc_options.dup) do |a, b, c, d|
125
+ process_output_from_presentation_xml(a, b, c, d)
126
+ end
127
+ end
128
+
129
+ # Generate output format from presentation XML
130
+ def process_output_from_presentation_xml(ext, output_paths, options,
131
+ isodoc_options)
132
+ @processor.output(nil, output_paths[:presentationxml],
133
+ output_paths[:out], ext, isodoc_options)
134
+ wrap_html(options, output_paths[:ext], output_paths[:out])
135
+ rescue StandardError => e
136
+ strict = ext == :presentation || isodoc_options[:strict] == true
137
+ isodoc_error_process(e, strict, false)
138
+ end
139
+
140
+ # Process format directly from semantic XML
141
+ def process_from_semantic_xml(ext, output_paths, sem_xml, isodoc_options)
142
+ @processor.output(sem_xml, output_paths[:xml], output_paths[:out],
143
+ ext, isodoc_options)
144
+ true # Return as Thread equivalent
145
+ rescue StandardError => e
146
+ strict = ext == :presentation || isodoc_options[:strict] == "true"
147
+ isodoc_error_process(e, strict, true)
148
+ ext != :presentation
149
+ end
150
+
151
+ # assume we pass in Presentation XML, but we want to recover Semantic XML
152
+ def sectionsplit_convert(input_filename, file, output_filename = nil,
153
+ opts = {})
154
+ @isodoc ||= IsoDoc::PresentationXMLConvert.new({})
155
+ input_filename += ".xml" unless input_filename.match?(/\.xml$/)
156
+ File.exist?(input_filename) or export_output(input_filename, file)
157
+ presxml = File.read(input_filename, encoding: "utf-8")
158
+ _xml, filename, dir = @isodoc.convert_init(presxml, input_filename, false)
159
+ ::Metanorma::Collection::Sectionsplit.new(
160
+ input: input_filename, isodoc: @isodoc, xml: presxml,
161
+ base: File.basename(output_filename || filename),
162
+ output: output_filename || filename, dir: dir, compile_opts: opts
163
+ ).build_collection
164
+ end
165
+
166
+ private
167
+
168
+ def isodoc_error_process(err, strict, must_abort)
169
+ if strict || err.message.include?("Fatal:")
170
+ @errors << err.message
171
+ else
172
+ puts err.message
173
+ end
174
+ puts err.backtrace.join("\n")
175
+ must_abort and 1
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ module Validator
6
+ def validate_options!(options)
7
+ validate_type!(options)
8
+ validate_format!(options)
9
+ end
10
+
11
+ def validate_type!(options)
12
+ options[:type] or
13
+ Util.log("[metanorma] Error: Please specify a standard type: "\
14
+ "#{@registry.supported_backends}.", :fatal)
15
+ stdtype = options[:type].to_sym
16
+ load_flavor(stdtype)
17
+ end
18
+
19
+ def validate_format!(options)
20
+ options[:format] == :asciidoc or
21
+ Util.log("[metanorma] Error: Only source file format currently "\
22
+ "supported is 'asciidoc'.", :fatal)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ module Writeable
6
+ def export_output(fname, content, **options)
7
+ mode = options[:binary] ? "wb" : "w:UTF-8"
8
+ File.open(fname, mode) { |f| f.write content }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -13,8 +13,14 @@ module Metanorma
13
13
  ::Asciidoctor.convert(file, out_opts)
14
14
  end
15
15
 
16
+ def header(file)
17
+ ret = file.split("\n\n", 2) or return [nil, nil]
18
+ ret[0] and ret[0] += "\n"
19
+ [ret[0], ret[1]]
20
+ end
21
+
16
22
  def extract_metanorma_options(file)
17
- hdr = file.sub(/\n\n.*$/m, "\n")
23
+ hdr, = header(file)
18
24
  /\n:(?:mn-)?(?:document-class|flavor):\s+(?<type>\S[^\n]*)\n/ =~ hdr
19
25
  /\n:(?:mn-)?output-extensions:\s+(?<extensions>\S[^\n]*)\n/ =~ hdr
20
26
  /\n:(?:mn-)?relaton-output-file:\s+(?<relaton>\S[^\n]*)\n/ =~ hdr
@@ -22,7 +28,7 @@ module Metanorma
22
28
  /\n(?<novalid>:novalid:[^\n]*)\n/ =~ hdr
23
29
  if defined?(asciimath)
24
30
  asciimath =
25
- !asciimath.nil? && !/keep-asciimath: false/.match?(asciimath)
31
+ !asciimath.nil? && !/keep-asciimath:\s*false/.match?(asciimath)
26
32
  end
27
33
  asciimath = nil if asciimath == false
28
34
  {
@@ -49,6 +55,7 @@ module Metanorma
49
55
  pdf-owner-password pdf-allow-copy-content pdf-allow-edit-content
50
56
  pdf-allow-assemble-document pdf-allow-edit-annotations
51
57
  pdf-allow-print pdf-allow-print-hq pdf-allow-fill-in-forms
58
+ pdf-stylesheet pdf-stylesheet-override
52
59
  fonts font-license-agreement pdf-allow-access-content
53
60
  pdf-encrypt-metadata iso-word-template document-scheme
54
61
  localize-number iso-word-bg-strip-color modspec-identifier-base)
@@ -68,17 +75,17 @@ module Metanorma
68
75
  end
69
76
 
70
77
  def extract_options(file)
71
- header = file.sub(/\n\n.*$/m, "\n")
78
+ hdr, = header(file)
72
79
  ret = ADOC_OPTIONS.each_with_object({}) do |w, acc|
73
- m = /\n:#{w}:\s+([^\n]+)\n/.match(header) or next
80
+ m = /\n:#{w}:\s+([^\n]+)\n/.match(hdr) or next
74
81
  acc[attr_name_normalise(w)] = m[1]&.strip
75
82
  end
76
83
  ret2 = EMPTY_ADOC_OPTIONS_DEFAULT_TRUE.each_with_object({}) do |w, acc|
77
- m = /\n:#{w}:([^\n]*)\n/.match(header) || [nil, "true"]
84
+ m = /\n:#{w}:([^\n]*)\n/.match(hdr) || [nil, "true"]
78
85
  acc[attr_name_normalise(w)] = (m[1].strip != "false")
79
86
  end
80
87
  ret3 = EMPTY_ADOC_OPTIONS_DEFAULT_FALSE.each_with_object({}) do |w, acc|
81
- m = /\n:#{w}:([^\n]*)\n/.match(header) || [nil, "false"]
88
+ m = /\n:#{w}:([^\n]*)\n/.match(hdr) || [nil, "false"]
82
89
  acc[attr_name_normalise(w)] = !["false"].include?(m[1].strip)
83
90
  end
84
91
  ret.merge(ret2).merge(ret3).compact
@@ -1,6 +1,7 @@
1
1
  # Registry of all Metanorma types and entry points
2
2
 
3
3
  require "singleton"
4
+ require "metanorma-taste"
4
5
 
5
6
  class Error < StandardError
6
7
  end
@@ -9,11 +10,17 @@ module Metanorma
9
10
  class Registry
10
11
  include Singleton
11
12
 
12
- attr_reader :processors
13
+ attr_reader :processors, :tastes
13
14
 
15
+ # TODO: make aliases configurable
14
16
  def initialize
15
17
  @processors = {}
18
+ @tastes = Metanorma::TasteRegister.instance
19
+ tastealiases = @tastes.available_tastes.each_with_object({}) do |x, m|
20
+ m[x] = @tastes.taste_info(x)[:base_flavor]
21
+ end
16
22
  @aliases = { csd: :cc, m3d: :m3aawg, mpfd: :mpfa, csand: :csa }
23
+ .merge tastealiases
17
24
  end
18
25
 
19
26
  def alias(flavour)
@@ -21,18 +28,14 @@ module Metanorma
21
28
  end
22
29
 
23
30
  def register(processor)
24
- raise Error unless processor < ::Metanorma::Processor
25
-
31
+ processor < ::Metanorma::Processor or raise Error
26
32
  p = processor.new
27
33
  # p.short[-1] is the canonical name
28
34
  short = Array(p.short)
29
35
  @processors[short[-1]] = p
30
- short.each do |s|
31
- @aliases[s] = short[-1]
32
- end
36
+ short.each { |s| @aliases[s] = short[-1] }
33
37
  Array(p.short)
34
- Util.log("[metanorma] processor \"#{Array(p.short)[0]}\" registered",
35
- :info)
38
+ Util.log("[metanorma] processor \"#{Array(p.short)[0]}\" registered", :info)
36
39
  end
37
40
 
38
41
  def find_processor(short)