vectory 0.5.0 → 0.6.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/README.adoc +1 -1
- data/lib/vectory/capture.rb +115 -0
- data/lib/vectory/svg_mapping.rb +14 -7
- data/lib/vectory/system_call.rb +4 -3
- data/lib/vectory/utils.rb +80 -98
- data/lib/vectory/version.rb +1 -1
- data/vectory.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50efb043c71096f66bc72c7804b8707b6210ab4af071f231d7e81f52a0c66da0
|
4
|
+
data.tar.gz: a3ffda2f587f7796f2033fab1230db852d98e82da88b269d230646dad6acbd39
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aca9af8bee9a5f916fc0360b9473705307bfeda4b0086ae9fa53929381169a4791a483a126f98f1295574da06d70a2ea92bf3a706cd7359f91fa6b492c305205
|
7
|
+
data.tar.gz: '05286cb944c4235c5d60d5591b63be2d15c43f796fa0d63b00205ced005fe8c84f314c2d468fd5ec2a02336682d4b4c0357fd63bd6757056f95aa727ec8a3884'
|
data/README.adoc
CHANGED
@@ -173,7 +173,7 @@ link references:
|
|
173
173
|
|
174
174
|
[source,ruby]
|
175
175
|
----
|
176
|
-
xml_string = Vectory::SvgMapping.from_path("doc.xml").
|
176
|
+
xml_string = Vectory::SvgMapping.from_path("doc.xml").to_xml
|
177
177
|
----
|
178
178
|
|
179
179
|
In order to do that an initial XML should support the `svgmap` tag with links
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require "timeout"
|
2
|
+
|
3
|
+
module Vectory
|
4
|
+
module Capture
|
5
|
+
class << self
|
6
|
+
# rubocop:disable all
|
7
|
+
#
|
8
|
+
# Originally from https://gist.github.com/pasela/9392115
|
9
|
+
#
|
10
|
+
# Capture the standard output and the standard error of a command.
|
11
|
+
# Almost same as Open3.capture3 method except for timeout handling and return value.
|
12
|
+
# See Open3.capture3.
|
13
|
+
#
|
14
|
+
# result = capture3_with_timeout([env,] cmd... [, opts])
|
15
|
+
#
|
16
|
+
# The arguments env, cmd and opts are passed to Process.spawn except
|
17
|
+
# opts[:stdin_data], opts[:binmode], opts[:timeout], opts[:signal]
|
18
|
+
# and opts[:kill_after]. See Process.spawn.
|
19
|
+
#
|
20
|
+
# If opts[:stdin_data] is specified, it is sent to the command's standard input.
|
21
|
+
#
|
22
|
+
# If opts[:binmode] is true, internal pipes are set to binary mode.
|
23
|
+
#
|
24
|
+
# If opts[:timeout] is specified, SIGTERM is sent to the command after specified seconds.
|
25
|
+
#
|
26
|
+
# If opts[:signal] is specified, it is used instead of SIGTERM on timeout.
|
27
|
+
#
|
28
|
+
# If opts[:kill_after] is specified, also send a SIGKILL after specified seconds.
|
29
|
+
# it is only sent if the command is still running after the initial signal was sent.
|
30
|
+
#
|
31
|
+
# The return value is a Hash as shown below.
|
32
|
+
#
|
33
|
+
# {
|
34
|
+
# :pid => PID of the command,
|
35
|
+
# :status => Process::Status of the command,
|
36
|
+
# :stdout => the standard output of the command,
|
37
|
+
# :stderr => the standard error of the command,
|
38
|
+
# :timeout => whether the command was timed out,
|
39
|
+
# }
|
40
|
+
def with_timeout(*cmd)
|
41
|
+
spawn_opts = Hash === cmd.last ? cmd.pop.dup : {}
|
42
|
+
opts = {
|
43
|
+
:stdin_data => spawn_opts.delete(:stdin_data) || "",
|
44
|
+
:binmode => spawn_opts.delete(:binmode) || false,
|
45
|
+
:timeout => spawn_opts.delete(:timeout),
|
46
|
+
:signal => spawn_opts.delete(:signal) || :TERM,
|
47
|
+
:kill_after => spawn_opts.delete(:kill_after),
|
48
|
+
}
|
49
|
+
|
50
|
+
in_r, in_w = IO.pipe
|
51
|
+
out_r, out_w = IO.pipe
|
52
|
+
err_r, err_w = IO.pipe
|
53
|
+
in_w.sync = true
|
54
|
+
|
55
|
+
if opts[:binmode]
|
56
|
+
in_w.binmode
|
57
|
+
out_r.binmode
|
58
|
+
err_r.binmode
|
59
|
+
end
|
60
|
+
|
61
|
+
spawn_opts[:in] = in_r
|
62
|
+
spawn_opts[:out] = out_w
|
63
|
+
spawn_opts[:err] = err_w
|
64
|
+
|
65
|
+
result = {
|
66
|
+
:pid => nil,
|
67
|
+
:status => nil,
|
68
|
+
:stdout => nil,
|
69
|
+
:stderr => nil,
|
70
|
+
:timeout => false,
|
71
|
+
}
|
72
|
+
|
73
|
+
out_reader = nil
|
74
|
+
err_reader = nil
|
75
|
+
wait_thr = nil
|
76
|
+
|
77
|
+
begin
|
78
|
+
Timeout.timeout(opts[:timeout]) do
|
79
|
+
result[:pid] = spawn(*cmd, spawn_opts)
|
80
|
+
wait_thr = Process.detach(result[:pid])
|
81
|
+
in_r.close
|
82
|
+
out_w.close
|
83
|
+
err_w.close
|
84
|
+
|
85
|
+
out_reader = Thread.new { out_r.read }
|
86
|
+
err_reader = Thread.new { err_r.read }
|
87
|
+
|
88
|
+
in_w.write opts[:stdin_data]
|
89
|
+
in_w.close
|
90
|
+
|
91
|
+
result[:status] = wait_thr.value
|
92
|
+
end
|
93
|
+
rescue Timeout::Error
|
94
|
+
result[:timeout] = true
|
95
|
+
pid = spawn_opts[:pgroup] ? -result[:pid] : result[:pid]
|
96
|
+
Process.kill(opts[:signal], pid)
|
97
|
+
if opts[:kill_after]
|
98
|
+
unless wait_thr.join(opts[:kill_after])
|
99
|
+
Process.kill(:KILL, pid)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
ensure
|
103
|
+
result[:status] = wait_thr.value if wait_thr
|
104
|
+
result[:stdout] = out_reader.value if out_reader
|
105
|
+
result[:stderr] = err_reader.value if err_reader
|
106
|
+
out_r.close unless out_r.closed?
|
107
|
+
err_r.close unless err_r.closed?
|
108
|
+
end
|
109
|
+
|
110
|
+
result
|
111
|
+
end
|
112
|
+
# rubocop:enable all
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/vectory/svg_mapping.rb
CHANGED
@@ -22,23 +22,30 @@ module Vectory
|
|
22
22
|
"processing-instruction()|.//processing-instruction()".freeze
|
23
23
|
|
24
24
|
def self.from_path(path)
|
25
|
-
new(File.read(path))
|
25
|
+
new(Nokogiri::XML(File.read(path)))
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
|
28
|
+
def self.from_xml(xml)
|
29
|
+
new(Nokogiri::XML(xml))
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(doc, local_directory = "")
|
33
|
+
@doc = doc
|
30
34
|
@local_directory = local_directory
|
31
35
|
end
|
32
36
|
|
33
37
|
def call
|
34
|
-
|
35
|
-
@namespace = Namespace.new(xmldoc)
|
38
|
+
@namespace = Namespace.new(@doc)
|
36
39
|
|
37
|
-
|
40
|
+
@doc.xpath(@namespace.ns("//svgmap")).each_with_index do |svgmap, index|
|
38
41
|
process_svgmap(svgmap, index)
|
39
42
|
end
|
40
43
|
|
41
|
-
|
44
|
+
@doc
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_xml
|
48
|
+
call.to_xml
|
42
49
|
end
|
43
50
|
|
44
51
|
private
|
data/lib/vectory/system_call.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "open3"
|
2
|
+
require_relative "capture"
|
2
3
|
|
3
4
|
module Vectory
|
4
5
|
class SystemCall
|
@@ -30,9 +31,9 @@ module Vectory
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def execute(cmd)
|
33
|
-
result =
|
34
|
-
|
35
|
-
|
34
|
+
result = Capture.with_timeout(cmd,
|
35
|
+
timeout: @timeout,
|
36
|
+
kill_after: @timeout)
|
36
37
|
@stdout = result[:stdout]
|
37
38
|
@stderr = result[:stderr]
|
38
39
|
@status = result[:status]
|
data/lib/vectory/utils.rb
CHANGED
@@ -1,117 +1,99 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "base64"
|
4
|
+
require "marcel"
|
5
|
+
require "tempfile"
|
4
6
|
|
5
7
|
module Vectory
|
6
8
|
class Utils
|
7
9
|
class << self
|
8
|
-
#
|
10
|
+
# Extracted from https://github.com/metanorma/metanorma-utils/blob/v1.5.2/lib/utils/image.rb
|
9
11
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# opts[:stdin_data], opts[:binmode], opts[:timeout], opts[:signal]
|
20
|
-
# and opts[:kill_after]. See Process.spawn.
|
21
|
-
#
|
22
|
-
# If opts[:stdin_data] is specified, it is sent to the command's standard input.
|
23
|
-
#
|
24
|
-
# If opts[:binmode] is true, internal pipes are set to binary mode.
|
25
|
-
#
|
26
|
-
# If opts[:timeout] is specified, SIGTERM is sent to the command after specified seconds.
|
27
|
-
#
|
28
|
-
# If opts[:signal] is specified, it is used instead of SIGTERM on timeout.
|
29
|
-
#
|
30
|
-
# If opts[:kill_after] is specified, also send a SIGKILL after specified seconds.
|
31
|
-
# it is only sent if the command is still running after the initial signal was sent.
|
32
|
-
#
|
33
|
-
# The return value is a Hash as shown below.
|
34
|
-
#
|
35
|
-
# {
|
36
|
-
# :pid => PID of the command,
|
37
|
-
# :status => Process::Status of the command,
|
38
|
-
# :stdout => the standard output of the command,
|
39
|
-
# :stderr => the standard error of the command,
|
40
|
-
# :timeout => whether the command was timed out,
|
41
|
-
# }
|
42
|
-
def capture3_with_timeout(*cmd)
|
43
|
-
spawn_opts = Hash === cmd.last ? cmd.pop.dup : {}
|
44
|
-
opts = {
|
45
|
-
:stdin_data => spawn_opts.delete(:stdin_data) || "",
|
46
|
-
:binmode => spawn_opts.delete(:binmode) || false,
|
47
|
-
:timeout => spawn_opts.delete(:timeout),
|
48
|
-
:signal => spawn_opts.delete(:signal) || :TERM,
|
49
|
-
:kill_after => spawn_opts.delete(:kill_after),
|
50
|
-
}
|
12
|
+
# sources/plantuml/plantuml20200524-90467-1iqek5i.png
|
13
|
+
# already includes localdir
|
14
|
+
# Check whether just the local path or the other specified relative path
|
15
|
+
# works.
|
16
|
+
def datauri(uri, local_dir = ".")
|
17
|
+
(datauri?(uri) || url?(uri)) and return uri
|
18
|
+
|
19
|
+
path = path_which_exist(uri, local_dir)
|
20
|
+
path and return encode_datauri(path)
|
51
21
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
in_w.sync = true
|
22
|
+
warn "Image specified at `#{uri}` does not exist."
|
23
|
+
uri # Return original provided location
|
24
|
+
end
|
56
25
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
26
|
+
def path_which_exist(uri, local_dir)
|
27
|
+
options = absolute_path?(uri) ? [uri] : [uri, File.join(local_dir, uri)]
|
28
|
+
options.detect do |p|
|
29
|
+
File.file?(p)
|
61
30
|
end
|
31
|
+
end
|
62
32
|
|
63
|
-
|
64
|
-
|
65
|
-
spawn_opts[:err] = err_w
|
33
|
+
def encode_datauri(path)
|
34
|
+
return nil unless File.exist?(path)
|
66
35
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
36
|
+
type = Marcel::MimeType.for(Pathname.new(path)) ||
|
37
|
+
'text/plain; charset="utf-8"'
|
38
|
+
|
39
|
+
bin = File.binread(path)
|
40
|
+
data = Base64.strict_encode64(bin)
|
41
|
+
"data:#{type};base64,#{data}"
|
42
|
+
# rescue StandardError
|
43
|
+
# warn "Data-URI encoding of `#{path}` failed."
|
44
|
+
# nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def datauri?(uri)
|
48
|
+
/^data:/.match?(uri)
|
49
|
+
end
|
50
|
+
|
51
|
+
def url?(url)
|
52
|
+
%r{^[A-Z]{2,}://}i.match?(url)
|
53
|
+
end
|
54
|
+
|
55
|
+
def absolute_path?(uri)
|
56
|
+
%r{^/}.match?(uri) || %r{^[A-Z]:/}.match?(uri)
|
57
|
+
end
|
58
|
+
|
59
|
+
def svgmap_rewrite0_path(src, localdirectory)
|
60
|
+
if /^data:/.match?(src)
|
61
|
+
save_dataimage(src)
|
62
|
+
else
|
63
|
+
File.file?(src) ? src : localdirectory + src
|
64
|
+
end
|
65
|
+
end
|
74
66
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
in_r.close
|
84
|
-
out_w.close
|
85
|
-
err_w.close
|
86
|
-
|
87
|
-
out_reader = Thread.new { out_r.read }
|
88
|
-
err_reader = Thread.new { err_r.read }
|
89
|
-
|
90
|
-
in_w.write opts[:stdin_data]
|
91
|
-
in_w.close
|
92
|
-
|
93
|
-
result[:status] = wait_thr.value
|
94
|
-
end
|
95
|
-
rescue Timeout::Error
|
96
|
-
result[:timeout] = true
|
97
|
-
pid = spawn_opts[:pgroup] ? -result[:pid] : result[:pid]
|
98
|
-
Process.kill(opts[:signal], pid)
|
99
|
-
if opts[:kill_after]
|
100
|
-
unless wait_thr.join(opts[:kill_after])
|
101
|
-
Process.kill(:KILL, pid)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
ensure
|
105
|
-
result[:status] = wait_thr.value if wait_thr
|
106
|
-
result[:stdout] = out_reader.value if out_reader
|
107
|
-
result[:stderr] = err_reader.value if err_reader
|
108
|
-
out_r.close unless out_r.closed?
|
109
|
-
err_r.close unless err_r.closed?
|
67
|
+
def save_dataimage(uri)
|
68
|
+
%r{^data:(?:image|application)/(?<imgtype>[^;]+);(?:charset=[^;]+;)?base64,(?<imgdata>.+)$} =~ uri # rubocop:disable Layout/LineLength
|
69
|
+
imgtype.sub!(/\+[a-z0-9]+$/, "") # svg+xml
|
70
|
+
imgtype = "png" unless /^[a-z0-9]+$/.match? imgtype
|
71
|
+
Tempfile.open(["image", ".#{imgtype}"]) do |f|
|
72
|
+
f.binmode
|
73
|
+
f.write(Base64.strict_decode64(imgdata))
|
74
|
+
f.path
|
110
75
|
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# FIXME: This method should ONLY return 1 type, remove Array wrapper
|
79
|
+
def datauri2mime(uri)
|
80
|
+
output = decode_datauri(uri)
|
81
|
+
return nil unless output && output[:type_detected]
|
111
82
|
|
112
|
-
|
83
|
+
[output[:type_detected]]
|
84
|
+
end
|
85
|
+
|
86
|
+
def decode_datauri(uri)
|
87
|
+
%r{^data:(?<mimetype>[^;]+);base64,(?<mimedata>.+)$} =~ uri
|
88
|
+
return nil unless mimetype && mimedata
|
89
|
+
|
90
|
+
data = Base64.strict_decode64(mimedata)
|
91
|
+
{
|
92
|
+
type_declared: mimetype,
|
93
|
+
type_detected: Marcel::MimeType.for(data, declared_type: mimetype),
|
94
|
+
data: data,
|
95
|
+
}
|
113
96
|
end
|
114
|
-
# rubocop:enable all
|
115
97
|
end
|
116
98
|
end
|
117
99
|
end
|
data/lib/vectory/version.rb
CHANGED
data/vectory.gemspec
CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.required_ruby_version = ">= 2.5.0"
|
28
28
|
|
29
29
|
spec.add_runtime_dependency "emf2svg"
|
30
|
+
spec.add_runtime_dependency "marcel", "~> 1.0.0"
|
30
31
|
spec.add_runtime_dependency "nokogiri", "~> 1.14"
|
31
32
|
spec.add_runtime_dependency "thor", "~> 1.2.1"
|
32
33
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vectory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: emf2svg
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: marcel
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.0
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: nokogiri
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -73,6 +87,7 @@ files:
|
|
73
87
|
- Rakefile
|
74
88
|
- exe/vectory
|
75
89
|
- lib/vectory.rb
|
90
|
+
- lib/vectory/capture.rb
|
76
91
|
- lib/vectory/cli.rb
|
77
92
|
- lib/vectory/datauri.rb
|
78
93
|
- lib/vectory/emf.rb
|