lib/vectory/inkscape_converter.rb
# frozen_string_literal: true
require "singleton"
require "tmpdir"
require_relative "system_call"
module Vectory
class InkscapeConverter
include Singleton
def self.convert(uri, output_extension, option)
instance.convert(uri, output_extension, option)
end
def convert(uri, output_extension, option)
exe = inkscape_path_or_raise_error
uri = external_path uri
exe = external_path exe
cmd = %(#{exe} #{option} #{uri})
call = SystemCall.new(cmd).call
output_path = find_output(uri, output_extension)
raise_conversion_error(call) unless output_path
output_path
end
def height(content, format)
query_integer(content, format, "--query-height")
end
def width(content, format)
query_integer(content, format, "--query-width")
end
private
def inkscape_path_or_raise_error
inkscape_path or raise(InkscapeNotFoundError,
"Inkscape missing in PATH, unable to " \
"convert image. Aborting.")
end
def inkscape_path
@inkscape_path ||= find_inkscape
end
def find_inkscape
cmds.each do |cmd|
extensions.each do |ext|
paths.each do |path|
exe = File.join(path, "#{cmd}#{ext}")
return exe if File.executable?(exe) && !File.directory?(exe)
end
end
end
nil
end
def cmds
["inkscapecom", "inkscape"]
end
def extensions
ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
end
def paths
ENV["PATH"].split(File::PATH_SEPARATOR)
end
def find_output(source_path, output_extension)
basenames = [File.basename(source_path, ".*"),
File.basename(source_path)]
paths = basenames.map do |basename|
"#{File.join(File.dirname(source_path), basename)}.#{output_extension}"
end
paths.find { |p| File.exist?(p) }
end
def raise_conversion_error(call)
raise Vectory::ConversionError,
"Could not convert with Inkscape. " \
"Inkscape cmd: '#{call.cmd}',\n" \
"status: '#{call.status}',\n" \
"stdout: '#{call.stdout.strip}',\n" \
"stderr: '#{call.stderr.strip}'."
end
def external_path(path)
win = !!((RUBY_PLATFORM =~ /(win|w)(32|64)$/) ||
(RUBY_PLATFORM =~ /mswin|mingw/))
if win
path.gsub!(%{/}, "\\")
path[/\s/] ? "\"#{path}\"" : path
else
path
end
end
def query_integer(content, format, options)
query(content, format, options).to_f.round
end
def query(content, format, options)
exe = inkscape_path_or_raise_error
with_file(content, format) do |path|
cmd = "#{external_path(exe)} #{options} #{external_path(path)}"
call = SystemCall.new(cmd).call
raise_query_error(call) if call.stdout.empty?
call.stdout
end
end
def with_file(content, extension)
Dir.mktmpdir do |dir|
path = File.join(dir, "image.#{extension}")
File.binwrite(path, content)
yield path
end
end
def raise_query_error(call)
raise Vectory::InkscapeQueryError,
"Could not query with Inkscape. " \
"Inkscape cmd: '#{call.cmd}',\n" \
"status: '#{call.status}',\n" \
"stdout: '#{call.stdout.strip}',\n" \
"stderr: '#{call.stderr.strip}'."
end
end
end