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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -1
- data/lib/metanorma/collection/collection.rb +10 -4
- data/lib/metanorma/collection/config/config.rb +21 -0
- data/lib/metanorma/collection/config/manifest.rb +4 -1
- data/lib/metanorma/collection/document/document.rb +2 -0
- data/lib/metanorma/collection/filelookup/filelookup.rb +15 -6
- data/lib/metanorma/collection/filelookup/filelookup_sectionsplit.rb +4 -3
- data/lib/metanorma/collection/manifest/manifest.rb +7 -11
- data/lib/metanorma/collection/renderer/fileparse.rb +12 -39
- data/lib/metanorma/collection/renderer/fileprocess.rb +26 -10
- data/lib/metanorma/collection/renderer/navigation.rb +1 -1
- data/lib/metanorma/collection/renderer/renderer.rb +14 -1
- data/lib/metanorma/collection/renderer/utils.rb +8 -10
- data/lib/metanorma/collection/sectionsplit/collection.rb +1 -0
- data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +84 -49
- data/lib/metanorma/collection/util/util.rb +24 -11
- data/lib/metanorma/collection/xrefprocess/xrefprocess.rb +2 -3
- data/lib/metanorma/compile/assets/icc-boilerplate.adoc +41 -0
- data/lib/metanorma/compile/compile.rb +148 -172
- data/lib/metanorma/compile/compile_options.rb +108 -82
- data/lib/metanorma/compile/extract.rb +76 -56
- data/lib/metanorma/compile/flavor.rb +54 -0
- data/lib/metanorma/compile/output_filename.rb +75 -0
- data/lib/metanorma/compile/output_filename_config.rb +27 -0
- data/lib/metanorma/compile/relaton_drop.rb +56 -0
- data/lib/metanorma/compile/render.rb +178 -0
- data/lib/metanorma/compile/validator.rb +26 -0
- data/lib/metanorma/compile/writeable.rb +12 -0
- data/lib/metanorma/input/asciidoc.rb +13 -6
- data/lib/metanorma/registry/registry.rb +11 -8
- data/lib/metanorma/version.rb +1 -1
- data/metanorma.gemspec +2 -1
- metadata +24 -3
- 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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
73
|
+
REQUIREMENT_XPATH =
|
|
74
|
+
"//requirement | //xmlns:requirement | //recommendation | "\
|
|
75
|
+
"//xmlns:recommendation | //permission | //xmlns:permission"
|
|
58
76
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
78
|
+
hdr, = header(file)
|
|
72
79
|
ret = ADOC_OPTIONS.each_with_object({}) do |w, acc|
|
|
73
|
-
m = /\n:#{w}:\s+([^\n]+)\n/.match(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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)
|