vectory 0.10.1 → 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/docs/features/conversion.adoc +11 -10
- data/docs/features/index.adoc +14 -7
- data/docs/getting-started/core-concepts.adoc +2 -1
- 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 +5 -15
- data/lib/vectory/errors.rb +0 -14
- data/lib/vectory/ps.rb +5 -15
- 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 -1
- metadata +9 -16
- 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
|
|
@@ -32,21 +32,22 @@ svg_to_eps = Vectory::Svg.from_path("diagram.svg").to_eps
|
|
|
32
32
|
eps_to_svg = Vectory::Eps.from_path("diagram.eps").to_svg
|
|
33
33
|
----
|
|
34
34
|
|
|
35
|
-
===
|
|
35
|
+
=== Direct Conversions (via postsvg)
|
|
36
36
|
|
|
37
|
-
EPS and PS files are converted to SVG
|
|
38
|
-
|
|
39
|
-
1. **EPS/PS → PDF** (via Ghostscript): Preserves BoundingBox information
|
|
40
|
-
2. **PDF → SVG** (via Inkscape): Converts PDF to SVG
|
|
37
|
+
EPS and PS files are converted to SVG directly by the postsvg gem (pure Ruby, no Ghostscript or Inkscape):
|
|
41
38
|
|
|
42
39
|
[source,ruby]
|
|
43
40
|
----
|
|
44
|
-
# Two-step conversion (automatic)
|
|
45
41
|
eps = Vectory::Eps.from_path("diagram.eps")
|
|
46
|
-
svg = eps.to_svg #
|
|
42
|
+
svg = eps.to_svg # Uses postsvg directly
|
|
43
|
+
|
|
44
|
+
ps = Vectory::Ps.from_path("diagram.ps")
|
|
45
|
+
svg = ps.to_svg # Also postsvg
|
|
47
46
|
----
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
postsvg reads the `%%BoundingBox` DSC comment to size the viewBox. Limitations: text/font rendering, image embedding, gradients/patterns, CMYK, and PS Level 2/3 advanced operators are not yet supported by postsvg upstream — EPS/PS files using those features will silently degrade.
|
|
49
|
+
|
|
50
|
+
=== Two-Step Conversions (EPS/PS → PDF/PS/EMF)
|
|
50
51
|
|
|
51
52
|
=== EMF Conversions (via emfsvg)
|
|
52
53
|
|
|
@@ -73,11 +74,11 @@ emf = Vectory::Svg.from_path("chart.svg").to_emf
|
|
|
73
74
|
|SVG|EMF|emfsvg gem
|
|
74
75
|
|SVG|PDF|Inkscape
|
|
75
76
|
|
|
76
|
-
|EPS|SVG|
|
|
77
|
+
|EPS|SVG|postsvg gem
|
|
77
78
|
|EPS|PDF|Ghostscript
|
|
78
79
|
|EPS|PS|Via PDF intermediate
|
|
79
80
|
|
|
80
|
-
|PS|SVG|
|
|
81
|
+
|PS|SVG|postsvg gem
|
|
81
82
|
|PS|PDF|Ghostscript
|
|
82
83
|
|PS|EPS|Via PDF intermediate
|
|
83
84
|
|
data/docs/features/index.adoc
CHANGED
|
@@ -68,23 +68,30 @@ Vectory uses different external tools depending on the source and target formats
|
|
|
68
68
|
|
|
69
69
|
=== Direct Conversions (via Inkscape)
|
|
70
70
|
|
|
71
|
-
* **SVG → EPS/PS/
|
|
72
|
-
* **
|
|
71
|
+
* **SVG → EPS/PS/PDF** (via Inkscape export)
|
|
72
|
+
* **PDF → SVG** (via Inkscape import)
|
|
73
73
|
|
|
74
74
|
Inkscape handles direct conversions to and from SVG format.
|
|
75
75
|
|
|
76
76
|
=== Direct Conversions (via emfsvg)
|
|
77
77
|
|
|
78
|
-
* **
|
|
78
|
+
* **SVG ↔ EMF** (via emfsvg gem, both directions)
|
|
79
79
|
|
|
80
|
-
The emfsvg gem provides
|
|
80
|
+
The emfsvg gem provides bidirectional EMF ↔ SVG conversion in pure Ruby, with no external tool dependency.
|
|
81
|
+
|
|
82
|
+
=== Direct Conversions (via postsvg)
|
|
83
|
+
|
|
84
|
+
* **EPS → SVG** (via postsvg gem)
|
|
85
|
+
* **PS → SVG** (via postsvg gem)
|
|
86
|
+
|
|
87
|
+
The postsvg gem converts PS/EPS to SVG in pure Ruby, with no external tool dependency.
|
|
81
88
|
|
|
82
89
|
=== Two-Step Conversions (via Ghostscript + Inkscape)
|
|
83
90
|
|
|
84
|
-
* **EPS → PDF → SVG** (
|
|
85
|
-
* **PS → PDF → SVG** (
|
|
91
|
+
* **EPS → PDF → SVG** (legacy path; replaced by postsvg for `Eps#to_svg`)
|
|
92
|
+
* **PS → PDF → SVG** (legacy path; replaced by postsvg for `Ps#to_svg`)
|
|
86
93
|
|
|
87
|
-
|
|
94
|
+
EPS/PS → PDF and EPS/PS → PS/EMF (via PDF intermediate) still go through Ghostscript.
|
|
88
95
|
|
|
89
96
|
=== Conversion Tool Matrix
|
|
90
97
|
|
|
@@ -95,11 +95,12 @@ Vectory uses a flexible conversion pipeline:
|
|
|
95
95
|
Some conversions are direct:
|
|
96
96
|
* **SVG → EPS/PS/PDF**: Via Inkscape
|
|
97
97
|
* **SVG ↔ EMF**: Via emfsvg (both directions, pure Ruby)
|
|
98
|
+
* **EPS → SVG** and **PS → SVG**: Via postsvg (pure Ruby)
|
|
98
99
|
|
|
99
100
|
=== Two-Step Conversions
|
|
100
101
|
|
|
101
102
|
Other conversions use an intermediate PDF format:
|
|
102
|
-
* **EPS/PS → PDF →
|
|
103
|
+
* **EPS/PS → PDF → {PS,EMF}**: Via Ghostscript + Inkscape
|
|
103
104
|
|
|
104
105
|
This two-step conversion ensures:
|
|
105
106
|
* BoundingBox preservation from EPS/PS
|
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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "postsvg"
|
|
4
|
+
|
|
3
5
|
module Vectory
|
|
4
6
|
class Eps < Vector
|
|
5
7
|
def self.default_extension
|
|
@@ -20,27 +22,15 @@ module Vectory
|
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
def to_ps
|
|
23
|
-
|
|
25
|
+
to_svg.to_ps
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def to_svg
|
|
27
|
-
|
|
29
|
+
Svg.from_content(Postsvg.convert(content))
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
def to_emf
|
|
31
|
-
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def to_pdf
|
|
35
|
-
pdf_content = GhostscriptWrapper.convert(content, eps_crop: true)
|
|
36
|
-
pdf = Pdf.new(pdf_content)
|
|
37
|
-
# Pass original BoundingBox dimensions to preserve them in conversions
|
|
38
|
-
bbox = parse_bounding_box
|
|
39
|
-
if bbox
|
|
40
|
-
pdf.original_width = bbox[:urx] - bbox[:llx]
|
|
41
|
-
pdf.original_height = bbox[:ury] - bbox[:lly]
|
|
42
|
-
end
|
|
43
|
-
pdf
|
|
33
|
+
to_svg.to_emf
|
|
44
34
|
end
|
|
45
35
|
|
|
46
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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "postsvg"
|
|
4
|
+
|
|
3
5
|
module Vectory
|
|
4
6
|
class Ps < Vector
|
|
5
7
|
def self.default_extension
|
|
@@ -20,27 +22,15 @@ module Vectory
|
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
def to_eps
|
|
23
|
-
|
|
25
|
+
to_svg.to_eps
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def to_emf
|
|
27
|
-
|
|
29
|
+
to_svg.to_emf
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
def to_svg
|
|
31
|
-
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def to_pdf
|
|
35
|
-
pdf_content = GhostscriptWrapper.convert(content, eps_crop: false)
|
|
36
|
-
pdf = Pdf.new(pdf_content)
|
|
37
|
-
# Pass original BoundingBox dimensions to preserve them in conversions
|
|
38
|
-
bbox = parse_bounding_box
|
|
39
|
-
if bbox
|
|
40
|
-
pdf.original_width = bbox[:urx] - bbox[:llx]
|
|
41
|
-
pdf.original_height = bbox[:ury] - bbox[:lly]
|
|
42
|
-
end
|
|
43
|
-
pdf
|
|
33
|
+
Svg.from_content(Postsvg.convert(content))
|
|
44
34
|
end
|
|
45
35
|
|
|
46
36
|
def height
|
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,6 +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.3"
|
|
34
35
|
spec.add_dependency "thor", "~> 1.0"
|
|
35
|
-
spec.add_dependency "ukiryu", "~> 0.3.0"
|
|
36
36
|
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.12.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: 2026-07-
|
|
11
|
+
date: 2026-07-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -87,33 +87,33 @@ dependencies:
|
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
88
|
version: '1.14'
|
|
89
89
|
- !ruby/object:Gem::Dependency
|
|
90
|
-
name:
|
|
90
|
+
name: postsvg
|
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '
|
|
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: '
|
|
102
|
+
version: '0.3'
|
|
103
103
|
- !ruby/object:Gem::Dependency
|
|
104
|
-
name:
|
|
104
|
+
name: thor
|
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements:
|
|
107
107
|
- - "~>"
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
|
-
version:
|
|
109
|
+
version: '1.0'
|
|
110
110
|
type: :runtime
|
|
111
111
|
prerelease: false
|
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
|
113
113
|
requirements:
|
|
114
114
|
- - "~>"
|
|
115
115
|
- !ruby/object:Gem::Version
|
|
116
|
-
version:
|
|
116
|
+
version: '1.0'
|
|
117
117
|
description: |
|
|
118
118
|
Vectory performs pairwise vector image conversions for common
|
|
119
119
|
vector image formats, such as SVG, EMF, EPS and PS.
|
|
@@ -158,21 +158,13 @@ files:
|
|
|
158
158
|
- lib/vectory.rb
|
|
159
159
|
- lib/vectory/cli.rb
|
|
160
160
|
- lib/vectory/configuration.rb
|
|
161
|
-
- lib/vectory/conversion.rb
|
|
162
|
-
- lib/vectory/conversion/ghostscript_strategy.rb
|
|
163
|
-
- lib/vectory/conversion/inkscape_strategy.rb
|
|
164
|
-
- lib/vectory/conversion/strategy.rb
|
|
165
161
|
- lib/vectory/datauri.rb
|
|
166
162
|
- lib/vectory/emf.rb
|
|
167
163
|
- lib/vectory/eps.rb
|
|
168
164
|
- lib/vectory/errors.rb
|
|
169
165
|
- lib/vectory/file_magic.rb
|
|
170
|
-
- lib/vectory/ghostscript_wrapper.rb
|
|
171
166
|
- lib/vectory/image.rb
|
|
172
167
|
- lib/vectory/image_resize.rb
|
|
173
|
-
- lib/vectory/inkscape_wrapper.rb
|
|
174
|
-
- lib/vectory/pdf.rb
|
|
175
|
-
- lib/vectory/platform.rb
|
|
176
168
|
- lib/vectory/ps.rb
|
|
177
169
|
- lib/vectory/svg.rb
|
|
178
170
|
- lib/vectory/svg_document.rb
|
|
@@ -180,6 +172,7 @@ files:
|
|
|
180
172
|
- lib/vectory/utils.rb
|
|
181
173
|
- lib/vectory/vector.rb
|
|
182
174
|
- lib/vectory/version.rb
|
|
175
|
+
- scripts/regenerate_fixtures.rb
|
|
183
176
|
- tmp/.keep
|
|
184
177
|
- vectory.gemspec
|
|
185
178
|
homepage: https://github.com/metanorma/vectory
|