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
data/lib/vectory/pdf.rb
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Vectory
|
|
4
|
-
class Pdf < Vector
|
|
5
|
-
attr_accessor :original_height, :original_width
|
|
6
|
-
|
|
7
|
-
def self.default_extension
|
|
8
|
-
"pdf"
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def self.mimetype
|
|
12
|
-
"application/pdf"
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def to_svg
|
|
16
|
-
svg = convert_to_svg
|
|
17
|
-
|
|
18
|
-
# If we have original dimensions from EPS/PS, adjust the SVG
|
|
19
|
-
if original_height && original_width
|
|
20
|
-
adjusted_content = adjust_svg_dimensions(svg.content, original_width,
|
|
21
|
-
original_height)
|
|
22
|
-
svg = Svg.new(adjusted_content, svg.initial_path)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
svg
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def to_eps
|
|
29
|
-
with_inkscape_pdf_fallback(:eps, Eps)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def to_ps
|
|
33
|
-
with_inkscape_pdf_fallback(:ps, Ps)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def to_emf
|
|
37
|
-
with_inkscape_pdf_fallback(:emf, Emf)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
# Execute a conversion with fallback for Inkscape PDF import issues
|
|
43
|
-
#
|
|
44
|
-
# Inkscape 1.4.x on Windows and macOS has a PDF import bug where it
|
|
45
|
-
# may fail to create output files. This method catches any conversion
|
|
46
|
-
# error and retries via the PDF → EPS → target format path.
|
|
47
|
-
#
|
|
48
|
-
# @param output_format [Symbol] the target format (:svg, :eps, :ps, :emf)
|
|
49
|
-
# @param output_class [Class] the output class (Svg, Eps, Ps, Emf)
|
|
50
|
-
# @param plain [Boolean] whether to use plain SVG format (only for SVG)
|
|
51
|
-
# @return [Vector] the converted output
|
|
52
|
-
# @raise [Vectory::ConversionError] if both methods fail
|
|
53
|
-
def with_inkscape_pdf_fallback(output_format, output_class, plain: false)
|
|
54
|
-
InkscapeWrapper.convert(
|
|
55
|
-
content: content,
|
|
56
|
-
input_format: :pdf,
|
|
57
|
-
output_format: output_format,
|
|
58
|
-
output_class: output_class,
|
|
59
|
-
plain: plain,
|
|
60
|
-
)
|
|
61
|
-
rescue Vectory::ConversionError => e
|
|
62
|
-
log_conversion_failure(e, output_format)
|
|
63
|
-
|
|
64
|
-
# Try fallback: PDF → EPS (Ghostscript) → target format (Inkscape)
|
|
65
|
-
begin
|
|
66
|
-
warn "[VECTORY] Attempting fallback: PDF → EPS → #{output_format.upcase}" if fallback_logging_enabled?
|
|
67
|
-
eps_content = GhostscriptWrapper.pdf_to_eps(content)
|
|
68
|
-
warn "[VECTORY] PDF → EPS succeeded, now trying EPS → #{output_format.upcase}" if fallback_logging_enabled?
|
|
69
|
-
InkscapeWrapper.convert(
|
|
70
|
-
content: eps_content,
|
|
71
|
-
input_format: :eps,
|
|
72
|
-
output_format: output_format,
|
|
73
|
-
output_class: output_class,
|
|
74
|
-
plain: plain,
|
|
75
|
-
)
|
|
76
|
-
rescue StandardError => fallback_error
|
|
77
|
-
# Wrap non-Vectory errors in ConversionError for consistent error handling
|
|
78
|
-
error_to_raise = if fallback_error.is_a?(Vectory::Error)
|
|
79
|
-
fallback_error
|
|
80
|
-
else
|
|
81
|
-
ConversionError.new(
|
|
82
|
-
"PDF fallback conversion failed: #{fallback_error.message}",
|
|
83
|
-
)
|
|
84
|
-
end
|
|
85
|
-
warn "[VECTORY] Fallback also failed: #{fallback_error.message[0..100]}" if fallback_logging_enabled?
|
|
86
|
-
raise error_to_raise
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Convert PDF to SVG using fallback mechanism
|
|
91
|
-
def convert_to_svg
|
|
92
|
-
with_inkscape_pdf_fallback(:svg, Svg, plain: true)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def log_conversion_failure(error, output_format)
|
|
96
|
-
return unless fallback_logging_enabled?
|
|
97
|
-
|
|
98
|
-
warn "[VECTORY] PDF → #{output_format.upcase} direct conversion failed:"
|
|
99
|
-
warn "[VECTORY] Error: #{error.message[0..200]}"
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def fallback_logging_enabled?
|
|
103
|
-
ENV["VECTORY_DEBUG"] || ENV["CI"]
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def adjust_svg_dimensions(svg_content, width, height)
|
|
107
|
-
# Replace width and height attributes in SVG root element
|
|
108
|
-
svg_content.gsub(/(<svg[^>]*\s)width="[^"]*"/, "\\1width=\"#{width}\"")
|
|
109
|
-
.gsub(/(<svg[^>]*\s)height="[^"]*"/, "\\1height=\"#{height}\"")
|
|
110
|
-
.gsub(/(<svg[^>]*\s)viewBox="[^"]*"/) do |match|
|
|
111
|
-
# Adjust viewBox to match new dimensions
|
|
112
|
-
"#{match.split('viewBox')[0]}viewBox=\"0 0 #{width} #{height}\""
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
end
|
data/lib/vectory/platform.rb
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Vectory
|
|
4
|
-
# Platform abstraction for centralized OS-specific behavior
|
|
5
|
-
#
|
|
6
|
-
# This class provides a single source of truth for platform detection
|
|
7
|
-
# and platform-specific path handling, eliminating duplicated logic
|
|
8
|
-
# across InkscapeWrapper, GhostscriptWrapper, and other classes.
|
|
9
|
-
#
|
|
10
|
-
# @example Check if running on Windows
|
|
11
|
-
# Vectory::Platform.windows? # => true or false
|
|
12
|
-
#
|
|
13
|
-
# @example Format a path for execution on the current platform
|
|
14
|
-
# Vectory::Platform.path_for_execution("C:/Program Files/Inkscape/inkscape.exe")
|
|
15
|
-
# # On Windows: "C:\\Program Files\\Inkscape\\inkscape.exe"
|
|
16
|
-
# # On Unix: "C:/Program Files/Inkscape/inkscape.exe"
|
|
17
|
-
class Platform
|
|
18
|
-
class << self
|
|
19
|
-
# Detect if running on Windows
|
|
20
|
-
#
|
|
21
|
-
# @return [Boolean] true if on Windows platform
|
|
22
|
-
def windows?
|
|
23
|
-
Gem.win_platform?
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Detect if running on macOS
|
|
27
|
-
#
|
|
28
|
-
# @return [Boolean] true if on macOS platform
|
|
29
|
-
def macos?
|
|
30
|
-
RbConfig::CONFIG["host_os"].include?("darwin")
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Detect if running on Linux
|
|
34
|
-
#
|
|
35
|
-
# @return [Boolean] true if on Linux platform
|
|
36
|
-
def linux?
|
|
37
|
-
RbConfig::CONFIG["host_os"].include?("linux")
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Format a file path for execution on the current platform
|
|
41
|
-
#
|
|
42
|
-
# On Windows, converts forward slashes to backslashes and quotes paths with spaces.
|
|
43
|
-
# On Unix-like systems, returns the path unchanged.
|
|
44
|
-
#
|
|
45
|
-
# @param path [String] the file path to format
|
|
46
|
-
# @return [String] platform-formatted path
|
|
47
|
-
def path_for_execution(path)
|
|
48
|
-
return path unless path
|
|
49
|
-
|
|
50
|
-
formatted_path = windows? ? path.gsub("/", "\\") : path
|
|
51
|
-
|
|
52
|
-
# Quote paths with spaces to prevent shell parsing issues
|
|
53
|
-
formatted_path[/\s/] ? "\"#{formatted_path}\"" : formatted_path
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Get the PATH environment variable as an array
|
|
57
|
-
#
|
|
58
|
-
# Handles different PATH separators on Windows (;) vs Unix (:)
|
|
59
|
-
#
|
|
60
|
-
# @return [Array<String>] array of directory paths
|
|
61
|
-
def executable_search_paths
|
|
62
|
-
@executable_search_paths ||= begin
|
|
63
|
-
path_sep = windows? ? ";" : ":"
|
|
64
|
-
(ENV["PATH"] || "").split(path_sep)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Check if a command is available in the system PATH
|
|
69
|
-
#
|
|
70
|
-
# @param command [String] the command to check
|
|
71
|
-
# @return [Boolean] true if command is found in PATH
|
|
72
|
-
def command_available?(command)
|
|
73
|
-
executable_search_paths.any? do |dir|
|
|
74
|
-
executable_path = File.join(dir, command)
|
|
75
|
-
File.executable?(executable_path) && !File.directory?(executable_path)
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Get the appropriate shell command extension for the platform
|
|
80
|
-
#
|
|
81
|
-
# @return [String, nil] ".exe" on Windows, nil on Unix
|
|
82
|
-
def command_extension
|
|
83
|
-
windows? ? ".exe" : nil
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Get the default shell for the platform
|
|
87
|
-
#
|
|
88
|
-
# @return [String] shell command (e.g., "cmd.exe" on Windows, "sh" on Unix)
|
|
89
|
-
def default_shell
|
|
90
|
-
if windows?
|
|
91
|
-
"cmd.exe"
|
|
92
|
-
else
|
|
93
|
-
"sh"
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Reset cached values (primarily for testing)
|
|
98
|
-
#
|
|
99
|
-
# @api private
|
|
100
|
-
def reset_cache
|
|
101
|
-
@executable_search_paths = nil
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|