Class: Metanorma::Compile

Inherits:
Object
  • Object
show all
Defined in:
lib/metanorma/compile.rb

Constant Summary collapse

REQUIREMENT_XPATH =
"//requirement | //xmlns:requirement | "\
"//recommendation | //xmlns:recommendation | //permission | "\
"//xmlns:permission".freeze

Instance Method Summary collapse

Constructor Details

#initializeCompile

Returns a new instance of Compile.



7
8
9
# File 'lib/metanorma/compile.rb', line 7

def initialize
  @registry = Metanorma::Registry.instance
end

Instance Method Details

#clean_sourcecode(xml) ⇒ Object



122
123
124
125
126
127
128
# File 'lib/metanorma/compile.rb', line 122

def clean_sourcecode(xml)
  xml.xpath(".//callout | .//annotation | .//xmlns:callout | .//xmlns:annotation").each do |x|
    x.remove
  end
  xml.xpath(".//br | .//xmlns:br").each { |x| x.replace("\n") }
  HTMLEntities.new.decode(xml.children.to_xml)
end

#compile(filename, options = {}) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
# File 'lib/metanorma/compile.rb', line 11

def compile(filename, options = {})
  options = options_extract(filename, options)
  validate(options) or return nil
  require_libraries(options)
  @processor = @registry.find_processor(options[:type].to_sym)
  extensions = get_extensions(options) or return nil
  (file, isodoc = process_input(filename, options)) or return nil
  relaton_export(isodoc, options)
  extract(isodoc, options[:extract], options[:extract_type])
  process_extensions(extensions, file, isodoc, options)
end

#extract(isodoc, dirname, extract_types) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/metanorma/compile.rb', line 130

def extract(isodoc, dirname, extract_types)
  return unless dirname
  if extract_types.nil? || extract_types.empty?
    extract_types = [:sourcecode, :image, :requirement]
  end
  FileUtils.rm_rf dirname
  FileUtils.mkdir_p dirname
  xml = Nokogiri::XML(isodoc)
  sourcecode_export(xml, dirname) if extract_types.include? :sourcecode
  image_export(xml, dirname) if extract_types.include? :image
  requirement_export(xml, dirname) if extract_types.include? :requirement
end

#get_extensions(options) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
# File 'lib/metanorma/compile.rb', line 79

def get_extensions(options)
  options[:extension_keys] ||= @processor.output_formats.inject([]) do |memo, (k, _)|
    memo << k; memo
  end
  extensions = options[:extension_keys].inject([]) do |memo, e|
    @processor.output_formats[e] and memo << e or
      Util.log("[metanorma] Error: #{e} format is not supported for this standard.", :error)
    memo
  end
  extensions
end

#image_export(xml, dirname) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/metanorma/compile.rb', line 154

def image_export(xml, dirname)
  xml.at("//image | //xmlns:image") or return
  FileUtils.mkdir_p "#{dirname}/image"
  xml.xpath("//image | //xmlns:image").each_with_index do |s, i|
    next unless /^data:image/.match s["src"]
    %r{^data:image/(?<imgtype>[^;]+);base64,(?<imgdata>.+)$} =~ s["src"]
    filename = s["filename"] || sprintf("image-%04d.%s", i, imgtype)
    File.open("#{dirname}/image/#{filename}", "wb") do |f|
      f.write(Base64.strict_decode64(imgdata))
    end
  end
end

#options_extract(filename, options) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/metanorma/compile.rb', line 31

def options_extract(filename, options)
  o = Metanorma::Input::Asciidoc.new.extract_metanorma_options(File.read(filename, encoding: "utf-8"))
  options[:type] ||= o[:type]&.to_sym
  dir = filename.sub(%r(/[^/]+$), "/")
  options[:relaton] ||= "#{dir}/#{o[:relaton]}" if o[:relaton]
  options[:sourcecode] ||= "#{dir}/#{o[:sourcecode]}" if o[:sourcecode]
  options[:extension_keys] ||= o[:extensions]&.split(/,[ ]*/)&.map(&:to_sym)
  options[:extension_keys] = nil if options[:extension_keys] == [:all]
  options[:format] ||= :asciidoc
  options[:filename] = filename
  options
end

#process_extensions(extensions, file, isodoc, options) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/metanorma/compile.rb', line 182

def process_extensions(extensions, file, isodoc, options)
  extensions.each do |ext|
    isodoc_options = @processor.extract_options(file)
    isodoc_options[:datauriimage] = true if options[:datauriimage]
    file_extension = @processor.output_formats[ext]
    outfilename = options[:filename].sub(/\.[^.]+$/, ".#{file_extension}")
    if ext == :rxl
      options[:relaton] = outfilename
      relaton_export(isodoc, options)
    else
      @processor.output(isodoc, outfilename, ext, isodoc_options)
    end
    if options[:wrapper] and /html$/.match file_extension
      outfilename = outfilename.sub(/\.html$/, "")
      FileUtils.mkdir_p outfilename
      FileUtils.mv "#{outfilename}.html", outfilename
      FileUtils.mv "#{outfilename}_images", outfilename, force: true
    end
  end
end

#process_input(filename, options) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/metanorma/compile.rb', line 91

def process_input(filename, options)
  case extname = File.extname(filename)
  when ".adoc"
    Util.log("[metanorma] Processing: Asciidoctor input.", :info)
    file = File.read(filename, encoding: "utf-8")
    options[:asciimath] and
      file.sub!(/^(=[^\n]+\n)/, "\\1:mn-keep-asciimath:\n")
    dir = File.dirname(filename)
    dir != '.' and
      file.gsub!(/^include::/, "include::#{dir}/")
    [file, @processor.input_to_isodoc(file, filename)]
  when ".xml"
    Util.log("[metanorma] Processing: Metanorma XML input.", :info)
    # TODO NN: this is a hack -- we should provide/bridge the
    # document attributes in Metanorma XML
    ["", File.read(filename, encoding: "utf-8")]
  else
    Util.log("[metanorma] Error: file extension #{extname} is not supported.", :error)
    nil
  end
end

#relaton_export(isodoc, options) ⇒ Object



113
114
115
116
117
118
119
120
# File 'lib/metanorma/compile.rb', line 113

def relaton_export(isodoc, options)
  return unless options[:relaton]
  xml = Nokogiri::XML(isodoc)
  bibdata = xml.at("//bibdata") || xml.at("//xmlns:bibdata")
  #docid = bibdata&.at("./xmlns:docidentifier")&.text || options[:filename]
  #outname = docid.sub(/^\s+/, "").sub(/\s+$/, "").gsub(/\s+/, "-") + ".xml"
  File.open(options[:relaton], "w:UTF-8") { |f| f.write bibdata.to_xml }
end

#require_libraries(options) ⇒ Object



23
24
25
26
27
28
29
# File 'lib/metanorma/compile.rb', line 23

def require_libraries(options)
  if options[:require]
    options[:require].each do |r|
      require r
    end
  end
end

#requirement_export(xml, dirname) ⇒ Object



171
172
173
174
175
176
177
178
179
180
# File 'lib/metanorma/compile.rb', line 171

def requirement_export(xml, dirname)
  xml.at(REQUIREMENT_XPATH) or return
  FileUtils.mkdir_p "#{dirname}/requirement"
  xml.xpath(REQUIREMENT_XPATH).each_with_index do |s, i|
    filename = s["filename"] || sprintf("%s-%04d.xml", s.name, i)
    File.open("#{dirname}/requirement/#{filename}", "w:UTF-8") do |f|
      f.write s
    end
  end
end

#sourcecode_export(xml, dirname) ⇒ Object



143
144
145
146
147
148
149
150
151
152
# File 'lib/metanorma/compile.rb', line 143

def sourcecode_export(xml, dirname)
  xml.at("//sourcecode | //xmlns:sourcecode") or return
  FileUtils.mkdir_p "#{dirname}/sourcecode"
  xml.xpath("//sourcecode | //xmlns:sourcecode").each_with_index do |s, i|
    filename = s["filename"] || sprintf("sourcecode-%04d.txt", i)
    File.open("#{dirname}/sourcecode/#{filename}", "w:UTF-8") do |f|
      f.write clean_sourcecode(s.dup) 
    end
  end
end

#validate(options) ⇒ Object



44
45
46
# File 'lib/metanorma/compile.rb', line 44

def validate(options)
  validate_type(options) && validate_format(options)
end

#validate_format(options) ⇒ Object



71
72
73
74
75
76
77
# File 'lib/metanorma/compile.rb', line 71

def validate_format(options)
  unless options[:format] == :asciidoc
    Util.log("[metanorma] Error: Only source file format currently supported is 'asciidoc'.", :error)
    return false
  end
  true
end

#validate_type(options) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/metanorma/compile.rb', line 48

def validate_type(options)
  unless options[:type]
    Util.log("[metanorma] Error: Please specify a standard type: #{@registry.supported_backends}.", :error)
    return nil
  end
  stdtype = options[:type].to_sym
  unless @registry.supported_backends.include? stdtype
    Util.log("[metanorma] Info: Loading `metanorma-#{stdtype}` gem for standard type `#{stdtype}`.", :info)
  end
  begin
    require "metanorma-#{stdtype}"
    Util.log("[metanorma] Info: gem `metanorma-#{stdtype}` loaded.", :info)
  rescue LoadError
    Util.log("[metanorma] Error: loading gem `metanorma-#{stdtype}` failed. Exiting.", :error)
    return false
  end
  unless @registry.supported_backends.include? stdtype
    Util.log("[metanorma] Error: The `metanorma-#{stdtype}` gem still doesn't support `#{stdtype}`. Exiting.", :error)
    return false
  end
  true
end