vectory 0.1.0 → 0.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/.github/workflows/rake.yml +1 -1
- data/.github/workflows/release.yml +24 -0
- data/.gitignore +4 -0
- data/README.adoc +157 -1
- data/Rakefile +4 -0
- data/bin/vectory +9 -0
- data/emf.emf +1 -0
- data/lib/vectory/cli.rb +140 -0
- data/lib/vectory/datauri.rb +48 -0
- data/lib/vectory/emf.rb +31 -0
- data/lib/vectory/eps.rb +31 -0
- data/lib/vectory/file_magic.rb +53 -0
- data/lib/vectory/image.rb +23 -0
- data/lib/vectory/inkscape_converter.rb +72 -0
- data/lib/vectory/ps.rb +25 -0
- data/lib/vectory/svg.rb +27 -0
- data/lib/vectory/system_call.rb +49 -0
- data/lib/vectory/utils.rb +54 -0
- data/lib/vectory/vector.rb +74 -0
- data/lib/vectory/version.rb +1 -1
- data/lib/vectory.rb +34 -1
- data/spec/examples/emf2eps/img.emf +0 -0
- data/spec/examples/emf2eps/img.emf.datauri +1 -0
- data/spec/examples/emf2eps/img.eps +88 -0
- data/spec/examples/emf2ps/img.emf +0 -0
- data/spec/examples/emf2ps/img.ps +125 -0
- data/spec/examples/emf2svg/img.emf +0 -0
- data/spec/examples/emf2svg/img.svg +9 -0
- data/spec/examples/eps2emf/img.emf +0 -0
- data/spec/examples/eps2emf/img.eps +199 -0
- data/spec/examples/eps2emf/img.eps.datauri +1 -0
- data/spec/examples/eps2ps/img.eps +199 -0
- data/spec/examples/eps2ps/img.ps +549 -0
- data/spec/examples/eps2svg/img.eps +199 -0
- data/spec/examples/eps2svg/img.svg +173 -0
- data/spec/examples/eps_but_svg_extension.svg +199 -0
- data/spec/examples/img.jpg +0 -0
- data/spec/examples/ps2emf/img.emf +0 -0
- data/spec/examples/ps2emf/img.ps +549 -0
- data/spec/examples/ps2emf/img.ps.datauri +1 -0
- data/spec/examples/ps2eps/img.eps +844 -0
- data/spec/examples/ps2eps/img.ps +549 -0
- data/spec/examples/ps2svg/img.ps +549 -0
- data/spec/examples/ps2svg/img.svg +476 -0
- data/spec/examples/svg2emf/img.emf +0 -0
- data/spec/examples/svg2emf/img.svg +1 -0
- data/spec/examples/svg2emf/img.svg.datauri +1 -0
- data/spec/examples/svg2eps/img.eps +88 -0
- data/spec/examples/svg2eps/img.svg +1 -0
- data/spec/examples/svg2ps/img.ps +125 -0
- data/spec/examples/svg2ps/img.svg +1 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/matchers.rb +39 -0
- data/spec/support/text_matcher.rb +63 -0
- data/spec/support/vectory_helper.rb +17 -0
- data/spec/vectory/cli_spec.rb +214 -0
- data/spec/vectory/datauri_spec.rb +101 -0
- data/spec/vectory/emf_spec.rb +38 -0
- data/spec/vectory/eps_spec.rb +40 -0
- data/spec/vectory/file_magic_spec.rb +24 -0
- data/spec/vectory/inkscape_converter_spec.rb +38 -0
- data/spec/vectory/ps_spec.rb +33 -0
- data/spec/vectory/svg_spec.rb +33 -0
- data/spec/vectory/vector_spec.rb +60 -0
- data/tmp/.keep +0 -0
- data/vectory.gemspec +10 -3
- metadata +163 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c46e34eee9accc66e136b741440e46269744e0f9cf8a1125382fe3ec1959b320
|
4
|
+
data.tar.gz: 95a3dcf51104cc31ab37af1b0ecc39861c7717399056b0116cfd94562a66126a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8775fc8bda0a86df3bc2c4bde5ab303d861a6d5812764f8b2c40a540a0f0369a42b55cd1bac655acc3ccf37348aa976620709ae41c35d26680ae221579e416e9
|
7
|
+
data.tar.gz: c880d981f9c7a26182bacf9a0ee0d0df0f5147d493f79de45bade50e813c25d258400157be70b8987f605b3d4a9b4c3217b7974d40da174503de6e074183e573
|
data/.github/workflows/rake.yml
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Auto-generated by Cimas: Do not edit it manually!
|
2
|
+
# See https://github.com/metanorma/cimas
|
3
|
+
name: release
|
4
|
+
|
5
|
+
on:
|
6
|
+
workflow_dispatch:
|
7
|
+
inputs:
|
8
|
+
next_version:
|
9
|
+
description: |
|
10
|
+
Next release version. Possible values: x.y.z, major, minor, patch or pre|rc|etc
|
11
|
+
required: true
|
12
|
+
default: 'skip'
|
13
|
+
push:
|
14
|
+
tags: [ v* ]
|
15
|
+
|
16
|
+
jobs:
|
17
|
+
release:
|
18
|
+
uses: metanorma/ci/.github/workflows/rubygems-release.yml@main
|
19
|
+
with:
|
20
|
+
next_version: ${{ github.event.inputs.next_version }}
|
21
|
+
secrets:
|
22
|
+
rubygems-api-key: ${{ secrets.METANORMA_CI_RUBYGEMS_API_KEY }}
|
23
|
+
pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}
|
24
|
+
|
data/.gitignore
ADDED
data/README.adoc
CHANGED
@@ -11,6 +11,11 @@ image:https://img.shields.io/github/commits-since/metanorma/vectory/latest.svg["
|
|
11
11
|
Vectory is a Ruby gem that performs pairwise vector image conversions for common
|
12
12
|
vector image formats (EPS, PS, EMF, SVG).
|
13
13
|
|
14
|
+
[quote]
|
15
|
+
____
|
16
|
+
Vectory shall give you a glorious vectory over EPS files.
|
17
|
+
____
|
18
|
+
|
14
19
|
|
15
20
|
== Installation
|
16
21
|
|
@@ -20,6 +25,7 @@ Vectory relies on the following software to be installed:
|
|
20
25
|
|
21
26
|
* https://github.com/metanorma/emf2svg-ruby[emf2svg-ruby]
|
22
27
|
* https://inkscape.org[Inkscape]
|
28
|
+
* https://www.ghostscript.com/[Ghostscript]
|
23
29
|
|
24
30
|
|
25
31
|
=== Gem install
|
@@ -43,7 +49,157 @@ Where,
|
|
43
49
|
|
44
50
|
`format`:: the desired output format (one of: `svg`, `eps`, `ps`, `emf`)
|
45
51
|
`input-file-name`:: file path to the input file
|
46
|
-
`output-file-name`:: file path to the desired output file (with the
|
52
|
+
`output-file-name`:: file path to the desired output file (with the
|
53
|
+
file extension) (default: writes to a current directory by the input filename
|
54
|
+
with an extension changed to a desired format)
|
55
|
+
|
56
|
+
|
57
|
+
=== Using the Ruby library
|
58
|
+
|
59
|
+
Some examples:
|
60
|
+
|
61
|
+
Take EMF as a path to a file and return SVG as a string:
|
62
|
+
|
63
|
+
[source,ruby]
|
64
|
+
----
|
65
|
+
path = "path/to/file.emf"
|
66
|
+
|
67
|
+
Vectory::Emf.from_path(path).to_svg.content
|
68
|
+
----
|
69
|
+
|
70
|
+
Take EPS as a string and return EMF as a path to a file:
|
71
|
+
|
72
|
+
[source,ruby]
|
73
|
+
----
|
74
|
+
# NOTE: content is shortened for readability
|
75
|
+
content = "%!PS-Adobe-3.0 EPSF-3.0\n ... %%Trailer"
|
76
|
+
|
77
|
+
Vectory::Eps.from_content(content).to_emf.write.path
|
78
|
+
----
|
79
|
+
|
80
|
+
Take SVG as a datauri and return EMF as a datauri:
|
81
|
+
|
82
|
+
[source,ruby]
|
83
|
+
----
|
84
|
+
# NOTE: datauri is shortened for readability
|
85
|
+
uri = "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0 ... GRkYiLz48L3N2Zz4="
|
86
|
+
|
87
|
+
Vectory::Datauri.new(uri).to_vector.to_emf.to_uri.content
|
88
|
+
----
|
89
|
+
|
90
|
+
|
91
|
+
==== What is supported?
|
92
|
+
|
93
|
+
There are several vector classes which support conversion between each other:
|
94
|
+
|
95
|
+
[source,ruby]
|
96
|
+
----
|
97
|
+
Vectory::Eps
|
98
|
+
Vectory::Ps
|
99
|
+
Vectory::Emf
|
100
|
+
Vectory::Svg
|
101
|
+
----
|
102
|
+
|
103
|
+
Each of them can be instantiated in several ways:
|
104
|
+
|
105
|
+
[source,ruby]
|
106
|
+
----
|
107
|
+
Vectory::Eps.from_path("images/img.eps")
|
108
|
+
Vectory::Eps.from_content("%!PS-Adobe-3.0...")
|
109
|
+
----
|
110
|
+
|
111
|
+
Converting to other formats:
|
112
|
+
|
113
|
+
[source,ruby]
|
114
|
+
----
|
115
|
+
Vectory::Eps.from_content(content).to_ps
|
116
|
+
Vectory::Eps.from_content(content).to_emf
|
117
|
+
Vectory::Eps.from_content(content).to_svg
|
118
|
+
----
|
119
|
+
|
120
|
+
Several ways of getting content of an object:
|
121
|
+
|
122
|
+
[source,ruby]
|
123
|
+
----
|
124
|
+
Vectory::Eps.from_content(content).to_svg.content
|
125
|
+
Vectory::Eps.from_content(content).to_svg.to_uri.content # as datauri
|
126
|
+
Vectory::Eps.from_content(content).to_svg.write.path
|
127
|
+
----
|
128
|
+
|
129
|
+
|
130
|
+
==== Datauri
|
131
|
+
|
132
|
+
Also there is the `Vectory::Datauri` class which represents vectory images in
|
133
|
+
the datauri format.
|
134
|
+
|
135
|
+
Convert an SVG datauri to a plain SVG:
|
136
|
+
|
137
|
+
[source,ruby]
|
138
|
+
----
|
139
|
+
# NOTE: datauri is shortened for readability
|
140
|
+
uri = "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0 ... GRkYiLz48L3N2Zz4="
|
141
|
+
Vectory::Datauri.new(uri).to_vector.content
|
142
|
+
----
|
143
|
+
|
144
|
+
Convert an EPS file to its datauri representation:
|
145
|
+
|
146
|
+
[source,ruby]
|
147
|
+
----
|
148
|
+
eps = Vectory::Eps.from_path("img.eps")
|
149
|
+
Vectory::Datauri.from_vector(eps).content
|
150
|
+
----
|
151
|
+
|
152
|
+
There is also a simplified API for this case:
|
153
|
+
|
154
|
+
[source,ruby]
|
155
|
+
----
|
156
|
+
Vectory::Eps.from_path("img.eps").to_uri.content
|
157
|
+
----
|
158
|
+
|
159
|
+
|
160
|
+
==== File system operations
|
161
|
+
|
162
|
+
An image object contains information where it is written. It can be obtained
|
163
|
+
with the `#path` API:
|
164
|
+
|
165
|
+
[source,ruby]
|
166
|
+
----
|
167
|
+
vector = Vectory::Eps.from_path("img.eps")
|
168
|
+
vector.path
|
169
|
+
----
|
170
|
+
|
171
|
+
Before the first write it raises the `NotWrittenToDiskError` error:
|
172
|
+
|
173
|
+
[source,ruby]
|
174
|
+
----
|
175
|
+
vector.path # => raise NotWrittenToDiskError
|
176
|
+
----
|
177
|
+
|
178
|
+
After writing it returns a path of the image on a disk:
|
179
|
+
|
180
|
+
[source,ruby]
|
181
|
+
----
|
182
|
+
vector.write
|
183
|
+
vector.path # => "/tmp/xxx/yyy"
|
184
|
+
----
|
185
|
+
|
186
|
+
By default it writes to a temporary directory but it can be changed by
|
187
|
+
providing an argument with a desired path:
|
188
|
+
|
189
|
+
[source,ruby]
|
190
|
+
----
|
191
|
+
vector.write("images/img.eps")
|
192
|
+
vector.path # => "images/img.eps"
|
193
|
+
----
|
194
|
+
|
195
|
+
Since an image can be initially read from a disk, it also keeps an initial
|
196
|
+
path. To avoid accidental overwrite, this path is used only for read-only
|
197
|
+
purposes.
|
198
|
+
|
199
|
+
[source,ruby]
|
200
|
+
----
|
201
|
+
vector.initial_path # => "storage/images/img.eps"
|
202
|
+
----
|
47
203
|
|
48
204
|
|
49
205
|
== Contributing
|
data/Rakefile
CHANGED
data/bin/vectory
ADDED
data/emf.emf
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
AQAAAMgAAAAAAAAAAAAAAPsEAAD7BAAAAAAAAAAAAACLCgAAiwoAACBFTUYAAAEAJAQAACgAAAACAAAALgAAAGwAAAAAAAAA3ScAAH0zAADYAAAAFwEAAAAAAAAAAAAAAAAAAMBLAwDYQQQASQBuAGsAcwBjAGEAcABlACAAMQAuADAAIAAoADQAMAAzADUAYQA0AGYALAAgADIAMAAyADAALQAwADUALQAwADEAKQAgAAAAbwBkAGYALgBlAG0AZgAAAAAAAAARAAAADAAAAAEAAAAkAAAAJAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAIAAABGAAAALAAAACAAAABTY3JlZW49MTAyMDV4MTMxODFweCwgMjE2eDI3OW1tAEYAAAAwAAAAIwAAAERyYXdpbmc9MTAwLjB4MTAwLjBweCwgMjYuNXgyNi41bW0AABIAAAAMAAAAAQAAABMAAAAMAAAAAgAAABYAAAAMAAAAGAAAABgAAAAMAAAAAAAAABQAAAAMAAAADQAAACcAAAAYAAAAAQAAAAAAAAAAAJkABgAAACUAAAAMAAAAAQAAADsAAAAIAAAAGwAAABAAAACkBAAAcQIAAAUAAAA0AAAAAAAAAAAAAAD//////////wMAAACkBAAAqAMAAKgDAACkBAAAcQIAAKQEAAAFAAAANAAAAAAAAAAAAAAA//////////8DAAAAOgEAAKQEAAA/AAAAqAMAAD8AAABxAgAABQAAADQAAAAAAAAAAAAAAP//////////AwAAAD8AAAA6AQAAOgEAAD8AAABxAgAAPwAAAAUAAAA0AAAAAAAAAAAAAAD//////////wMAAACoAwAAPwAAAKQEAAA6AQAApAQAAHECAAA9AAAACAAAADwAAAAIAAAAPgAAABgAAAAAAAAAAAAAAP//////////JQAAAAwAAAAFAACAKAAAAAwAAAABAAAAJwAAABgAAAABAAAAAAAAAP///wAGAAAAJQAAAAwAAAABAAAAOwAAAAgAAAAbAAAAEAAAAJ0BAABFAQAANgAAABAAAADPAwAARQEAAAUAAAA0AAAAAAAAAAAAAAD//////////wMAAABfBAAA7QEAAGQEAADjAgAA2wMAAJEDAAAFAAAANAAAAAAAAAAAAAAA//////////8DAAAAUgMAAD4EAABhAgAAcwQAAJ0BAAAOBAAANgAAABAAAACdAQAAyQIAADYAAAAQAAAA4gIAAMkCAAA2AAAAEAAAAOICAAAaAgAANgAAABAAAACdAQAAGgIAAD0AAAAIAAAAPAAAAAgAAAA+AAAAGAAAAAAAAAAAAAAA//////////8lAAAADAAAAAUAAIAoAAAADAAAAAEAAAAOAAAAFAAAAAAAAAAAAAAAJAQAAA==
|
data/lib/vectory/cli.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require "thor"
|
2
|
+
require_relative "../vectory"
|
3
|
+
require_relative "file_magic"
|
4
|
+
|
5
|
+
module Vectory
|
6
|
+
class CLI < Thor
|
7
|
+
STATUS_SUCCESS = 0
|
8
|
+
STATUS_UNKNOWN_ERROR = 1
|
9
|
+
STATUS_UNSUPPORTED_INPUT_FORMAT_ERROR = 2
|
10
|
+
STATUS_UNSUPPORTED_OUTPUT_FORMAT_ERROR = 3
|
11
|
+
STATUS_CONVERSION_ERROR = 4
|
12
|
+
STATUS_SYSTEM_CALL_ERROR = 5
|
13
|
+
STATUS_INKSCAPE_NOT_FOUND_ERROR = 6
|
14
|
+
|
15
|
+
MAP_ERROR_TO_STATUS = {
|
16
|
+
Vectory::ConversionError => STATUS_CONVERSION_ERROR,
|
17
|
+
Vectory::InkscapeNotFoundError => STATUS_INKSCAPE_NOT_FOUND_ERROR,
|
18
|
+
Vectory::SystemCallError => STATUS_SYSTEM_CALL_ERROR,
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
module SupportedInputFormats
|
22
|
+
EPS = :eps
|
23
|
+
PS = :ps
|
24
|
+
SVG = :svg
|
25
|
+
EMF = :emf
|
26
|
+
|
27
|
+
def self.all
|
28
|
+
constants.map { |x| const_get(x) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module SupportedOutputFormats
|
33
|
+
EPS = "eps".freeze
|
34
|
+
PS = "ps".freeze
|
35
|
+
SVG = "svg".freeze
|
36
|
+
EMF = "emf".freeze
|
37
|
+
|
38
|
+
def self.all
|
39
|
+
constants.map { |x| const_get(x) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.exit_on_failure?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "convert INPUT_FILE_NAME",
|
48
|
+
"Perform pairwise vector image conversions for common vector image " \
|
49
|
+
"formats (EPS, PS, EMF, SVG)"
|
50
|
+
option :format,
|
51
|
+
aliases: :f,
|
52
|
+
required: true,
|
53
|
+
desc: "the desired output format (one of: svg, eps, ps, emf)"
|
54
|
+
|
55
|
+
option :output,
|
56
|
+
aliases: :o,
|
57
|
+
desc: "file path to the desired output file (with the file " \
|
58
|
+
"extension)"
|
59
|
+
def convert(file)
|
60
|
+
unless supported_format?(options[:format])
|
61
|
+
return unsupported_format_error(options[:format])
|
62
|
+
end
|
63
|
+
|
64
|
+
input_format = detect_input_format(file)
|
65
|
+
unless supported_input_format?(input_format)
|
66
|
+
return unsupported_input_format_error
|
67
|
+
end
|
68
|
+
|
69
|
+
object = source_object(file, input_format)
|
70
|
+
|
71
|
+
convert_to_format(object, options)
|
72
|
+
end
|
73
|
+
default_task :convert
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def supported_format?(format)
|
78
|
+
SupportedOutputFormats.all.include?(format)
|
79
|
+
end
|
80
|
+
|
81
|
+
def unsupported_format_error(format)
|
82
|
+
formats = SupportedOutputFormats.all.map { |v| "'#{v}'" }.join(", ")
|
83
|
+
|
84
|
+
Vectory.ui.error(
|
85
|
+
"Unsupported output format '#{format}'. " \
|
86
|
+
"Please choose one of: #{formats}.",
|
87
|
+
)
|
88
|
+
|
89
|
+
STATUS_UNSUPPORTED_OUTPUT_FORMAT_ERROR
|
90
|
+
end
|
91
|
+
|
92
|
+
def detect_input_format(file)
|
93
|
+
FileMagic.detect(file)
|
94
|
+
end
|
95
|
+
|
96
|
+
def supported_input_format?(format)
|
97
|
+
SupportedInputFormats.all.include?(format)
|
98
|
+
end
|
99
|
+
|
100
|
+
def unsupported_input_format_error
|
101
|
+
formats = SupportedInputFormats.all.map { |v| "'#{v}'" }.join(", ")
|
102
|
+
Vectory.ui.error(
|
103
|
+
"Could not detect input format. " \
|
104
|
+
"Please provide file of the following formats: #{formats}.",
|
105
|
+
)
|
106
|
+
|
107
|
+
STATUS_UNSUPPORTED_INPUT_FORMAT_ERROR
|
108
|
+
end
|
109
|
+
|
110
|
+
def source_object(file, format)
|
111
|
+
Vectory.const_get(format.capitalize).from_path(file)
|
112
|
+
end
|
113
|
+
|
114
|
+
def convert_to_format(object, options)
|
115
|
+
path = options[:output] || current_dir_path(object.initial_path,
|
116
|
+
options[:format])
|
117
|
+
to_format(object, options[:format]).write(path)
|
118
|
+
Vectory.ui.info("Output file was written to #{path}")
|
119
|
+
|
120
|
+
STATUS_SUCCESS
|
121
|
+
rescue Vectory::Error => e
|
122
|
+
handle_vectory_error(e)
|
123
|
+
end
|
124
|
+
|
125
|
+
def handle_vectory_error(exception)
|
126
|
+
Vectory.ui.error(exception.message)
|
127
|
+
|
128
|
+
MAP_ERROR_TO_STATUS[exception.class] || raise(exception)
|
129
|
+
end
|
130
|
+
|
131
|
+
def current_dir_path(input_path, output_format)
|
132
|
+
File.join(Dir.pwd, "#{File.basename(input_path, '.*')}.#{output_format}")
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_format(object, format)
|
136
|
+
method_name = "to_#{format}"
|
137
|
+
object.public_send(method_name)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "base64"
|
2
|
+
|
3
|
+
module Vectory
|
4
|
+
class Datauri < Image
|
5
|
+
REGEX = %r{^
|
6
|
+
data:
|
7
|
+
(?<mimetype>[^;]+);
|
8
|
+
(?:charset=[^;]+;)?
|
9
|
+
base64,
|
10
|
+
(?<data>.+)
|
11
|
+
$}x.freeze
|
12
|
+
|
13
|
+
def self.from_vector(vector)
|
14
|
+
mimetype = vector.class.mimetype
|
15
|
+
content = vector.content.gsub("\r\n", "\n")
|
16
|
+
data = Base64.strict_encode64(content)
|
17
|
+
|
18
|
+
new("data:#{mimetype};base64,#{data}")
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_vector
|
22
|
+
match = parse_datauri(@content)
|
23
|
+
content = Base64.strict_decode64(match[:data])
|
24
|
+
image_class = detect_image_class(match[:mimetype])
|
25
|
+
|
26
|
+
image_class.from_content(content)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def parse_datauri(uri)
|
32
|
+
match = REGEX.match(uri)
|
33
|
+
return match if match
|
34
|
+
|
35
|
+
raise ConversionError, "Could not parse datauri: '#{uri.slice(0, 30)}'."
|
36
|
+
end
|
37
|
+
|
38
|
+
def detect_image_class(image_type)
|
39
|
+
case image_type
|
40
|
+
when Eps.mimetype then return Eps
|
41
|
+
when Emf.mimetype then return Emf
|
42
|
+
when Svg.mimetype then return Svg
|
43
|
+
end
|
44
|
+
|
45
|
+
raise ConversionError, "Could not detect image type '#{image_type}'."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/vectory/emf.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "emf2svg"
|
4
|
+
|
5
|
+
module Vectory
|
6
|
+
class Emf < Vector
|
7
|
+
def self.default_extension
|
8
|
+
"emf"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.mimetype
|
12
|
+
"image/emf"
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_svg
|
16
|
+
with_file("emf") do |input_path|
|
17
|
+
content = Emf2svg.from_file(input_path).sub(/<\?[^>]+>/, "")
|
18
|
+
|
19
|
+
Svg.from_content(content)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_eps
|
24
|
+
convert_with_inkscape("--export-type=eps", Eps)
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_ps
|
28
|
+
convert_with_inkscape("--export-type=ps", Ps)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/vectory/eps.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vectory
|
4
|
+
class Eps < Vector
|
5
|
+
def self.default_extension
|
6
|
+
"eps"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.mimetype
|
10
|
+
"application/postscript"
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_ps
|
14
|
+
convert_with_inkscape("--export-type=ps", Ps)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_svg
|
18
|
+
convert_with_inkscape("--export-plain-svg --export-type=svg", Svg)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_emf
|
22
|
+
convert_with_inkscape("--export-type=emf", Emf)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def imgfile_suffix(uri, suffix)
|
28
|
+
"#{File.join(File.dirname(uri), File.basename(uri, '.*'))}.#{suffix}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Vectory
|
2
|
+
class FileMagic
|
3
|
+
EPS_30_MAGIC = "\x25\x21\x50\x53\x2d\x41\x64\x6f\x62\x65\x2d\x33\x2e\x30" \
|
4
|
+
"\x20\x45\x50\x53\x46\x2d\x33\x2e\x30"
|
5
|
+
.force_encoding("BINARY") # "%!PS-Adobe-3.0 EPSF-3.0"
|
6
|
+
|
7
|
+
EPS_31_MAGIC = "\x25\x21\x50\x53\x2d\x41\x64\x6f\x62\x65\x2d\x33\x2e\x31" \
|
8
|
+
"\x20\x45\x50\x53\x46\x2d\x33\x2e\x30"
|
9
|
+
.force_encoding("BINARY") # "%!PS-Adobe-3.1 EPSF-3.0"
|
10
|
+
|
11
|
+
PS_MAGIC = "\x25\x21\x50\x53\x2d\x41\x64\x6f\x62\x65\x2d\x33\x2e\x30"
|
12
|
+
.force_encoding("BINARY") # "%!PS-Adobe-3.0"
|
13
|
+
|
14
|
+
EMF_MAGIC = "\x01\x00\x00\x00".force_encoding("BINARY")
|
15
|
+
|
16
|
+
def self.detect(path)
|
17
|
+
new(path).detect
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(path)
|
21
|
+
@path = path
|
22
|
+
end
|
23
|
+
|
24
|
+
def detect
|
25
|
+
beginning = File.read(@path, 100, mode: "rb")
|
26
|
+
|
27
|
+
eps_slice = beginning.byteslice(0, EPS_30_MAGIC.size)
|
28
|
+
Vectory.ui.debug("File magic is '#{to_bytes(beginning)}' of '#{@path}'.")
|
29
|
+
|
30
|
+
return :eps if [EPS_30_MAGIC, EPS_31_MAGIC].include?(eps_slice)
|
31
|
+
|
32
|
+
ps_slice = beginning.byteslice(0, PS_MAGIC.size)
|
33
|
+
return :ps if ps_slice == PS_MAGIC
|
34
|
+
|
35
|
+
emf_slice = beginning.byteslice(0, EMF_MAGIC.size)
|
36
|
+
return :emf if emf_slice == EMF_MAGIC
|
37
|
+
|
38
|
+
return :svg if contain_svg_tag?
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def to_bytes(str)
|
44
|
+
str.unpack("c*").map { |e| "\\x#{e.to_s(16).rjust(2, '0')}" }.join
|
45
|
+
end
|
46
|
+
|
47
|
+
def contain_svg_tag?
|
48
|
+
content = File.read(@path, 4096)
|
49
|
+
|
50
|
+
return :svg if content.include?("<svg")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tmpdir"
|
4
|
+
|
5
|
+
module Vectory
|
6
|
+
class Image
|
7
|
+
class << self
|
8
|
+
def from_path(path)
|
9
|
+
new(File.read(path, mode: "rb"))
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_content(content)
|
13
|
+
new(content)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :content
|
18
|
+
|
19
|
+
def initialize(content)
|
20
|
+
@content = content
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
require_relative "system_call"
|
5
|
+
|
6
|
+
module Vectory
|
7
|
+
class InkscapeConverter
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def self.convert(uri, output_extension, option)
|
11
|
+
instance.convert(uri, output_extension, option)
|
12
|
+
end
|
13
|
+
|
14
|
+
def convert(uri, output_extension, option)
|
15
|
+
exe = inkscape_path_or_raise_error(uri)
|
16
|
+
uri = external_path uri
|
17
|
+
exe = external_path exe
|
18
|
+
cmd = %(#{exe} #{option} #{uri})
|
19
|
+
|
20
|
+
call = SystemCall.new(cmd).call
|
21
|
+
|
22
|
+
output_path = "#{uri}.#{output_extension}"
|
23
|
+
raise_conversion_error(call) unless File.exist?(output_path)
|
24
|
+
|
25
|
+
# and return Vectory::Utils::datauri(file)
|
26
|
+
|
27
|
+
output_path
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def inkscape_path_or_raise_error(path)
|
33
|
+
inkscape_path or raise(InkscapeNotFoundError,
|
34
|
+
"Inkscape missing in PATH, unable to " \
|
35
|
+
"convert image #{path}. Aborting.")
|
36
|
+
end
|
37
|
+
|
38
|
+
def inkscape_path
|
39
|
+
cmd = "inkscape"
|
40
|
+
exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
|
41
|
+
|
42
|
+
ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
|
43
|
+
exts.each do |ext|
|
44
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
45
|
+
return exe if File.executable?(exe) && !File.directory?(exe)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def raise_conversion_error(call)
|
53
|
+
raise Vectory::ConversionError,
|
54
|
+
"Could not convert with Inkscape. " \
|
55
|
+
"Inkscape cmd: '#{call.cmd}',\n" \
|
56
|
+
"status: '#{call.status}',\n" \
|
57
|
+
"stdout: '#{call.stdout.strip}',\n" \
|
58
|
+
"stderr: '#{call.stderr.strip}'."
|
59
|
+
end
|
60
|
+
|
61
|
+
def external_path(path)
|
62
|
+
win = !!((RUBY_PLATFORM =~ /(win|w)(32|64)$/) ||
|
63
|
+
(RUBY_PLATFORM =~ /mswin|mingw/))
|
64
|
+
if win
|
65
|
+
path.gsub!(%{/}, "\\")
|
66
|
+
path[/\s/] ? "\"#{path}\"" : path
|
67
|
+
else
|
68
|
+
path
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/vectory/ps.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vectory
|
4
|
+
class Ps < Vector
|
5
|
+
def self.default_extension
|
6
|
+
"ps"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.mimetype
|
10
|
+
"application/postscript"
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_eps
|
14
|
+
convert_with_inkscape("--export-type=eps", Eps)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_emf
|
18
|
+
convert_with_inkscape("--export-type=emf", Emf)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_svg
|
22
|
+
convert_with_inkscape("--export-type=svg", Svg)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|