vectory 0.2.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/README.adoc +167 -0
- data/lib/vectory/datauri.rb +13 -0
- data/lib/vectory/image.rb +4 -0
- data/lib/vectory/inkscape_converter.rb +82 -11
- data/lib/vectory/svg.rb +84 -1
- data/lib/vectory/svg_mapping.rb +118 -0
- data/lib/vectory/system_call.rb +10 -2
- data/lib/vectory/utils.rb +111 -0
- data/lib/vectory/vector.rb +25 -2
- data/lib/vectory/version.rb +1 -1
- data/lib/vectory.rb +3 -0
- data/spec/examples/svg/action_schemaexpg1.svg +124 -0
- data/spec/examples/svg/action_schemaexpg2.svg +124 -0
- data/spec/examples/svg/doc-ref.xml +109 -0
- data/spec/examples/svg/doc.xml +51 -0
- data/spec/examples/svg/doc2-ref.xml +59 -0
- data/spec/examples/svg/doc2.xml +28 -0
- data/spec/support/matchers.rb +12 -0
- data/spec/support/vectory_helper.rb +14 -0
- data/spec/vectory/cli_spec.rb +15 -14
- data/spec/vectory/datauri_spec.rb +80 -0
- data/spec/vectory/emf_spec.rb +27 -3
- data/spec/vectory/eps_spec.rb +36 -9
- data/spec/vectory/file_magic_spec.rb +3 -3
- data/spec/vectory/inkscape_converter_spec.rb +11 -6
- data/spec/vectory/ps_spec.rb +28 -3
- data/spec/vectory/svg_mapping_spec.rb +42 -0
- data/spec/vectory/svg_spec.rb +43 -3
- data/spec/vectory/vector_spec.rb +37 -0
- data/vectory.gemspec +0 -4
- metadata +22 -70
- /data/spec/examples/emf2eps/{img.eps → ref.eps} +0 -0
- /data/spec/examples/emf2ps/{img.ps → ref.ps} +0 -0
- /data/spec/examples/emf2svg/{img.svg → ref.svg} +0 -0
- /data/spec/examples/eps2emf/{img.emf → ref.emf} +0 -0
- /data/spec/examples/eps2ps/{img.ps → ref.ps} +0 -0
- /data/spec/examples/eps2svg/{img.svg → ref.svg} +0 -0
- /data/spec/examples/ps2emf/{img.emf → ref.emf} +0 -0
- /data/spec/examples/ps2eps/{img.eps → ref.eps} +0 -0
- /data/spec/examples/ps2svg/{img.svg → ref.svg} +0 -0
- /data/spec/examples/svg2emf/{img.emf → ref.emf} +0 -0
- /data/spec/examples/svg2eps/{img.eps → ref.eps} +0 -0
- /data/spec/examples/svg2ps/{img.ps → ref.ps} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f51d610adc333c06f92e8e28ca9722a890f12b03668bf12e96ce057d2574dea
|
4
|
+
data.tar.gz: 792bc815a0b86a6211b83268f16b9abe7a138aba0081a2d7a95242c5e9a7af82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 249d3f2fa7df2d216cdf5d046647507b81a55bd5db56a603a90a3890501adb10e8a03f41b272385c65a8de48c50b8b52db743ec5ddc20be88e4e5aab6ed21ca6
|
7
|
+
data.tar.gz: 5ed331050b3c4a3231cabc7ba061695f017de5ea44a3056bf01537144b12c96cf5587c0a52793d3d435d961052210aefed73a52c4afc9c3563695fca8e23c91d
|
data/Gemfile
CHANGED
data/README.adoc
CHANGED
@@ -27,6 +27,9 @@ Vectory relies on the following software to be installed:
|
|
27
27
|
* https://inkscape.org[Inkscape]
|
28
28
|
* https://www.ghostscript.com/[Ghostscript]
|
29
29
|
|
30
|
+
NOTE: Inkscape 1.3.1 does not work properly with EPS/PS on Windows. To avoid
|
31
|
+
this issue, the 1.3.0 version of Inkscape can be used.
|
32
|
+
|
30
33
|
|
31
34
|
=== Gem install
|
32
35
|
|
@@ -157,6 +160,131 @@ Vectory::Eps.from_path("img.eps").to_uri.content
|
|
157
160
|
----
|
158
161
|
|
159
162
|
|
163
|
+
==== SVG mapping (for the metanorma project)
|
164
|
+
|
165
|
+
Vectory can integrate SVG files into XML or HTML, respecting internal id and
|
166
|
+
link references:
|
167
|
+
|
168
|
+
[source,ruby]
|
169
|
+
----
|
170
|
+
xml_string = Vectory::SvgMapping.from_path("doc.xml").call
|
171
|
+
----
|
172
|
+
|
173
|
+
In order to do that an initial XML should support the `svgmap` tag with links
|
174
|
+
mapping. For example, it can convert XML like this:
|
175
|
+
|
176
|
+
[source,xml]
|
177
|
+
----
|
178
|
+
<svgmap id="_4072bdcb-5895-4821-b636-5795b96787cb">
|
179
|
+
<figure><image src="action_schemaexpg1.svg"/></figure>
|
180
|
+
<target href="mn://action_schema">
|
181
|
+
<xref target="ref1">Computer</xref>
|
182
|
+
</target>
|
183
|
+
<target href="http://www.example.com">
|
184
|
+
<link target="http://www.example.com">Phone</link><
|
185
|
+
/target>
|
186
|
+
</svgmap>
|
187
|
+
----
|
188
|
+
|
189
|
+
.action_schemaexpg1.svg
|
190
|
+
[source,xml]
|
191
|
+
----
|
192
|
+
<?xml version="1.0" encoding="utf-8"?>
|
193
|
+
<!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
194
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
195
|
+
viewBox="0 0 595.28 841.89" style="enable-background:new 0 0 595.28 841.89;" xml:space="preserve">
|
196
|
+
<style type="text/css">
|
197
|
+
#Layer_1 { fill:none }
|
198
|
+
svg[id = 'Layer_1'] { fill:none }
|
199
|
+
.st0{fill:none;stroke:#000000;stroke-miterlimit:10;}
|
200
|
+
</style>
|
201
|
+
<image style="overflow:visible;" width="368" height="315" xlink:href="data:image/gif;base64,R0lG..ommited to save space" transform="matrix(1 0 0 1 114 263.8898)">
|
202
|
+
</image>
|
203
|
+
<a xlink:href="mn://action_schema" xlink:dummy="Layer_1">
|
204
|
+
<rect x="123.28" y="273.93" class="st0" width="88.05" height="41.84"/>
|
205
|
+
</a>
|
206
|
+
<a xlink:href="mn://basic_attribute_schema" >
|
207
|
+
<rect x="324.69" y="450.52" class="st0" width="132.62" height="40.75"/>
|
208
|
+
</a>
|
209
|
+
<a xlink:href="mn://support_resource_schema" >
|
210
|
+
<rect x="324.69" y="528.36" class="st0" width="148.16" height="40.75"/>
|
211
|
+
</a>
|
212
|
+
</svg>
|
213
|
+
----
|
214
|
+
|
215
|
+
into XML containing inline SVG tags. Notice changes in the `id` attributes and
|
216
|
+
the `a` tags:
|
217
|
+
|
218
|
+
[source,xml]
|
219
|
+
----
|
220
|
+
<figure>
|
221
|
+
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='Layer_1_000000001' x='0px' y='0px' viewBox='0 0 595.28 841.89' style='enable-background:new 0 0 595.28 841.89;' xml:space='preserve'>
|
222
|
+
<style> ..ommited to save space </style>
|
223
|
+
<image> ..ommited </image>
|
224
|
+
<a xlink:href='#ref1' xlink:dummy='Layer_1_000000001'>
|
225
|
+
<rect x='123.28' y='273.93' class='st0' width='88.05' height='41.84'/>
|
226
|
+
</a>
|
227
|
+
<a xlink:href='mn://basic_attribute_schema'>
|
228
|
+
<rect x='324.69' y='450.52' class='st0' width='132.62' height='40.75'/>
|
229
|
+
</a>
|
230
|
+
<a xlink:href='mn://support_resource_schema'>
|
231
|
+
<rect x='324.69' y='528.36' class='st0' width='148.16' height='40.75'/>
|
232
|
+
</a>
|
233
|
+
</svg>
|
234
|
+
</figure>
|
235
|
+
----
|
236
|
+
|
237
|
+
It also supports SVG in a form of an inline tag:
|
238
|
+
|
239
|
+
[source,xml]
|
240
|
+
----
|
241
|
+
<svgmap id="_60dadf08-48d4-4164-845c-b4e293e00abd">
|
242
|
+
<figure>
|
243
|
+
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='Layer_1' x='0px' y='0px' viewBox='0 0 595.28 841.89' style='enable-background:new 0 0 595.28 841.89;' xml:space='preserve'>
|
244
|
+
<a href="mn://action_schema" >
|
245
|
+
<rect x="123.28" y="273.93" class="st0" width="88.05" height="41.84"/>
|
246
|
+
</a>
|
247
|
+
<a href="mn://basic_attribute_schema" >
|
248
|
+
<rect x="324.69" y="450.52" class="st0" width="132.62" height="40.75"/>
|
249
|
+
</a>
|
250
|
+
<a xlink:href="mn://support_resource_schema" >
|
251
|
+
<rect x="324.69" y="528.36" class="st0" width="148.16" height="40.75"/>
|
252
|
+
</a>
|
253
|
+
</svg>
|
254
|
+
</figure>
|
255
|
+
<target href="mn://action_schema">
|
256
|
+
<xref target="ref1">Computer</xref>
|
257
|
+
</target>
|
258
|
+
<target href="http://www.example.com">
|
259
|
+
<link target="http://www.example.com">Phone</link>
|
260
|
+
</target>
|
261
|
+
</svgmap>
|
262
|
+
----
|
263
|
+
|
264
|
+
and datauri:
|
265
|
+
|
266
|
+
[source,xml]
|
267
|
+
----
|
268
|
+
<svgmap id="_60dadf08-48d4-4164-845c-b4e293e00abd">
|
269
|
+
<figure>
|
270
|
+
<image src='data:image/svg+xml;base64,PD94..ommited to save space' id='__ISO_17301-1_2016' mimetype='image/svg+xml' height='auto' width='auto' alt='Workmap1'/>
|
271
|
+
</figure>
|
272
|
+
<target href="href1.htm">
|
273
|
+
<xref target="ref1">Computer</xref>
|
274
|
+
</target>
|
275
|
+
<target href="mn://basic_attribute_schema">
|
276
|
+
<link target="http://www.example.com">Phone</link>
|
277
|
+
</target>
|
278
|
+
<target href="mn://support_resource_schema">
|
279
|
+
<eref type="express" bibitemid="express_action_schema" citeas="">
|
280
|
+
<localityStack><locality type="anchor"><referenceFrom>action_schema.basic</referenceFrom></locality></localityStack>
|
281
|
+
Coffee
|
282
|
+
</eref>
|
283
|
+
</target>
|
284
|
+
</svgmap>
|
285
|
+
----
|
286
|
+
|
287
|
+
|
160
288
|
==== File system operations
|
161
289
|
|
162
290
|
An image object contains information where it is written. It can be obtained
|
@@ -201,6 +329,45 @@ purposes.
|
|
201
329
|
vector.initial_path # => "storage/images/img.eps"
|
202
330
|
----
|
203
331
|
|
332
|
+
==== Additional properties
|
333
|
+
|
334
|
+
The following additional properties are supported:
|
335
|
+
|
336
|
+
[source,ruby]
|
337
|
+
----
|
338
|
+
Datauri#mime
|
339
|
+
Datauri#height
|
340
|
+
Datauri#width
|
341
|
+
Vector (Eps, Ps, Svg, Emf)
|
342
|
+
Vector#mime
|
343
|
+
Vector#size
|
344
|
+
Vector#file_size
|
345
|
+
Vector#height
|
346
|
+
Vector#width
|
347
|
+
----
|
348
|
+
|
349
|
+
|
350
|
+
== Development
|
351
|
+
|
352
|
+
=== Releasing
|
353
|
+
|
354
|
+
Releasing is done automatically with GitHub Actions. Just bump and tag with
|
355
|
+
`gem-release`.
|
356
|
+
|
357
|
+
For a patch release (0.0.x) use:
|
358
|
+
|
359
|
+
[source,sh]
|
360
|
+
----
|
361
|
+
gem bump --version patch --tag --push
|
362
|
+
----
|
363
|
+
|
364
|
+
For a minor release (0.x.0) use:
|
365
|
+
|
366
|
+
[source,sh]
|
367
|
+
----
|
368
|
+
gem bump --version minor --tag --push
|
369
|
+
----
|
370
|
+
|
204
371
|
|
205
372
|
== Contributing
|
206
373
|
|
data/lib/vectory/datauri.rb
CHANGED
@@ -18,6 +18,19 @@ module Vectory
|
|
18
18
|
new("data:#{mimetype};base64,#{data}")
|
19
19
|
end
|
20
20
|
|
21
|
+
def mime
|
22
|
+
match = parse_datauri(@content)
|
23
|
+
match[:mimetype]
|
24
|
+
end
|
25
|
+
|
26
|
+
def height
|
27
|
+
to_vector.height
|
28
|
+
end
|
29
|
+
|
30
|
+
def width
|
31
|
+
to_vector.width
|
32
|
+
end
|
33
|
+
|
21
34
|
def to_vector
|
22
35
|
match = parse_datauri(@content)
|
23
36
|
content = Base64.strict_decode64(match[:data])
|
data/lib/vectory/image.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "singleton"
|
4
|
+
require "tmpdir"
|
4
5
|
require_relative "system_call"
|
5
6
|
|
6
7
|
module Vectory
|
@@ -12,43 +13,78 @@ module Vectory
|
|
12
13
|
end
|
13
14
|
|
14
15
|
def convert(uri, output_extension, option)
|
15
|
-
exe = inkscape_path_or_raise_error
|
16
|
+
exe = inkscape_path_or_raise_error
|
16
17
|
uri = external_path uri
|
17
18
|
exe = external_path exe
|
18
19
|
cmd = %(#{exe} #{option} #{uri})
|
19
20
|
|
20
21
|
call = SystemCall.new(cmd).call
|
21
22
|
|
22
|
-
output_path =
|
23
|
-
raise_conversion_error(call) unless
|
23
|
+
output_path = find_output(uri, output_extension)
|
24
|
+
raise_conversion_error(call) unless output_path
|
24
25
|
|
25
26
|
# and return Vectory::Utils::datauri(file)
|
26
27
|
|
27
28
|
output_path
|
28
29
|
end
|
29
30
|
|
31
|
+
def height(content, format)
|
32
|
+
query_integer(content, format, "--query-height")
|
33
|
+
end
|
34
|
+
|
35
|
+
def width(content, format)
|
36
|
+
query_integer(content, format, "--query-width")
|
37
|
+
end
|
38
|
+
|
30
39
|
private
|
31
40
|
|
32
|
-
def inkscape_path_or_raise_error
|
41
|
+
def inkscape_path_or_raise_error
|
33
42
|
inkscape_path or raise(InkscapeNotFoundError,
|
34
43
|
"Inkscape missing in PATH, unable to " \
|
35
|
-
"convert image
|
44
|
+
"convert image. Aborting.")
|
36
45
|
end
|
37
46
|
|
38
47
|
def inkscape_path
|
39
|
-
|
40
|
-
|
48
|
+
@inkscape_path ||= find_inkscape
|
49
|
+
end
|
41
50
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
51
|
+
def find_inkscape
|
52
|
+
cmds.each do |cmd|
|
53
|
+
extensions.each do |ext|
|
54
|
+
paths.each do |path|
|
55
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
56
|
+
|
57
|
+
return exe if File.executable?(exe) && !File.directory?(exe)
|
58
|
+
end
|
46
59
|
end
|
47
60
|
end
|
48
61
|
|
49
62
|
nil
|
50
63
|
end
|
51
64
|
|
65
|
+
def cmds
|
66
|
+
["inkscapecom", "inkscape"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def extensions
|
70
|
+
ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
|
71
|
+
end
|
72
|
+
|
73
|
+
def paths
|
74
|
+
ENV["PATH"].split(File::PATH_SEPARATOR)
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_output(source_path, output_extension)
|
78
|
+
basenames = [File.basename(source_path, ".*"),
|
79
|
+
File.basename(source_path)]
|
80
|
+
|
81
|
+
paths = basenames.map do |basename|
|
82
|
+
"#{File.join(File.dirname(source_path), basename)}.#{output_extension}"
|
83
|
+
end
|
84
|
+
|
85
|
+
paths.find { |p| File.exist?(p) }
|
86
|
+
end
|
87
|
+
|
52
88
|
def raise_conversion_error(call)
|
53
89
|
raise Vectory::ConversionError,
|
54
90
|
"Could not convert with Inkscape. " \
|
@@ -68,5 +104,40 @@ module Vectory
|
|
68
104
|
path
|
69
105
|
end
|
70
106
|
end
|
107
|
+
|
108
|
+
def query_integer(content, format, options)
|
109
|
+
query(content, format, options).to_f.round
|
110
|
+
end
|
111
|
+
|
112
|
+
def query(content, format, options)
|
113
|
+
exe = inkscape_path_or_raise_error
|
114
|
+
|
115
|
+
with_file(content, format) do |path|
|
116
|
+
cmd = "#{external_path(exe)} #{options} #{external_path(path)}"
|
117
|
+
|
118
|
+
call = SystemCall.new(cmd).call
|
119
|
+
raise_query_error(call) if call.stdout.empty?
|
120
|
+
|
121
|
+
call.stdout
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def with_file(content, extension)
|
126
|
+
Dir.mktmpdir do |dir|
|
127
|
+
path = File.join(dir, "image.#{extension}")
|
128
|
+
File.binwrite(path, content)
|
129
|
+
|
130
|
+
yield path
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def raise_query_error(call)
|
135
|
+
raise Vectory::InkscapeQueryError,
|
136
|
+
"Could not query with Inkscape. " \
|
137
|
+
"Inkscape cmd: '#{call.cmd}',\n" \
|
138
|
+
"status: '#{call.status}',\n" \
|
139
|
+
"stdout: '#{call.stdout.strip}',\n" \
|
140
|
+
"stderr: '#{call.stderr.strip}'."
|
141
|
+
end
|
71
142
|
end
|
72
143
|
end
|
data/lib/vectory/svg.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "nokogiri"
|
4
|
+
|
3
5
|
module Vectory
|
4
6
|
class Svg < Vector
|
5
|
-
|
7
|
+
SVG_NS = "http://www.w3.org/2000/svg"
|
6
8
|
|
7
9
|
def self.default_extension
|
8
10
|
"svg"
|
@@ -12,6 +14,10 @@ module Vectory
|
|
12
14
|
"image/svg+xml"
|
13
15
|
end
|
14
16
|
|
17
|
+
def content
|
18
|
+
@document&.to_xml || @content
|
19
|
+
end
|
20
|
+
|
15
21
|
def to_emf
|
16
22
|
convert_with_inkscape("--export-type=emf", Emf)
|
17
23
|
end
|
@@ -23,5 +29,82 @@ module Vectory
|
|
23
29
|
def to_ps
|
24
30
|
convert_with_inkscape("--export-type=ps", Ps)
|
25
31
|
end
|
32
|
+
|
33
|
+
def namespace(suffix, links, xpath_to_remove)
|
34
|
+
remap_links(links)
|
35
|
+
suffix_ids(suffix)
|
36
|
+
remove_xpath(xpath_to_remove)
|
37
|
+
end
|
38
|
+
|
39
|
+
def remap_links(map)
|
40
|
+
document.xpath(".//m:a", "m" => SVG_NS).each do |a|
|
41
|
+
href_attrs = ["xlink:href", "href"]
|
42
|
+
href_attrs.each do |p|
|
43
|
+
a[p] and x = map[File.expand_path(a[p])] and a[p] = x
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def suffix_ids(suffix)
|
51
|
+
ids = collect_ids
|
52
|
+
return if ids.empty?
|
53
|
+
|
54
|
+
update_ids_attrs(ids, suffix)
|
55
|
+
update_ids_css(ids, suffix)
|
56
|
+
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def remove_xpath(xpath)
|
61
|
+
document.xpath(xpath).remove
|
62
|
+
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def content=(content)
|
69
|
+
if @document
|
70
|
+
@document = Nokogiri::XML(content)
|
71
|
+
else
|
72
|
+
@content = content
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def document
|
77
|
+
@document ||= begin
|
78
|
+
doc = Nokogiri::XML(@content)
|
79
|
+
@content = nil
|
80
|
+
doc
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def collect_ids
|
85
|
+
document.xpath("./@id | .//@id").map(&:value)
|
86
|
+
end
|
87
|
+
|
88
|
+
def update_ids_attrs(ids, suffix)
|
89
|
+
document.xpath(". | .//*[@*]").each do |a|
|
90
|
+
a.attribute_nodes.each do |x|
|
91
|
+
ids.include?(x.value) and x.value += sprintf("_%09d", suffix)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def update_ids_css(ids, suffix)
|
97
|
+
document.xpath("//m:style", "m" => SVG_NS).each do |s|
|
98
|
+
c = s.children.to_xml
|
99
|
+
ids.each do |i|
|
100
|
+
c = c.gsub(%r[##{i}\b],
|
101
|
+
sprintf("#%<id>s_%<suffix>09d", id: i, suffix: suffix))
|
102
|
+
.gsub(%r(\[id\s*=\s*['"]?#{i}['"]?\]),
|
103
|
+
sprintf("[id='%<id>s_%<suffix>09d']", id: i, suffix: suffix))
|
104
|
+
end
|
105
|
+
|
106
|
+
s.children = c
|
107
|
+
end
|
108
|
+
end
|
26
109
|
end
|
27
110
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require_relative "svg"
|
2
|
+
|
3
|
+
module Vectory
|
4
|
+
class SvgMapping
|
5
|
+
class Namespace
|
6
|
+
def initialize(xmldoc)
|
7
|
+
@namespace = xmldoc.root.namespace
|
8
|
+
end
|
9
|
+
|
10
|
+
def ns(path)
|
11
|
+
return path if @namespace.nil?
|
12
|
+
|
13
|
+
path.gsub(%r{/([a-zA-z])}, "/xmlns:\\1")
|
14
|
+
.gsub(%r{::([a-zA-z])}, "::xmlns:\\1")
|
15
|
+
.gsub(%r{\[([a-zA-z][a-z0-9A-Z@/]* ?=)}, "[xmlns:\\1")
|
16
|
+
.gsub(%r{\[([a-zA-z][a-z0-9A-Z@/]*\])}, "[xmlns:\\1")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
SVG_NS = "http://www.w3.org/2000/svg".freeze
|
21
|
+
PROCESSING_XPATH =
|
22
|
+
"processing-instruction()|.//processing-instruction()".freeze
|
23
|
+
|
24
|
+
def self.from_path(path)
|
25
|
+
new(File.read(path))
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(xml, local_directory = "")
|
29
|
+
@xml = xml
|
30
|
+
@local_directory = local_directory
|
31
|
+
end
|
32
|
+
|
33
|
+
def call
|
34
|
+
xmldoc = Nokogiri::XML(@xml)
|
35
|
+
@namespace = Namespace.new(xmldoc)
|
36
|
+
|
37
|
+
xmldoc.xpath(@namespace.ns("//svgmap")).each_with_index do |svgmap, index|
|
38
|
+
process_svgmap(svgmap, index)
|
39
|
+
end
|
40
|
+
|
41
|
+
xmldoc.to_xml
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def process_svgmap(svgmap, suffix)
|
47
|
+
image = extract_image_tag(svgmap)
|
48
|
+
return unless image
|
49
|
+
|
50
|
+
content = generate_content(image, svgmap, suffix)
|
51
|
+
return unless content
|
52
|
+
|
53
|
+
image.replace(content)
|
54
|
+
|
55
|
+
simplify_svgmap(svgmap)
|
56
|
+
end
|
57
|
+
|
58
|
+
def extract_image_tag(svgmap)
|
59
|
+
image = svgmap.at(@namespace.ns(".//image"))
|
60
|
+
return image if image && image["src"] && !image["src"].empty?
|
61
|
+
|
62
|
+
svgmap.at(".//m:svg", "m" => SVG_NS)
|
63
|
+
end
|
64
|
+
|
65
|
+
def generate_content(image, svgmap, suffix)
|
66
|
+
vector = build_vector(image)
|
67
|
+
return unless vector
|
68
|
+
|
69
|
+
links_map = from_targets_to_links_map(svgmap)
|
70
|
+
vector.namespace(suffix, links_map, PROCESSING_XPATH)
|
71
|
+
|
72
|
+
vector.content
|
73
|
+
end
|
74
|
+
|
75
|
+
def build_vector(image)
|
76
|
+
return Vectory::Svg.from_content(image.to_xml) if image.name == "svg"
|
77
|
+
|
78
|
+
return unless image.name == "image"
|
79
|
+
|
80
|
+
src = image["src"]
|
81
|
+
return Vectory::Datauri.new(src).to_vector if /^data:/.match?(src)
|
82
|
+
|
83
|
+
path = @local_directory.empty? ? src : File.join(@local_directory, src)
|
84
|
+
return unless File.exist?(path)
|
85
|
+
|
86
|
+
Vectory::Svg.from_path(path)
|
87
|
+
end
|
88
|
+
|
89
|
+
def from_targets_to_links_map(svgmap)
|
90
|
+
targets = svgmap.xpath(@namespace.ns("./target"))
|
91
|
+
targets.each_with_object({}) do |target_tag, m|
|
92
|
+
target = link_target(target_tag)
|
93
|
+
next unless target
|
94
|
+
|
95
|
+
href = File.expand_path(target_tag["href"])
|
96
|
+
m[href] = target
|
97
|
+
|
98
|
+
target_tag.remove
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def link_target(target_tag)
|
103
|
+
xref = target_tag.at(@namespace.ns("./xref"))
|
104
|
+
return "##{xref['target']}" if xref
|
105
|
+
|
106
|
+
link = target_tag.at(@namespace.ns("./link"))
|
107
|
+
return unless link
|
108
|
+
|
109
|
+
link["target"]
|
110
|
+
end
|
111
|
+
|
112
|
+
def simplify_svgmap(svgmap)
|
113
|
+
return if svgmap.at(@namespace.ns("./target/eref"))
|
114
|
+
|
115
|
+
svgmap.replace(svgmap.at(@namespace.ns("./figure")))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/vectory/system_call.rb
CHANGED
@@ -2,10 +2,13 @@ require "open3"
|
|
2
2
|
|
3
3
|
module Vectory
|
4
4
|
class SystemCall
|
5
|
+
TIMEOUT = 60
|
6
|
+
|
5
7
|
attr_reader :status, :stdout, :stderr, :cmd
|
6
8
|
|
7
|
-
def initialize(cmd)
|
9
|
+
def initialize(cmd, timeout = TIMEOUT)
|
8
10
|
@cmd = cmd
|
11
|
+
@timeout = timeout
|
9
12
|
end
|
10
13
|
|
11
14
|
def call
|
@@ -27,7 +30,12 @@ module Vectory
|
|
27
30
|
end
|
28
31
|
|
29
32
|
def execute(cmd)
|
30
|
-
|
33
|
+
result = Utils.capture3_with_timeout(cmd,
|
34
|
+
timeout: @timeout,
|
35
|
+
kill_after: @timeout)
|
36
|
+
@stdout = result[:stdout]
|
37
|
+
@stderr = result[:stderr]
|
38
|
+
@status = result[:status]
|
31
39
|
rescue Errno::ENOENT => e
|
32
40
|
raise SystemCallError, e.inspect
|
33
41
|
end
|