vectory 0.10.2 → 0.12.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 +4 -6
- data/lib/vectory/cli.rb +0 -4
- data/lib/vectory/configuration.rb +3 -31
- data/lib/vectory/emf.rb +25 -12
- data/lib/vectory/eps.rb +2 -14
- data/lib/vectory/errors.rb +0 -14
- data/lib/vectory/ps.rb +2 -14
- data/lib/vectory/svg.rb +25 -34
- data/lib/vectory/vector.rb +6 -2
- data/lib/vectory/version.rb +1 -1
- data/lib/vectory.rb +0 -10
- data/scripts/regenerate_fixtures.rb +33 -0
- data/vectory.gemspec +1 -2
- metadata +4 -25
- data/lib/vectory/conversion/ghostscript_strategy.rb +0 -74
- data/lib/vectory/conversion/inkscape_strategy.rb +0 -121
- data/lib/vectory/conversion/strategy.rb +0 -58
- data/lib/vectory/conversion.rb +0 -104
- data/lib/vectory/ghostscript_wrapper.rb +0 -183
- data/lib/vectory/inkscape_wrapper.rb +0 -226
- data/lib/vectory/pdf.rb +0 -116
- data/lib/vectory/platform.rb +0 -105
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a9abbde7cd6a7876ad8a2f8077786e870dcf860b3206bef30f061f997145d31
|
|
4
|
+
data.tar.gz: 05c8f3c7a6ab01aa09e3facc83d145ba17a6a8ea7dde5e2bba02ab1405d49999
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a9651cbe852b2ba5dbdbdb07db31105aa33bf9a41bcc7bd0cb4f621c679ba41f4343f85ae0d4f3722a34cfaab2cb4cb80210e2aa383b71d7df34256b36a781ac
|
|
7
|
+
data.tar.gz: c03d30678dfa73b41dac5b987cd70905fc0edc2dcd5621a7bdfb2d61bf8be11769f1d4135602c93aa7b51b243e05b915d0c7b0e55f5002badc50d45365863e3a
|
data/README.adoc
CHANGED
|
@@ -21,14 +21,12 @@ ____
|
|
|
21
21
|
|
|
22
22
|
=== Prerequisites
|
|
23
23
|
|
|
24
|
-
Vectory
|
|
24
|
+
Vectory ships as a pure-Ruby gem. Format support is provided by these Ruby gems (installed automatically):
|
|
25
25
|
|
|
26
|
-
* https://github.com/claricle/emfsvg[emfsvg]
|
|
27
|
-
* https://
|
|
28
|
-
* https://www.ghostscript.com/[Ghostscript]
|
|
26
|
+
* https://github.com/claricle/emfsvg[emfsvg] (EMF <-> SVG)
|
|
27
|
+
* https://github.com/claricle/postsvg[postsvg] (PS/EPS <-> SVG)
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
this issue, the 1.3.0 version of Inkscape can be used.
|
|
29
|
+
No external binaries are required.
|
|
32
30
|
|
|
33
31
|
|
|
34
32
|
=== Gem install
|
data/lib/vectory/cli.rb
CHANGED
|
@@ -9,14 +9,10 @@ module Vectory
|
|
|
9
9
|
STATUS_UNSUPPORTED_OUTPUT_FORMAT_ERROR = 3
|
|
10
10
|
STATUS_CONVERSION_ERROR = 4
|
|
11
11
|
STATUS_SYSTEM_CALL_ERROR = 5
|
|
12
|
-
STATUS_INKSCAPE_NOT_FOUND_ERROR = 6
|
|
13
12
|
STATUS_SAME_FORMAT_ERROR = 7
|
|
14
|
-
STATUS_GHOSTSCRIPT_NOT_FOUND_ERROR = 8
|
|
15
13
|
|
|
16
14
|
MAP_ERROR_TO_STATUS = {
|
|
17
15
|
Vectory::ConversionError => STATUS_CONVERSION_ERROR,
|
|
18
|
-
Vectory::InkscapeNotFoundError => STATUS_INKSCAPE_NOT_FOUND_ERROR,
|
|
19
|
-
Vectory::GhostscriptNotFoundError => STATUS_GHOSTSCRIPT_NOT_FOUND_ERROR,
|
|
20
16
|
Vectory::SystemCallError => STATUS_SYSTEM_CALL_ERROR,
|
|
21
17
|
}.freeze
|
|
22
18
|
|
|
@@ -3,14 +3,10 @@
|
|
|
3
3
|
module Vectory
|
|
4
4
|
# Configuration for Vectory
|
|
5
5
|
#
|
|
6
|
-
# Provides centralized configuration for
|
|
6
|
+
# Provides centralized configuration for timeouts, caching, etc.
|
|
7
7
|
# Can be loaded from environment variables or a configuration file.
|
|
8
8
|
#
|
|
9
|
-
# @example Set custom Inkscape path
|
|
10
|
-
# Vectory::Configuration.instance.inkscape_path = "/path/to/inkscape"
|
|
11
|
-
#
|
|
12
9
|
# @example Load from environment variables
|
|
13
|
-
# # Set VECTORY_INKSCAPE_PATH environment variable
|
|
14
10
|
# Vectory::Configuration.load_from_environment
|
|
15
11
|
class Configuration
|
|
16
12
|
# Default timeout for external tool execution (seconds)
|
|
@@ -22,8 +18,8 @@ module Vectory
|
|
|
22
18
|
# Default temporary directory
|
|
23
19
|
DEFAULT_TEMP_DIR = nil # Use system default
|
|
24
20
|
|
|
25
|
-
attr_accessor :
|
|
26
|
-
:
|
|
21
|
+
attr_accessor :timeout, :cache_ttl, :cache_enabled, :temp_dir,
|
|
22
|
+
:verbose_logging
|
|
27
23
|
|
|
28
24
|
# Get the singleton instance
|
|
29
25
|
#
|
|
@@ -46,15 +42,11 @@ module Vectory
|
|
|
46
42
|
@cache_enabled = true
|
|
47
43
|
@temp_dir = DEFAULT_TEMP_DIR
|
|
48
44
|
@verbose_logging = false
|
|
49
|
-
@inkscape_path = nil
|
|
50
|
-
@ghostscript_path = nil
|
|
51
45
|
end
|
|
52
46
|
|
|
53
47
|
# Load configuration from environment variables
|
|
54
48
|
#
|
|
55
49
|
# Supported environment variables:
|
|
56
|
-
# - VECTORY_INKSCAPE_PATH: Path to Inkscape executable
|
|
57
|
-
# - VECTORY_GHOSTSCRIPT_PATH: Path to Ghostscript executable
|
|
58
50
|
# - VECTORY_TIMEOUT: Timeout for external tools (default: 120)
|
|
59
51
|
# - VECTORY_CACHE_TTL: Cache TTL in seconds (default: 300)
|
|
60
52
|
# - VECTORY_CACHE_ENABLED: Enable/disable caching (default: true)
|
|
@@ -65,8 +57,6 @@ module Vectory
|
|
|
65
57
|
def self.load_from_environment
|
|
66
58
|
config = instance
|
|
67
59
|
|
|
68
|
-
config.inkscape_path = ENV["VECTORY_INKSCAPE_PATH"] if ENV["VECTORY_INKSCAPE_PATH"]
|
|
69
|
-
config.ghostscript_path = ENV["VECTORY_GHOSTSCRIPT_PATH"] if ENV["VECTORY_GHOSTSCRIPT_PATH"]
|
|
70
60
|
config.timeout = ENV["VECTORY_TIMEOUT"]&.to_i || config.timeout
|
|
71
61
|
config.cache_ttl = ENV["VECTORY_CACHE_TTL"]&.to_i || config.cache_ttl
|
|
72
62
|
config.cache_enabled = ENV["VECTORY_CACHE_ENABLED"] != "false"
|
|
@@ -86,8 +76,6 @@ module Vectory
|
|
|
86
76
|
config_data = YAML.load_file(path)
|
|
87
77
|
|
|
88
78
|
config = instance
|
|
89
|
-
config.inkscape_path = config_data["inkscape_path"] if config_data["inkscape_path"]
|
|
90
|
-
config.ghostscript_path = config_data["ghostscript_path"] if config_data["ghostscript_path"]
|
|
91
79
|
config.timeout = config_data["timeout"] || config.timeout
|
|
92
80
|
config.cache_ttl = config_data["cache_ttl"] || config.cache_ttl
|
|
93
81
|
config.cache_enabled = config_data.fetch("cache_enabled",
|
|
@@ -104,8 +92,6 @@ module Vectory
|
|
|
104
92
|
# @return [Hash] the configuration as a hash
|
|
105
93
|
def to_h
|
|
106
94
|
{
|
|
107
|
-
inkscape_path: @inkscape_path,
|
|
108
|
-
ghostscript_path: @ghostscript_path,
|
|
109
95
|
timeout: @timeout,
|
|
110
96
|
cache_ttl: @cache_ttl,
|
|
111
97
|
cache_enabled: @cache_enabled,
|
|
@@ -114,20 +100,6 @@ module Vectory
|
|
|
114
100
|
}
|
|
115
101
|
end
|
|
116
102
|
|
|
117
|
-
# Get the Inkscape path (custom or auto-detected)
|
|
118
|
-
#
|
|
119
|
-
# @return [String, nil] the configured Inkscape path or nil if not set
|
|
120
|
-
def effective_inkscape_path
|
|
121
|
-
@inkscape_path
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Get the Ghostscript path (custom or auto-detected)
|
|
125
|
-
#
|
|
126
|
-
# @return [String, nil] the configured Ghostscript path or nil if not set
|
|
127
|
-
def effective_ghostscript_path
|
|
128
|
-
@ghostscript_path
|
|
129
|
-
end
|
|
130
|
-
|
|
131
103
|
# Check if caching is enabled
|
|
132
104
|
#
|
|
133
105
|
# @return [Boolean] true if caching is enabled
|
data/lib/vectory/emf.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "emf"
|
|
3
4
|
require "emfsvg"
|
|
4
5
|
|
|
5
6
|
module Vectory
|
|
@@ -32,21 +33,33 @@ module Vectory
|
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
def to_eps
|
|
35
|
-
|
|
36
|
-
content: content,
|
|
37
|
-
input_format: :emf,
|
|
38
|
-
output_format: :eps,
|
|
39
|
-
output_class: Eps,
|
|
40
|
-
)
|
|
36
|
+
to_svg.to_eps
|
|
41
37
|
end
|
|
42
38
|
|
|
43
39
|
def to_ps
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
to_svg.to_ps
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def height
|
|
44
|
+
bounds = parse_header_bounds
|
|
45
|
+
return super unless bounds
|
|
46
|
+
|
|
47
|
+
bounds.bottom - bounds.top
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def width
|
|
51
|
+
bounds = parse_header_bounds
|
|
52
|
+
return super unless bounds
|
|
53
|
+
|
|
54
|
+
bounds.right - bounds.left
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def parse_header_bounds
|
|
60
|
+
::Emf.parse(content).header.bounds
|
|
61
|
+
rescue StandardError
|
|
62
|
+
nil
|
|
50
63
|
end
|
|
51
64
|
end
|
|
52
65
|
end
|
data/lib/vectory/eps.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Vectory
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def to_ps
|
|
25
|
-
|
|
25
|
+
to_svg.to_ps
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def to_svg
|
|
@@ -30,19 +30,7 @@ module Vectory
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def to_emf
|
|
33
|
-
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def to_pdf
|
|
37
|
-
pdf_content = GhostscriptWrapper.convert(content, eps_crop: true)
|
|
38
|
-
pdf = Pdf.new(pdf_content)
|
|
39
|
-
# Pass original BoundingBox dimensions to preserve them in conversions
|
|
40
|
-
bbox = parse_bounding_box
|
|
41
|
-
if bbox
|
|
42
|
-
pdf.original_width = bbox[:urx] - bbox[:llx]
|
|
43
|
-
pdf.original_height = bbox[:ury] - bbox[:lly]
|
|
44
|
-
end
|
|
45
|
-
pdf
|
|
33
|
+
to_svg.to_emf
|
|
46
34
|
end
|
|
47
35
|
|
|
48
36
|
def height
|
data/lib/vectory/errors.rb
CHANGED
|
@@ -3,20 +3,6 @@
|
|
|
3
3
|
module Vectory
|
|
4
4
|
class ConversionError < Error; end
|
|
5
5
|
|
|
6
|
-
class InkscapeNotFoundError < Error
|
|
7
|
-
def initialize(msg = nil)
|
|
8
|
-
super(msg || "Inkscape not found in PATH. Please install Inkscape.")
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
class GhostscriptNotFoundError < Error
|
|
13
|
-
def initialize(msg = nil)
|
|
14
|
-
super(msg || "Ghostscript not found in PATH. Please install Ghostscript.")
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
class InkscapeQueryError < Error; end
|
|
19
|
-
|
|
20
6
|
class InvalidFormatError < Error
|
|
21
7
|
def initialize(format, supported_formats)
|
|
22
8
|
super("Invalid format '#{format}'. Supported formats: #{supported_formats.join(', ')}")
|
data/lib/vectory/ps.rb
CHANGED
|
@@ -22,29 +22,17 @@ module Vectory
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def to_eps
|
|
25
|
-
|
|
25
|
+
to_svg.to_eps
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def to_emf
|
|
29
|
-
|
|
29
|
+
to_svg.to_emf
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def to_svg
|
|
33
33
|
Svg.from_content(Postsvg.convert(content))
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def to_pdf
|
|
37
|
-
pdf_content = GhostscriptWrapper.convert(content, eps_crop: false)
|
|
38
|
-
pdf = Pdf.new(pdf_content)
|
|
39
|
-
# Pass original BoundingBox dimensions to preserve them in conversions
|
|
40
|
-
bbox = parse_bounding_box
|
|
41
|
-
if bbox
|
|
42
|
-
pdf.original_width = bbox[:urx] - bbox[:llx]
|
|
43
|
-
pdf.original_height = bbox[:ury] - bbox[:lly]
|
|
44
|
-
end
|
|
45
|
-
pdf
|
|
46
|
-
end
|
|
47
|
-
|
|
48
36
|
def height
|
|
49
37
|
bbox = parse_bounding_box
|
|
50
38
|
return super unless bbox
|
data/lib/vectory/svg.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "nokogiri"
|
|
4
|
+
require "postsvg"
|
|
4
5
|
|
|
5
6
|
module Vectory
|
|
6
7
|
class Svg < Vector
|
|
@@ -36,53 +37,43 @@ module Vectory
|
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def to_eps
|
|
39
|
-
|
|
40
|
-
content: content,
|
|
41
|
-
input_format: :svg,
|
|
42
|
-
output_format: :eps,
|
|
43
|
-
output_class: Eps,
|
|
44
|
-
)
|
|
40
|
+
Eps.from_content(Postsvg.to_eps(content))
|
|
45
41
|
end
|
|
46
42
|
|
|
47
43
|
def to_ps
|
|
48
|
-
|
|
49
|
-
content: content,
|
|
50
|
-
input_format: :svg,
|
|
51
|
-
output_format: :ps,
|
|
52
|
-
output_class: Ps,
|
|
53
|
-
)
|
|
44
|
+
Ps.from_content(Postsvg.to_ps(content))
|
|
54
45
|
end
|
|
55
46
|
|
|
56
47
|
def height
|
|
57
|
-
|
|
58
|
-
doc = Nokogiri::XML(content)
|
|
59
|
-
svg_element = doc.at_xpath("//svg:svg",
|
|
60
|
-
"svg" => SVG_NS) || doc.at_xpath("//svg")
|
|
61
|
-
|
|
62
|
-
if svg_element && svg_element["height"]
|
|
63
|
-
svg_element["height"].to_f.round
|
|
64
|
-
else
|
|
65
|
-
# Fall back to Inkscape query if no height attribute
|
|
66
|
-
super
|
|
67
|
-
end
|
|
48
|
+
dim_from_attr("height") || dim_from_viewbox(3) || super
|
|
68
49
|
end
|
|
69
50
|
|
|
70
51
|
def width
|
|
71
|
-
|
|
72
|
-
doc = Nokogiri::XML(content)
|
|
73
|
-
svg_element = doc.at_xpath("//svg:svg",
|
|
74
|
-
"svg" => SVG_NS) || doc.at_xpath("//svg")
|
|
75
|
-
|
|
76
|
-
if svg_element && svg_element["width"]
|
|
77
|
-
svg_element["width"].to_f.round
|
|
78
|
-
else
|
|
79
|
-
# Fall back to Inkscape query if no width attribute
|
|
80
|
-
super
|
|
81
|
-
end
|
|
52
|
+
dim_from_attr("width") || dim_from_viewbox(2) || super
|
|
82
53
|
end
|
|
83
54
|
|
|
84
55
|
private
|
|
85
56
|
|
|
57
|
+
def svg_root
|
|
58
|
+
doc = Nokogiri::XML(content)
|
|
59
|
+
doc.at_xpath("//svg:svg", "svg" => SVG_NS) || doc.at_xpath("//svg")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def dim_from_attr(name)
|
|
63
|
+
value = svg_root&.[](name)
|
|
64
|
+
value&.to_f&.round
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def dim_from_viewbox(index)
|
|
68
|
+
vb = svg_root&.[]("viewBox")
|
|
69
|
+
return nil unless vb
|
|
70
|
+
|
|
71
|
+
parts = vb.split
|
|
72
|
+
return nil unless parts.length == 4
|
|
73
|
+
|
|
74
|
+
parts[index].to_f.round
|
|
75
|
+
end
|
|
76
|
+
|
|
86
77
|
def content=(content)
|
|
87
78
|
# non-root node inserts the xml tag which breaks markup when placed in
|
|
88
79
|
# another xml document
|
data/lib/vectory/vector.rb
CHANGED
|
@@ -46,11 +46,15 @@ module Vectory
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def height
|
|
49
|
-
|
|
49
|
+
raise Vectory::NotImplementedError,
|
|
50
|
+
"#height should be implemented in a subclass when no intrinsic " \
|
|
51
|
+
"dimension source is available."
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
def width
|
|
53
|
-
|
|
55
|
+
raise Vectory::NotImplementedError,
|
|
56
|
+
"#width should be implemented in a subclass when no intrinsic " \
|
|
57
|
+
"dimension source is available."
|
|
54
58
|
end
|
|
55
59
|
|
|
56
60
|
def to_uri
|
data/lib/vectory/version.rb
CHANGED
data/lib/vectory.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
# External dependencies
|
|
4
4
|
require "logger"
|
|
5
|
-
require "ukiryu"
|
|
6
5
|
|
|
7
6
|
# Define base error class and additional error classes
|
|
8
7
|
# (used in class bodies like cli.rb, so can't be autoloaded)
|
|
@@ -25,14 +24,6 @@ require_relative "vectory/version"
|
|
|
25
24
|
module Vectory
|
|
26
25
|
# Core utilities
|
|
27
26
|
autoload :Utils, "vectory/utils"
|
|
28
|
-
autoload :Platform, "vectory/platform"
|
|
29
|
-
|
|
30
|
-
# Wrappers
|
|
31
|
-
autoload :GhostscriptWrapper, "vectory/ghostscript_wrapper"
|
|
32
|
-
autoload :InkscapeWrapper, "vectory/inkscape_wrapper"
|
|
33
|
-
|
|
34
|
-
# Conversion system
|
|
35
|
-
autoload :Conversion, "vectory/conversion"
|
|
36
27
|
|
|
37
28
|
# Format classes
|
|
38
29
|
autoload :Configuration, "vectory/configuration"
|
|
@@ -40,7 +31,6 @@ module Vectory
|
|
|
40
31
|
autoload :ImageResize, "vectory/image_resize"
|
|
41
32
|
autoload :Datauri, "vectory/datauri"
|
|
42
33
|
autoload :Vector, "vectory/vector"
|
|
43
|
-
autoload :Pdf, "vectory/pdf"
|
|
44
34
|
autoload :Eps, "vectory/eps"
|
|
45
35
|
autoload :Ps, "vectory/ps"
|
|
46
36
|
autoload :Emf, "vectory/emf"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# One-shot fixture regeneration script.
|
|
5
|
+
# Run from the vectory root: `bundle exec ruby scripts/regenerate_fixtures.rb`
|
|
6
|
+
|
|
7
|
+
require_relative "../lib/vectory"
|
|
8
|
+
|
|
9
|
+
Pairs = [
|
|
10
|
+
["spec/examples/emf2eps/img.emf", :to_eps, "spec/examples/emf2eps/ref.eps"],
|
|
11
|
+
["spec/examples/emf2ps/img.emf", :to_ps, "spec/examples/emf2ps/ref.ps"],
|
|
12
|
+
["spec/examples/eps2emf/img.eps", :to_emf, "spec/examples/eps2emf/ref.emf"],
|
|
13
|
+
["spec/examples/eps2ps/img.eps", :to_ps, "spec/examples/eps2ps/ref.ps"],
|
|
14
|
+
["spec/examples/eps2svg/img.eps", :to_svg, "spec/examples/eps2svg/ref.svg"],
|
|
15
|
+
["spec/examples/ps2emf/img.ps", :to_emf, "spec/examples/ps2emf/ref.emf"],
|
|
16
|
+
["spec/examples/ps2eps/img.ps", :to_eps, "spec/examples/ps2eps/ref.eps"],
|
|
17
|
+
["spec/examples/ps2svg/img.ps", :to_svg, "spec/examples/ps2svg/ref.svg"],
|
|
18
|
+
["spec/examples/svg2emf/img.svg", :to_emf, "spec/examples/svg2emf/ref.emf"],
|
|
19
|
+
["spec/examples/svg2eps/img.svg", :to_eps, "spec/examples/svg2eps/ref.eps"],
|
|
20
|
+
["spec/examples/svg2ps/img.svg", :to_ps, "spec/examples/svg2ps/ref.ps"],
|
|
21
|
+
].freeze
|
|
22
|
+
|
|
23
|
+
Pairs.each do |input, method, output|
|
|
24
|
+
ext = File.extname(input).delete(".")
|
|
25
|
+
klass_name = { "emf" => "Emf", "eps" => "Eps", "ps" => "Ps",
|
|
26
|
+
"svg" => "Svg" }.fetch(ext)
|
|
27
|
+
klass = Vectory.const_get(klass_name)
|
|
28
|
+
result = klass.from_path(input).public_send(method)
|
|
29
|
+
File.binwrite(output, result.content)
|
|
30
|
+
puts "[OK] #{input} -> #{output} (#{result.content.bytesize} bytes)"
|
|
31
|
+
rescue => e
|
|
32
|
+
puts "[FAIL] #{input} -> #{output}: #{e.class}: #{e.message}"
|
|
33
|
+
end
|
data/vectory.gemspec
CHANGED
|
@@ -31,7 +31,6 @@ Gem::Specification.new do |spec|
|
|
|
31
31
|
spec.add_dependency "image_size", ">= 3.2.0"
|
|
32
32
|
spec.add_dependency "marcel", "~> 1.0"
|
|
33
33
|
spec.add_dependency "nokogiri", "~> 1.14"
|
|
34
|
-
spec.add_dependency "postsvg", "~> 0.
|
|
34
|
+
spec.add_dependency "postsvg", "~> 0.3"
|
|
35
35
|
spec.add_dependency "thor", "~> 1.0"
|
|
36
|
-
spec.add_dependency "ukiryu", "~> 0.3.0"
|
|
37
36
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vectory
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -92,14 +92,14 @@ dependencies:
|
|
|
92
92
|
requirements:
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '0.
|
|
95
|
+
version: '0.3'
|
|
96
96
|
type: :runtime
|
|
97
97
|
prerelease: false
|
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
|
100
100
|
- - "~>"
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '0.
|
|
102
|
+
version: '0.3'
|
|
103
103
|
- !ruby/object:Gem::Dependency
|
|
104
104
|
name: thor
|
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -114,20 +114,6 @@ dependencies:
|
|
|
114
114
|
- - "~>"
|
|
115
115
|
- !ruby/object:Gem::Version
|
|
116
116
|
version: '1.0'
|
|
117
|
-
- !ruby/object:Gem::Dependency
|
|
118
|
-
name: ukiryu
|
|
119
|
-
requirement: !ruby/object:Gem::Requirement
|
|
120
|
-
requirements:
|
|
121
|
-
- - "~>"
|
|
122
|
-
- !ruby/object:Gem::Version
|
|
123
|
-
version: 0.3.0
|
|
124
|
-
type: :runtime
|
|
125
|
-
prerelease: false
|
|
126
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
127
|
-
requirements:
|
|
128
|
-
- - "~>"
|
|
129
|
-
- !ruby/object:Gem::Version
|
|
130
|
-
version: 0.3.0
|
|
131
117
|
description: |
|
|
132
118
|
Vectory performs pairwise vector image conversions for common
|
|
133
119
|
vector image formats, such as SVG, EMF, EPS and PS.
|
|
@@ -172,21 +158,13 @@ files:
|
|
|
172
158
|
- lib/vectory.rb
|
|
173
159
|
- lib/vectory/cli.rb
|
|
174
160
|
- lib/vectory/configuration.rb
|
|
175
|
-
- lib/vectory/conversion.rb
|
|
176
|
-
- lib/vectory/conversion/ghostscript_strategy.rb
|
|
177
|
-
- lib/vectory/conversion/inkscape_strategy.rb
|
|
178
|
-
- lib/vectory/conversion/strategy.rb
|
|
179
161
|
- lib/vectory/datauri.rb
|
|
180
162
|
- lib/vectory/emf.rb
|
|
181
163
|
- lib/vectory/eps.rb
|
|
182
164
|
- lib/vectory/errors.rb
|
|
183
165
|
- lib/vectory/file_magic.rb
|
|
184
|
-
- lib/vectory/ghostscript_wrapper.rb
|
|
185
166
|
- lib/vectory/image.rb
|
|
186
167
|
- lib/vectory/image_resize.rb
|
|
187
|
-
- lib/vectory/inkscape_wrapper.rb
|
|
188
|
-
- lib/vectory/pdf.rb
|
|
189
|
-
- lib/vectory/platform.rb
|
|
190
168
|
- lib/vectory/ps.rb
|
|
191
169
|
- lib/vectory/svg.rb
|
|
192
170
|
- lib/vectory/svg_document.rb
|
|
@@ -194,6 +172,7 @@ files:
|
|
|
194
172
|
- lib/vectory/utils.rb
|
|
195
173
|
- lib/vectory/vector.rb
|
|
196
174
|
- lib/vectory/version.rb
|
|
175
|
+
- scripts/regenerate_fixtures.rb
|
|
197
176
|
- tmp/.keep
|
|
198
177
|
- vectory.gemspec
|
|
199
178
|
homepage: https://github.com/metanorma/vectory
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Vectory
|
|
4
|
-
module Conversion
|
|
5
|
-
# Ghostscript-based conversion strategy
|
|
6
|
-
#
|
|
7
|
-
# Handles PS/EPS → PDF conversions using Ghostscript.
|
|
8
|
-
# Ghostscript is used for its accurate BoundingBox preservation.
|
|
9
|
-
#
|
|
10
|
-
# @see https://www.ghostscript.com/
|
|
11
|
-
class GhostscriptStrategy < Strategy
|
|
12
|
-
# Ghostscript supports PS/EPS → PDF conversions
|
|
13
|
-
SUPPORTED_CONVERSIONS = [
|
|
14
|
-
%i[ps pdf],
|
|
15
|
-
%i[eps pdf],
|
|
16
|
-
].freeze
|
|
17
|
-
|
|
18
|
-
# Convert PS/EPS content to PDF using Ghostscript
|
|
19
|
-
#
|
|
20
|
-
# @param content [String] the PS/EPS content to convert
|
|
21
|
-
# @param input_format [Symbol] the input format (:ps or :eps)
|
|
22
|
-
# @param output_format [Symbol] the output format (must be :pdf)
|
|
23
|
-
# @param options [Hash] additional options
|
|
24
|
-
# @option options [Boolean] :eps_crop use EPSCrop for better BoundingBox handling
|
|
25
|
-
# @return [String] the PDF content
|
|
26
|
-
# @raise [Vectory::GhostscriptNotFoundError] if Ghostscript is not available
|
|
27
|
-
# @raise [Vectory::ConversionError] if conversion fails
|
|
28
|
-
def convert(content, input_format:, output_format:, **options)
|
|
29
|
-
unless output_format == :pdf
|
|
30
|
-
raise ArgumentError,
|
|
31
|
-
"Ghostscript only supports PDF output, got: #{output_format}"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
unless %i[ps eps].include?(input_format)
|
|
35
|
-
raise ArgumentError,
|
|
36
|
-
"Ghostscript only supports PS/EPS input, got: #{input_format}"
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
GhostscriptWrapper.convert(content,
|
|
40
|
-
eps_crop: options[:eps_crop] || false)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Check if this conversion is supported
|
|
44
|
-
#
|
|
45
|
-
# @param input_format [Symbol] the input format
|
|
46
|
-
# @param output_format [Symbol] the output format
|
|
47
|
-
# @return [Boolean] true if Ghostscript supports this conversion
|
|
48
|
-
def supports?(input_format, output_format)
|
|
49
|
-
SUPPORTED_CONVERSIONS.include?([input_format, output_format])
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Get supported conversions
|
|
53
|
-
#
|
|
54
|
-
# @return [Array<Array<Symbol>>] array of [input, output] format pairs
|
|
55
|
-
def supported_conversions
|
|
56
|
-
SUPPORTED_CONVERSIONS
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Check if Ghostscript is available
|
|
60
|
-
#
|
|
61
|
-
# @return [Boolean] true if Ghostscript can be found
|
|
62
|
-
def available?
|
|
63
|
-
GhostscriptWrapper.available?
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Get the tool name
|
|
67
|
-
#
|
|
68
|
-
# @return [String] "ghostscript"
|
|
69
|
-
def tool_name
|
|
70
|
-
"ghostscript"
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|