lib/vectory/utils.rb in vectory-0.5.0 vs lib/vectory/utils.rb in vectory-0.6.0
- old
+ new
@@ -1,117 +1,99 @@
# frozen_string_literal: true
-require "timeout"
+require "base64"
+require "marcel"
+require "tempfile"
module Vectory
class Utils
class << self
- # rubocop:disable all
+ # Extracted from https://github.com/metanorma/metanorma-utils/blob/v1.5.2/lib/utils/image.rb
#
- # Originally from https://gist.github.com/pasela/9392115
- #
- # Capture the standard output and the standard error of a command.
- # Almost same as Open3.capture3 method except for timeout handling and return value.
- # See Open3.capture3.
- #
- # result = capture3_with_timeout([env,] cmd... [, opts])
- #
- # The arguments env, cmd and opts are passed to Process.spawn except
- # opts[:stdin_data], opts[:binmode], opts[:timeout], opts[:signal]
- # and opts[:kill_after]. See Process.spawn.
- #
- # If opts[:stdin_data] is specified, it is sent to the command's standard input.
- #
- # If opts[:binmode] is true, internal pipes are set to binary mode.
- #
- # If opts[:timeout] is specified, SIGTERM is sent to the command after specified seconds.
- #
- # If opts[:signal] is specified, it is used instead of SIGTERM on timeout.
- #
- # If opts[:kill_after] is specified, also send a SIGKILL after specified seconds.
- # it is only sent if the command is still running after the initial signal was sent.
- #
- # The return value is a Hash as shown below.
- #
- # {
- # :pid => PID of the command,
- # :status => Process::Status of the command,
- # :stdout => the standard output of the command,
- # :stderr => the standard error of the command,
- # :timeout => whether the command was timed out,
- # }
- def capture3_with_timeout(*cmd)
- spawn_opts = Hash === cmd.last ? cmd.pop.dup : {}
- opts = {
- :stdin_data => spawn_opts.delete(:stdin_data) || "",
- :binmode => spawn_opts.delete(:binmode) || false,
- :timeout => spawn_opts.delete(:timeout),
- :signal => spawn_opts.delete(:signal) || :TERM,
- :kill_after => spawn_opts.delete(:kill_after),
- }
+ # sources/plantuml/plantuml20200524-90467-1iqek5i.png
+ # already includes localdir
+ # Check whether just the local path or the other specified relative path
+ # works.
+ def datauri(uri, local_dir = ".")
+ (datauri?(uri) || url?(uri)) and return uri
- in_r, in_w = IO.pipe
- out_r, out_w = IO.pipe
- err_r, err_w = IO.pipe
- in_w.sync = true
+ path = path_which_exist(uri, local_dir)
+ path and return encode_datauri(path)
- if opts[:binmode]
- in_w.binmode
- out_r.binmode
- err_r.binmode
+ warn "Image specified at `#{uri}` does not exist."
+ uri # Return original provided location
+ end
+
+ def path_which_exist(uri, local_dir)
+ options = absolute_path?(uri) ? [uri] : [uri, File.join(local_dir, uri)]
+ options.detect do |p|
+ File.file?(p)
end
+ end
- spawn_opts[:in] = in_r
- spawn_opts[:out] = out_w
- spawn_opts[:err] = err_w
+ def encode_datauri(path)
+ return nil unless File.exist?(path)
- result = {
- :pid => nil,
- :status => nil,
- :stdout => nil,
- :stderr => nil,
- :timeout => false,
- }
+ type = Marcel::MimeType.for(Pathname.new(path)) ||
+ 'text/plain; charset="utf-8"'
- out_reader = nil
- err_reader = nil
- wait_thr = nil
+ bin = File.binread(path)
+ data = Base64.strict_encode64(bin)
+ "data:#{type};base64,#{data}"
+ # rescue StandardError
+ # warn "Data-URI encoding of `#{path}` failed."
+ # nil
+ end
- begin
- Timeout.timeout(opts[:timeout]) do
- result[:pid] = spawn(*cmd, spawn_opts)
- wait_thr = Process.detach(result[:pid])
- in_r.close
- out_w.close
- err_w.close
+ def datauri?(uri)
+ /^data:/.match?(uri)
+ end
- out_reader = Thread.new { out_r.read }
- err_reader = Thread.new { err_r.read }
+ def url?(url)
+ %r{^[A-Z]{2,}://}i.match?(url)
+ end
- in_w.write opts[:stdin_data]
- in_w.close
+ def absolute_path?(uri)
+ %r{^/}.match?(uri) || %r{^[A-Z]:/}.match?(uri)
+ end
- result[:status] = wait_thr.value
- end
- rescue Timeout::Error
- result[:timeout] = true
- pid = spawn_opts[:pgroup] ? -result[:pid] : result[:pid]
- Process.kill(opts[:signal], pid)
- if opts[:kill_after]
- unless wait_thr.join(opts[:kill_after])
- Process.kill(:KILL, pid)
- end
- end
- ensure
- result[:status] = wait_thr.value if wait_thr
- result[:stdout] = out_reader.value if out_reader
- result[:stderr] = err_reader.value if err_reader
- out_r.close unless out_r.closed?
- err_r.close unless err_r.closed?
+ def svgmap_rewrite0_path(src, localdirectory)
+ if /^data:/.match?(src)
+ save_dataimage(src)
+ else
+ File.file?(src) ? src : localdirectory + src
end
+ end
- result
+ def save_dataimage(uri)
+ %r{^data:(?:image|application)/(?<imgtype>[^;]+);(?:charset=[^;]+;)?base64,(?<imgdata>.+)$} =~ uri # rubocop:disable Layout/LineLength
+ imgtype.sub!(/\+[a-z0-9]+$/, "") # svg+xml
+ imgtype = "png" unless /^[a-z0-9]+$/.match? imgtype
+ Tempfile.open(["image", ".#{imgtype}"]) do |f|
+ f.binmode
+ f.write(Base64.strict_decode64(imgdata))
+ f.path
+ end
end
- # rubocop:enable all
+
+ # FIXME: This method should ONLY return 1 type, remove Array wrapper
+ def datauri2mime(uri)
+ output = decode_datauri(uri)
+ return nil unless output && output[:type_detected]
+
+ [output[:type_detected]]
+ end
+
+ def decode_datauri(uri)
+ %r{^data:(?<mimetype>[^;]+);base64,(?<mimedata>.+)$} =~ uri
+ return nil unless mimetype && mimedata
+
+ data = Base64.strict_decode64(mimedata)
+ {
+ type_declared: mimetype,
+ type_detected: Marcel::MimeType.for(data, declared_type: mimetype),
+ data: data,
+ }
+ end
end
end
end