vectory 0.2.0 → 0.3.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/README.adoc +150 -0
  4. data/lib/vectory/image.rb +4 -0
  5. data/lib/vectory/inkscape_converter.rb +35 -8
  6. data/lib/vectory/svg.rb +84 -1
  7. data/lib/vectory/svg_mapping.rb +118 -0
  8. data/lib/vectory/system_call.rb +11 -2
  9. data/lib/vectory/utils.rb +111 -0
  10. data/lib/vectory/vector.rb +3 -2
  11. data/lib/vectory/version.rb +1 -1
  12. data/lib/vectory.rb +1 -0
  13. data/spec/examples/svg/action_schemaexpg1.svg +124 -0
  14. data/spec/examples/svg/action_schemaexpg2.svg +124 -0
  15. data/spec/examples/svg/doc-ref.xml +109 -0
  16. data/spec/examples/svg/doc.xml +51 -0
  17. data/spec/examples/svg/doc2-ref.xml +59 -0
  18. data/spec/examples/svg/doc2.xml +28 -0
  19. data/spec/support/vectory_helper.rb +14 -0
  20. data/spec/vectory/cli_spec.rb +12 -12
  21. data/spec/vectory/emf_spec.rb +3 -3
  22. data/spec/vectory/eps_spec.rb +3 -3
  23. data/spec/vectory/file_magic_spec.rb +3 -3
  24. data/spec/vectory/inkscape_converter_spec.rb +11 -6
  25. data/spec/vectory/ps_spec.rb +3 -3
  26. data/spec/vectory/svg_mapping_spec.rb +42 -0
  27. data/spec/vectory/svg_spec.rb +11 -3
  28. data/vectory.gemspec +0 -4
  29. metadata +22 -70
  30. /data/spec/examples/emf2eps/{img.eps → ref.eps} +0 -0
  31. /data/spec/examples/emf2ps/{img.ps → ref.ps} +0 -0
  32. /data/spec/examples/emf2svg/{img.svg → ref.svg} +0 -0
  33. /data/spec/examples/eps2emf/{img.emf → ref.emf} +0 -0
  34. /data/spec/examples/eps2ps/{img.ps → ref.ps} +0 -0
  35. /data/spec/examples/eps2svg/{img.svg → ref.svg} +0 -0
  36. /data/spec/examples/ps2emf/{img.emf → ref.emf} +0 -0
  37. /data/spec/examples/ps2eps/{img.eps → ref.eps} +0 -0
  38. /data/spec/examples/ps2svg/{img.svg → ref.svg} +0 -0
  39. /data/spec/examples/svg2emf/{img.emf → ref.emf} +0 -0
  40. /data/spec/examples/svg2eps/{img.eps → ref.eps} +0 -0
  41. /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: c46e34eee9accc66e136b741440e46269744e0f9cf8a1125382fe3ec1959b320
4
- data.tar.gz: 95a3dcf51104cc31ab37af1b0ecc39861c7717399056b0116cfd94562a66126a
3
+ metadata.gz: 865823bf5ac58c0780cdffc76abf7d396c2b481bcf292c807e419c0983f44646
4
+ data.tar.gz: cd29b9a6f1c3a851e37fa207427474a60656ad8a1ba6f27d954d08d2ded47f5d
5
5
  SHA512:
6
- metadata.gz: 8775fc8bda0a86df3bc2c4bde5ab303d861a6d5812764f8b2c40a540a0f0369a42b55cd1bac655acc3ccf37348aa976620709ae41c35d26680ae221579e416e9
7
- data.tar.gz: c880d981f9c7a26182bacf9a0ee0d0df0f5147d493f79de45bade50e813c25d258400157be70b8987f605b3d4a9b4c3217b7974d40da174503de6e074183e573
6
+ metadata.gz: 01ee6626bca7c7a61f14dffa639355171e052637f9b1c58d4dfb39721a7b7a815e6b11cd5c1deb76b4faecba37abf873e45661c16ebd2eaf7434cb9031a6deff
7
+ data.tar.gz: 6706861380815c36d273445cc970db3b01693024208596c8072e9661ab301908db5c6c81e7d707826b501263aac8dbcc4b65f553fc62857a773cb45404d53de4
data/Gemfile CHANGED
@@ -9,4 +9,6 @@ gem "rake", "~> 13.0"
9
9
 
10
10
  gem "rspec", "~> 3.0"
11
11
 
12
- gem "rubocop", "~> 1.21"
12
+ gem "rubocop", "~> 1.22"
13
+ gem "rubocop-performance", "~> 1.10"
14
+ gem "rubocop-rails", "~> 2.9"
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
@@ -202,6 +330,28 @@ vector.initial_path # => "storage/images/img.eps"
202
330
  ----
203
331
 
204
332
 
333
+ == Development
334
+
335
+ === Releasing
336
+
337
+ Releasing is done automatically with GitHub Actions. Just bump and tag with
338
+ `gem-release`.
339
+
340
+ For a patch release (0.0.x) use:
341
+
342
+ [source,sh]
343
+ ----
344
+ gem bump --version patch --tag --push
345
+ ----
346
+
347
+ For a minor release (0.x.0) use:
348
+
349
+ [source,sh]
350
+ ----
351
+ gem bump --version minor --tag --push
352
+ ----
353
+
354
+
205
355
  == Contributing
206
356
 
207
357
  Bug reports and pull requests are welcome on GitHub at:
data/lib/vectory/image.rb CHANGED
@@ -19,5 +19,9 @@ module Vectory
19
19
  def initialize(content)
20
20
  @content = content
21
21
  end
22
+
23
+ private
24
+
25
+ attr_writer :content
22
26
  end
23
27
  end
@@ -19,8 +19,8 @@ module Vectory
19
19
 
20
20
  call = SystemCall.new(cmd).call
21
21
 
22
- output_path = "#{uri}.#{output_extension}"
23
- raise_conversion_error(call) unless File.exist?(output_path)
22
+ output_path = find_output(uri, output_extension)
23
+ raise_conversion_error(call) unless output_path
24
24
 
25
25
  # and return Vectory::Utils::datauri(file)
26
26
 
@@ -36,19 +36,46 @@ module Vectory
36
36
  end
37
37
 
38
38
  def inkscape_path
39
- cmd = "inkscape"
40
- exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
39
+ @inkscape_path ||= find_inkscape
40
+ end
41
+
42
+ def find_inkscape
43
+ cmds.each do |cmd|
44
+ extensions.each do |ext|
45
+ paths.each do |path|
46
+ exe = File.join(path, "#{cmd}#{ext}")
41
47
 
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)
48
+ return exe if File.executable?(exe) && !File.directory?(exe)
49
+ end
46
50
  end
47
51
  end
48
52
 
49
53
  nil
50
54
  end
51
55
 
56
+ def cmds
57
+ ["inkscapecom", "inkscape"]
58
+ end
59
+
60
+ def extensions
61
+ ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
62
+ end
63
+
64
+ def paths
65
+ ENV["PATH"].split(File::PATH_SEPARATOR)
66
+ end
67
+
68
+ def find_output(source_path, output_extension)
69
+ basenames = [File.basename(source_path, ".*"),
70
+ File.basename(source_path)]
71
+
72
+ paths = basenames.map do |basename|
73
+ "#{File.join(File.dirname(source_path), basename)}.#{output_extension}"
74
+ end
75
+
76
+ paths.find { |p| File.exist?(p) }
77
+ end
78
+
52
79
  def raise_conversion_error(call)
53
80
  raise Vectory::ConversionError,
54
81
  "Could not convert with Inkscape. " \
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
- SVG = { "m" => "http://www.w3.org/2000/svg" }.freeze
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
@@ -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,13 @@ module Vectory
27
30
  end
28
31
 
29
32
  def execute(cmd)
30
- @stdout, @stderr, @status = Open3.capture3(cmd)
33
+ result = Utils.capture3_with_timeout(cmd,
34
+ timeout: @timeout,
35
+ kill_after: @timeout)
36
+ Vectory.ui.error(result.inspect)
37
+ @stdout = result[:stdout]
38
+ @stderr = result[:stderr]
39
+ @status = result[:status]
31
40
  rescue Errno::ENOENT => e
32
41
  raise SystemCallError, e.inspect
33
42
  end
data/lib/vectory/utils.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "marcel"
4
+ require "timeout"
5
+
3
6
  module Vectory
4
7
  class Utils
5
8
  # Extracted from https://github.com/metanorma/metanorma-utils/blob/v1.5.2/lib/utils/image.rb
@@ -49,6 +52,114 @@ module Vectory
49
52
  def absolute_path?(uri)
50
53
  %r{^/}.match?(uri) || %r{^[A-Z]:/}.match?(uri)
51
54
  end
55
+
56
+ # rubocop:disable all
57
+ #
58
+ # Originally from https://gist.github.com/pasela/9392115
59
+ #
60
+ # Capture the standard output and the standard error of a command.
61
+ # Almost same as Open3.capture3 method except for timeout handling and return value.
62
+ # See Open3.capture3.
63
+ #
64
+ # result = capture3_with_timeout([env,] cmd... [, opts])
65
+ #
66
+ # The arguments env, cmd and opts are passed to Process.spawn except
67
+ # opts[:stdin_data], opts[:binmode], opts[:timeout], opts[:signal]
68
+ # and opts[:kill_after]. See Process.spawn.
69
+ #
70
+ # If opts[:stdin_data] is specified, it is sent to the command's standard input.
71
+ #
72
+ # If opts[:binmode] is true, internal pipes are set to binary mode.
73
+ #
74
+ # If opts[:timeout] is specified, SIGTERM is sent to the command after specified seconds.
75
+ #
76
+ # If opts[:signal] is specified, it is used instead of SIGTERM on timeout.
77
+ #
78
+ # If opts[:kill_after] is specified, also send a SIGKILL after specified seconds.
79
+ # it is only sent if the command is still running after the initial signal was sent.
80
+ #
81
+ # The return value is a Hash as shown below.
82
+ #
83
+ # {
84
+ # :pid => PID of the command,
85
+ # :status => Process::Status of the command,
86
+ # :stdout => the standard output of the command,
87
+ # :stderr => the standard error of the command,
88
+ # :timeout => whether the command was timed out,
89
+ # }
90
+ def capture3_with_timeout(*cmd)
91
+ spawn_opts = Hash === cmd.last ? cmd.pop.dup : {}
92
+ opts = {
93
+ :stdin_data => spawn_opts.delete(:stdin_data) || "",
94
+ :binmode => spawn_opts.delete(:binmode) || false,
95
+ :timeout => spawn_opts.delete(:timeout),
96
+ :signal => spawn_opts.delete(:signal) || :TERM,
97
+ :kill_after => spawn_opts.delete(:kill_after),
98
+ }
99
+
100
+ in_r, in_w = IO.pipe
101
+ out_r, out_w = IO.pipe
102
+ err_r, err_w = IO.pipe
103
+ in_w.sync = true
104
+
105
+ if opts[:binmode]
106
+ in_w.binmode
107
+ out_r.binmode
108
+ err_r.binmode
109
+ end
110
+
111
+ spawn_opts[:in] = in_r
112
+ spawn_opts[:out] = out_w
113
+ spawn_opts[:err] = err_w
114
+
115
+ result = {
116
+ :pid => nil,
117
+ :status => nil,
118
+ :stdout => nil,
119
+ :stderr => nil,
120
+ :timeout => false,
121
+ }
122
+
123
+ out_reader = nil
124
+ err_reader = nil
125
+ wait_thr = nil
126
+
127
+ begin
128
+ Timeout.timeout(opts[:timeout]) do
129
+ result[:pid] = spawn(*cmd, spawn_opts)
130
+ wait_thr = Process.detach(result[:pid])
131
+ in_r.close
132
+ out_w.close
133
+ err_w.close
134
+
135
+ out_reader = Thread.new { out_r.read }
136
+ err_reader = Thread.new { err_r.read }
137
+
138
+ in_w.write opts[:stdin_data]
139
+ in_w.close
140
+
141
+ result[:status] = wait_thr.value
142
+ end
143
+ rescue Timeout::Error
144
+ result[:timeout] = true
145
+ pid = spawn_opts[:pgroup] ? -result[:pid] : result[:pid]
146
+ Process.kill(opts[:signal], pid)
147
+ if opts[:kill_after]
148
+ unless wait_thr.join(opts[:kill_after])
149
+ Process.kill(:KILL, pid)
150
+ end
151
+ end
152
+ ensure
153
+ result[:status] = wait_thr.value if wait_thr
154
+ result[:stdout] = out_reader.value if out_reader
155
+ result[:stderr] = err_reader.value if err_reader
156
+ out_r.close unless out_r.closed?
157
+ err_r.close unless err_r.closed?
158
+ end
159
+
160
+ result
161
+ end
162
+ # rubocop:enable all
52
163
  end
53
164
  end
54
165
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "tempfile"
3
4
  require_relative "inkscape_converter"
4
5
 
5
6
  module Vectory
@@ -44,7 +45,7 @@ module Vectory
44
45
 
45
46
  def write(path = nil)
46
47
  target_path = path || @path || tmp_path
47
- File.binwrite(target_path, @content)
48
+ File.binwrite(target_path, content)
48
49
  @path = File.expand_path(target_path)
49
50
 
50
51
  self
@@ -59,7 +60,7 @@ module Vectory
59
60
  def with_file(input_extension)
60
61
  Dir.mktmpdir do |dir|
61
62
  input_path = File.join(dir, "image.#{input_extension}")
62
- File.binwrite(input_path, @content)
63
+ File.binwrite(input_path, content)
63
64
 
64
65
  yield input_path
65
66
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vectory
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/vectory.rb CHANGED
@@ -10,6 +10,7 @@ require_relative "vectory/eps"
10
10
  require_relative "vectory/ps"
11
11
  require_relative "vectory/emf"
12
12
  require_relative "vectory/svg"
13
+ require_relative "vectory/svg_mapping"
13
14
 
14
15
  module Vectory
15
16
  class Error < StandardError; end