metanorma 2.1.4 → 2.5.4

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +23 -2
  3. data/Gemfile +14 -0
  4. data/README.adoc +1 -1
  5. data/lib/metanorma/collection/artifact_store.rb +142 -0
  6. data/lib/metanorma/collection/collection.rb +140 -131
  7. data/lib/metanorma/collection/config/bibdata.rb +1 -1
  8. data/lib/metanorma/collection/config/config.rb +50 -20
  9. data/lib/metanorma/collection/config/converters.rb +89 -31
  10. data/lib/metanorma/collection/config/doc_container.rb +28 -0
  11. data/lib/metanorma/collection/config/manifest.rb +30 -6
  12. data/lib/metanorma/collection/config/namespaces.rb +12 -0
  13. data/lib/metanorma/collection/document/document.rb +54 -15
  14. data/lib/metanorma/collection/filelookup/filelookup.rb +170 -74
  15. data/lib/metanorma/collection/filelookup/filelookup_sectionsplit.rb +62 -27
  16. data/lib/metanorma/collection/filelookup/utils.rb +52 -0
  17. data/lib/metanorma/collection/helpers.rb +82 -0
  18. data/lib/metanorma/collection/log.rb +24 -0
  19. data/lib/metanorma/collection/manifest/manifest.rb +27 -14
  20. data/lib/metanorma/collection/multilingual/multilingual.rb +1 -1
  21. data/lib/metanorma/collection/renderer/filelocation.rb +166 -0
  22. data/lib/metanorma/collection/renderer/fileparse.rb +60 -55
  23. data/lib/metanorma/collection/renderer/fileprocess.rb +184 -40
  24. data/lib/metanorma/collection/renderer/navigation.rb +20 -6
  25. data/lib/metanorma/collection/renderer/render_word.rb +8 -4
  26. data/lib/metanorma/collection/renderer/renderer.rb +202 -19
  27. data/lib/metanorma/collection/renderer/svg.rb +60 -5
  28. data/lib/metanorma/collection/renderer/utils.rb +76 -42
  29. data/lib/metanorma/collection/sectionsplit/collection.rb +14 -4
  30. data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +141 -61
  31. data/lib/metanorma/collection/util/disambig_files.rb +4 -5
  32. data/lib/metanorma/collection/util/util.rb +147 -23
  33. data/lib/metanorma/collection/xrefprocess/xrefprocess.rb +7 -7
  34. data/lib/metanorma/compile/assets/icc-boilerplate.adoc +41 -0
  35. data/lib/metanorma/compile/compile.rb +180 -165
  36. data/lib/metanorma/compile/compile_options.rb +155 -83
  37. data/lib/metanorma/compile/extract.rb +76 -56
  38. data/lib/metanorma/compile/flavor.rb +19 -0
  39. data/lib/metanorma/compile/output_filename.rb +75 -0
  40. data/lib/metanorma/compile/output_filename_config.rb +27 -0
  41. data/lib/metanorma/compile/relaton_drop.rb +56 -0
  42. data/lib/metanorma/compile/render.rb +196 -0
  43. data/lib/metanorma/compile/validator.rb +26 -0
  44. data/lib/metanorma/compile/writeable.rb +12 -0
  45. data/lib/metanorma/util/fontist_helper.rb +35 -19
  46. data/lib/metanorma/version.rb +1 -1
  47. data/lib/metanorma.rb +1 -7
  48. data/metanorma.gemspec +11 -20
  49. data/plans/collection-rendering-architecture-review.md +516 -0
  50. metadata +42 -122
  51. data/.hound.yml +0 -5
  52. data/lib/metanorma/asciidoctor_extensions/glob_include_processor.rb +0 -22
  53. data/lib/metanorma/asciidoctor_extensions.rb +0 -1
  54. data/lib/metanorma/compile/compile_validate.rb +0 -68
  55. data/lib/metanorma/config/config.rb +0 -23
  56. data/lib/metanorma/input/asciidoc.rb +0 -88
  57. data/lib/metanorma/input/base.rb +0 -9
  58. data/lib/metanorma/input.rb +0 -7
  59. data/lib/metanorma/processor/processor.rb +0 -54
  60. data/lib/metanorma/processor.rb +0 -6
  61. data/lib/metanorma/registry/registry.rb +0 -63
  62. data/lib/metanorma/util/util.rb +0 -45
@@ -0,0 +1,196 @@
1
+ module Metanorma
2
+ class Compile
3
+ NO_FONTIST_FORMATS = %i[xml presentation rxl].freeze
4
+
5
+ # Generate presentation XML from semantic XML
6
+ def generate_presentation_xml(source_file, xml, bibdata, output_paths, opt)
7
+ process_ext(:presentation, source_file, xml, bibdata, output_paths, opt)
8
+ end
9
+
10
+ # Generate multiple output formats with parallel processing
11
+ def generate_outputs_parallel(
12
+ source_file, semantic_xml, bibdata, extensions, output_paths, options
13
+ )
14
+ @queue = ::Metanorma::Util::WorkersPool.new(
15
+ ENV["METANORMA_PARALLEL"]&.to_i || DEFAULT_NUM_WORKERS,
16
+ )
17
+ # Install required fonts for all extensions
18
+ gather_and_install_fonts(source_file, options.dup, extensions)
19
+ # Process each extension in order
20
+ process_extensions_in_order(
21
+ source_file, semantic_xml, bibdata, extensions, output_paths, options
22
+ )
23
+ @queue.shutdown
24
+ end
25
+
26
+ def process_extensions_in_order(
27
+ source_file, semantic_xml, bibdata, extensions, output_paths, options
28
+ )
29
+ Util.sort_extensions_execution(extensions).each do |ext|
30
+ process_ext(
31
+ ext, source_file, semantic_xml, bibdata, output_paths, options
32
+ ) or break
33
+ end
34
+ end
35
+
36
+ # Export given bibliographic data to Relaton XML on disk
37
+ # @param bibdata [Nokogiri::XML::Element] the bibliographic data element
38
+ # @param options [Hash] compilation options
39
+ def export_relaton_from_bibdata(bibdata, options)
40
+ options[:relaton] or return
41
+ export_output(options[:relaton], bibdata.to_xml)
42
+ end
43
+
44
+ # @param xml [Nokogiri::XML::Document] the XML document
45
+ # @return [Nokogiri::XML::Element] the bibliographic data element
46
+ def extract_relaton_metadata(xml)
47
+ xml.at("//bibdata") || xml.at("//xmlns:bibdata")
48
+ end
49
+
50
+ def wrap_html(options, file_extension, outfilename)
51
+ if options[:wrapper] && /html$/.match(file_extension)
52
+ outfilename = outfilename.sub(/\.html$/, "")
53
+ FileUtils.mkdir_p outfilename
54
+ FileUtils.mv "#{outfilename}.html", outfilename
55
+ FileUtils.mv "#{outfilename}_images", outfilename, force: true
56
+ end
57
+ end
58
+
59
+ # isodoc is Raw Metanorma XML
60
+ def gather_and_install_fonts(source_file, options, extensions)
61
+ return if (extensions - NO_FONTIST_FORMATS).empty?
62
+
63
+ install_options = @processor.extract_options(source_file).merge(options)
64
+ font_install(install_options)
65
+ end
66
+
67
+ # Process a single extension (output format)
68
+ def process_ext(ext, source_file, semantic_xml, bibdata, output_paths,
69
+ options)
70
+ output_paths[:ext] = effective_output_formats(options)[ext]
71
+ output_paths[:out] = @output_filename.for_format(ext) ||
72
+ output_paths[:xml].sub(/\.[^.]+$/, ".#{output_paths[:ext]}")
73
+ isodoc_options = get_isodoc_options(source_file, options, ext)
74
+
75
+ # Handle special cases first
76
+ return true if process_ext_special(
77
+ ext, semantic_xml, bibdata, output_paths, options, isodoc_options
78
+ )
79
+
80
+ # Otherwise, determine if it uses presentation XML
81
+ if uses_presentation_xml?(ext, options)
82
+ # Format requires presentation XML first, then convert to final format
83
+ process_via_presentation_xml(ext, output_paths, options, isodoc_options)
84
+ else
85
+ # Format can be generated directly from semantic XML
86
+ process_from_semantic_xml(
87
+ ext, output_paths, semantic_xml, isodoc_options
88
+ )
89
+ end
90
+ end
91
+
92
+ # Process special extensions with custom handling
93
+ def process_ext_special(
94
+ ext, sem_xml, bibdata, output_paths, options, isodoc_options
95
+ )
96
+ if ext == :rxl
97
+ # Special case: Relaton export
98
+ export_relaton_from_bibdata(
99
+ bibdata,
100
+ options.merge(relaton: output_paths[:out]),
101
+ )
102
+ true
103
+ elsif ext == :presentation && options[:passthrough_presentation_xml]
104
+ # Special case: Pass through presentation XML
105
+ f = if File.exist?(output_paths[:orig_filename])
106
+ output_paths[:orig_filename]
107
+ else
108
+ output_paths[:xml]
109
+ end
110
+ FileUtils.cp f, output_paths[:presentationxml]
111
+ true
112
+ elsif ext == :html && options[:sectionsplit]
113
+ # Special case: Split HTML into sections
114
+ sectionsplit_convert(
115
+ output_paths[:xml], sem_xml, output_paths[:out], isodoc_options
116
+ )
117
+ true
118
+ else
119
+ false
120
+ end
121
+ end
122
+
123
+ # Process format that requires presentation XML
124
+ def process_via_presentation_xml(ext, output_paths, options, isodoc_options)
125
+ @queue.schedule(ext, output_paths.dup, options.dup,
126
+ isodoc_options.dup) do |a, b, c, d|
127
+ process_output_from_presentation_xml(a, b, c, d)
128
+ end
129
+ end
130
+
131
+ # Generate output format from presentation XML
132
+ def process_output_from_presentation_xml(ext, output_paths, options,
133
+ isodoc_options)
134
+ @processor.output(nil, output_paths[:presentationxml],
135
+ output_paths[:out], ext, isodoc_options)
136
+ wrap_html(options, output_paths[:ext], output_paths[:out])
137
+ rescue StandardError => e
138
+ isodoc_error_process(e, strict_error?(ext, isodoc_options), false)
139
+ end
140
+
141
+ # Process format directly from semantic XML
142
+ def process_from_semantic_xml(ext, output_paths, sem_xml, isodoc_options)
143
+ @processor.output(sem_xml, output_paths[:xml], output_paths[:out],
144
+ ext, isodoc_options)
145
+ true # Return as Thread equivalent
146
+ rescue StandardError => e
147
+ isodoc_error_process(e, strict_error?(ext, isodoc_options), 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
+ sectionsplit_filename: opts[:sectionsplit_filename],
163
+ output: output_filename || filename, dir: dir, compile_opts: opts
164
+ ).build_collection
165
+ end
166
+
167
+ private
168
+
169
+ # A presentation failure is always strict: every downstream format
170
+ # needs the presentation XML. The :strict option arrives as a
171
+ # boolean from the CLI but as a string from document attributes, so
172
+ # accept both (the two rescue sites previously each checked only
173
+ # one form).
174
+ def strict_error?(ext, isodoc_options)
175
+ ext == :presentation ||
176
+ [true, "true"].include?(isodoc_options[:strict])
177
+ end
178
+
179
+ # On the strict path the message is pooled into @errors for the
180
+ # caller to surface (the standalone CLI reports the pool and
181
+ # aborts; the collection renderer attributes it to the failed
182
+ # member, #586). The class+message line is also printed here in all
183
+ # cases, so it sits adjacent to its backtrace in the build log: a
184
+ # pooled message surfaces only at end of run, and a bare backtrace
185
+ # mid-log is undiagnosable. The exception class is retained because
186
+ # for errors like Encoding::UndefinedConversionError it carries
187
+ # half the diagnosis.
188
+ def isodoc_error_process(err, strict, must_abort)
189
+ msg = "#{err.class}: #{err.message}"
190
+ strict || err.message.include?("Fatal:") and @errors << msg
191
+ puts msg
192
+ puts err.backtrace.join("\n")
193
+ must_abort and 1
194
+ end
195
+ end
196
+ 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
@@ -15,12 +15,12 @@ module Metanorma
15
15
 
16
16
  def validate_install_fonts(processor, options)
17
17
  unless install_fonts?(options)
18
- Util.log("[fontist] Skip font installation because" \
19
- " --no-install-fonts argument passed", :debug)
18
+ Util.log("[fontist] Skip font installation because " \
19
+ "--no-install-fonts argument passed", :debug)
20
20
  return false
21
21
  end
22
22
  unless has_custom_fonts?(processor, options, options)
23
- Util.log("[fontist] Skip font installation because "\
23
+ Util.log("[fontist] Skip font installation because " \
24
24
  "fonts_manifest is missing", :debug)
25
25
  return false
26
26
  end
@@ -34,24 +34,23 @@ module Metanorma
34
34
  rescue Fontist::Errors::FontError => e
35
35
  log_level = continue ? :warning : :fatal
36
36
  Util.log("[fontist] '#{e.font}' font is not supported. " \
37
- "Please report this issue at github.com/metanorma/metanorma" \
38
- "/issues to report this issue.", log_level)
37
+ "Please report this issue at " \
38
+ "https://github.com/metanorma/metanorma/issues", log_level)
39
39
  rescue Fontist::Errors::FormulaIndexNotFoundError
40
40
  fontist_update_repo(manifest, agree, continue, no_progress)
41
41
  end
42
42
 
43
43
  def fontist_install(manifest, agree, no_progress)
44
- if agree
45
- no_license_log
44
+ if agree then no_license_log
46
45
  else
47
- Fontist.log_level = :info
46
+ # Fontist.log_level = :info
47
+ # Shutting up Fontist 2.0's verbose logging of installation locations
48
+ Fontist.log_level = :fatal
48
49
  end
49
-
50
- Fontist::Manifest::Install.from_hash(
51
- manifest,
50
+ Fontist::Manifest.from_hash(manifest).install(
52
51
  confirmation: agree ? "yes" : "no",
53
52
  no_progress: no_progress,
54
- )
53
+ ).to_hash
55
54
  end
56
55
 
57
56
  def license_error_log(continue)
@@ -61,8 +60,8 @@ module Metanorma
61
60
  :debug,
62
61
  )
63
62
  else
64
- Util.log("[fontist] Aborting without proper fonts installed," \
65
- " make sure that you have set option --agree-to-terms",
63
+ Util.log("[fontist] Aborting without proper fonts installed, " \
64
+ "make sure that you have set option --agree-to-terms",
66
65
  :fatal)
67
66
  end
68
67
  end
@@ -92,7 +91,12 @@ module Metanorma
92
91
  return unless validate_install_fonts(processor, options)
93
92
 
94
93
  @@updated_formulas_repo = false
95
- manifest = processor.fonts_manifest.dup
94
+ manifest = if processor.respond_to?(:fonts_manifest) &&
95
+ !processor.fonts_manifest.nil?
96
+ processor.fonts_manifest.dup
97
+ else
98
+ {}
99
+ end
96
100
  append_source_fonts(manifest, options)
97
101
  agree_to_terms, can_without_fonts, no_progress = validate_options(options)
98
102
 
@@ -105,16 +109,28 @@ module Metanorma
105
109
  end
106
110
 
107
111
  def self.has_custom_fonts?(processor, options, source_attributes)
108
- install_fonts?(options) \
112
+ (install_fonts?(options) \
109
113
  && processor.respond_to?(:fonts_manifest) \
110
- && !processor.fonts_manifest.nil? \
114
+ && !processor.fonts_manifest.nil?) \
111
115
  || source_attributes[:fonts]
112
116
  end
113
117
 
114
118
  def self.location_manifest(processor, source_attributes)
115
- Fontist::Manifest::Locations.from_hash(
116
- append_source_fonts(processor.fonts_manifest.dup, source_attributes),
119
+ base = if processor.respond_to?(:fonts_manifest) &&
120
+ !processor.fonts_manifest.nil?
121
+ processor.fonts_manifest.dup
122
+ else
123
+ {}
124
+ end
125
+
126
+ return nil if base.empty? && !source_attributes[:fonts]
127
+
128
+ instance = Fontist::Manifest.from_hash(
129
+ append_source_fonts(base, source_attributes),
130
+ locations: true,
117
131
  )
132
+
133
+ instance.to_hash unless instance.nil?
118
134
  end
119
135
 
120
136
  def self.append_source_fonts(manifest, source_attributes)
@@ -1,3 +1,3 @@
1
1
  module Metanorma
2
- VERSION = "2.1.4".freeze
2
+ VERSION = "2.5.4".freeze
3
3
  end
data/lib/metanorma.rb CHANGED
@@ -5,13 +5,7 @@ class Array
5
5
  end
6
6
 
7
7
  require "metanorma/version"
8
- require "asciidoctor"
9
- require "metanorma/util/util"
10
- require "metanorma/config/config"
11
- require "metanorma/input"
12
- require "metanorma/registry/registry"
13
- require "metanorma/processor/processor"
14
- require "metanorma/asciidoctor_extensions"
8
+ require "metanorma-core"
15
9
  require "metanorma/compile/compile"
16
10
  require "metanorma/collection/collection"
17
11
  require "metanorma/collection/manifest/manifest"
data/metanorma.gemspec CHANGED
@@ -22,31 +22,22 @@ Gem::Specification.new do |spec|
22
22
  spec.bindir = "bin"
23
23
  # spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ["lib"]
25
- spec.required_ruby_version = ">= 3.1.0"
25
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.3.0")
26
26
 
27
27
  spec.add_runtime_dependency "asciidoctor"
28
28
  spec.add_runtime_dependency "concurrent-ruby"
29
- spec.add_runtime_dependency "fontist", ">= 1.14.3"
29
+ spec.add_runtime_dependency "fontist", ">= 2.1.2"
30
30
  spec.add_runtime_dependency "htmlentities"
31
- spec.add_runtime_dependency "isodoc", ">= 3.0.0"
31
+ spec.add_runtime_dependency "isodoc", ">= 3.5.0"
32
+ spec.add_runtime_dependency "marcel"
33
+ spec.add_runtime_dependency "metanorma-core"
34
+ spec.add_runtime_dependency "metanorma-standoc"
32
35
  spec.add_runtime_dependency "mn2pdf", "~> 2"
33
36
  spec.add_runtime_dependency "nokogiri"
34
-
35
- # get relaton-cli to avoid circular reference with metanorma-standoc
36
- # spec.add_dependency "relaton-cli"
37
- # spec.add_dependency "metanorma-standoc"
38
-
39
- spec.add_development_dependency "debug"
40
- spec.add_development_dependency "equivalent-xml", "~> 0.6"
37
+ spec.add_development_dependency "canon" #, "= 0.2.3"
38
+ spec.add_development_dependency "metanorma-iho"
41
39
  spec.add_development_dependency "metanorma-iso"
42
- spec.add_development_dependency "mnconvert"
43
- spec.add_development_dependency "pry"
44
- spec.add_development_dependency "rake", "~> 13.0"
45
- spec.add_development_dependency "rspec", "~> 3.0"
46
- spec.add_development_dependency "rspec-command", "~> 1.0"
47
- spec.add_development_dependency "rubocop", "~> 1"
48
- spec.add_development_dependency "rubocop-performance"
49
- spec.add_development_dependency "sassc-embedded", "~> 1"
50
- spec.add_development_dependency "simplecov", "~> 0.15"
51
- spec.add_development_dependency "xml-c14n"
40
+
41
+ # relaton-cli is required by Metanorma::Collection
42
+ spec.add_dependency "relaton-cli"
52
43
  end