vectory 0.7.8 → 0.8.1
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/.github/workflows/docs.yml +59 -0
- data/.github/workflows/links.yml +99 -0
- data/.github/workflows/rake.yml +5 -1
- data/.github/workflows/release.yml +7 -3
- data/.gitignore +5 -0
- data/.rubocop.yml +11 -3
- data/.rubocop_todo.yml +252 -0
- data/Gemfile +4 -2
- data/README.adoc +23 -1
- data/Rakefile +13 -0
- data/docs/Gemfile +18 -0
- data/docs/_config.yml +179 -0
- data/docs/features/conversion.adoc +205 -0
- data/docs/features/external-dependencies.adoc +305 -0
- data/docs/features/format-detection.adoc +173 -0
- data/docs/features/index.adoc +205 -0
- data/docs/getting-started/core-concepts.adoc +214 -0
- data/docs/getting-started/index.adoc +37 -0
- data/docs/getting-started/installation.adoc +318 -0
- data/docs/getting-started/quick-start.adoc +160 -0
- data/docs/guides/error-handling.adoc +400 -0
- data/docs/guides/index.adoc +197 -0
- data/docs/index.adoc +146 -0
- data/docs/lychee.toml +25 -0
- data/docs/reference/api.adoc +355 -0
- data/docs/reference/index.adoc +189 -0
- data/docs/understanding/architecture.adoc +277 -0
- data/docs/understanding/index.adoc +148 -0
- data/docs/understanding/inkscape-wrapper.adoc +270 -0
- data/lib/vectory/capture.rb +165 -37
- data/lib/vectory/cli.rb +2 -0
- data/lib/vectory/configuration.rb +177 -0
- data/lib/vectory/conversion/ghostscript_strategy.rb +77 -0
- data/lib/vectory/conversion/inkscape_strategy.rb +124 -0
- data/lib/vectory/conversion/strategy.rb +58 -0
- data/lib/vectory/conversion.rb +104 -0
- data/lib/vectory/datauri.rb +1 -1
- data/lib/vectory/emf.rb +17 -5
- data/lib/vectory/eps.rb +45 -3
- data/lib/vectory/errors.rb +25 -0
- data/lib/vectory/file_magic.rb +2 -2
- data/lib/vectory/ghostscript_wrapper.rb +160 -0
- data/lib/vectory/image_resize.rb +98 -12
- data/lib/vectory/inkscape_wrapper.rb +205 -0
- data/lib/vectory/pdf.rb +76 -0
- data/lib/vectory/platform.rb +105 -0
- data/lib/vectory/ps.rb +47 -3
- data/lib/vectory/svg.rb +46 -3
- data/lib/vectory/svg_document.rb +40 -24
- data/lib/vectory/system_call.rb +36 -9
- data/lib/vectory/vector.rb +3 -23
- data/lib/vectory/version.rb +1 -1
- data/lib/vectory.rb +16 -11
- metadata +34 -3
- data/lib/vectory/inkscape_converter.rb +0 -141
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Architecture
|
|
4
|
+
parent: Understanding
|
|
5
|
+
nav_order: 1
|
|
6
|
+
---
|
|
7
|
+
= Architecture Overview
|
|
8
|
+
|
|
9
|
+
Vectory's design principles and architecture.
|
|
10
|
+
|
|
11
|
+
== Purpose
|
|
12
|
+
|
|
13
|
+
This section explains Vectory's architecture, design decisions, and implementation patterns.
|
|
14
|
+
|
|
15
|
+
== Design Philosophy
|
|
16
|
+
|
|
17
|
+
Vectory follows these core principles:
|
|
18
|
+
|
|
19
|
+
* **Object-Oriented Design**: Clear class hierarchy with inheritance
|
|
20
|
+
* **Wrapper Pattern**: External tools abstracted behind clean interfaces
|
|
21
|
+
* **Singleton Pattern**: Shared tool instances for efficiency
|
|
22
|
+
* **Model-Based**: Object representation rather than serialization manipulation
|
|
23
|
+
* **Separation of Concerns**: Each class has a single responsibility
|
|
24
|
+
|
|
25
|
+
== Class Hierarchy
|
|
26
|
+
|
|
27
|
+
[source,ruby]
|
|
28
|
+
----
|
|
29
|
+
Vectory::Image # Base class for all images
|
|
30
|
+
│
|
|
31
|
+
└── Vectory::Vector # Abstract base for vector formats
|
|
32
|
+
│
|
|
33
|
+
├── Vectory::Eps # Encapsulated PostScript
|
|
34
|
+
├── Vectory::Ps # PostScript
|
|
35
|
+
├── Vectory::Emf # Enhanced Metafile
|
|
36
|
+
├── Vectory::Svg # Scalable Vector Graphics
|
|
37
|
+
└── Vectory::Pdf # PDF (intermediate format)
|
|
38
|
+
----
|
|
39
|
+
|
|
40
|
+
Each format class inherits from `Vectory::Vector` and shares common functionality.
|
|
41
|
+
|
|
42
|
+
== Design Patterns
|
|
43
|
+
|
|
44
|
+
=== Wrapper Pattern
|
|
45
|
+
|
|
46
|
+
External tools are wrapped in singleton classes:
|
|
47
|
+
|
|
48
|
+
[source,ruby]
|
|
49
|
+
----
|
|
50
|
+
# Inkscape wrapper
|
|
51
|
+
class InkscapeWrapper
|
|
52
|
+
include Singleton
|
|
53
|
+
|
|
54
|
+
def executable
|
|
55
|
+
@executable ||= find_inkscape
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def version
|
|
59
|
+
@version ||= detect_version
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
----
|
|
63
|
+
|
|
64
|
+
**Benefits**:
|
|
65
|
+
|
|
66
|
+
* Clean interface to external tools
|
|
67
|
+
* Centralized tool discovery
|
|
68
|
+
* Version detection and caching
|
|
69
|
+
* Error handling
|
|
70
|
+
|
|
71
|
+
=== Singleton Pattern
|
|
72
|
+
|
|
73
|
+
Tool wrappers use singleton pattern:
|
|
74
|
+
|
|
75
|
+
[source,ruby]
|
|
76
|
+
----
|
|
77
|
+
# Single instance shared across conversions
|
|
78
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
79
|
+
|
|
80
|
+
# Version detected once, cached
|
|
81
|
+
puts inkscape.version # Cached result
|
|
82
|
+
----
|
|
83
|
+
|
|
84
|
+
**Benefits**:
|
|
85
|
+
|
|
86
|
+
* Efficient resource usage
|
|
87
|
+
* Version detection happens once
|
|
88
|
+
* Consistent behavior across conversions
|
|
89
|
+
|
|
90
|
+
=== Factory Methods
|
|
91
|
+
|
|
92
|
+
Each format class provides factory methods:
|
|
93
|
+
|
|
94
|
+
[source,ruby]
|
|
95
|
+
----
|
|
96
|
+
Vectory::Eps.from_path(path) # Load from file
|
|
97
|
+
Vectory::Eps.from_content(content) # Load from string
|
|
98
|
+
Vectory::Eps.from_datauri(uri) # Load from data URI
|
|
99
|
+
Vectory::Eps.from_node(node) # Load from XML node
|
|
100
|
+
----
|
|
101
|
+
|
|
102
|
+
**Benefits**:
|
|
103
|
+
|
|
104
|
+
* Flexible input sources
|
|
105
|
+
* Consistent API across formats
|
|
106
|
+
* Easy testing and mocking
|
|
107
|
+
|
|
108
|
+
== Conversion Pipeline
|
|
109
|
+
|
|
110
|
+
=== Direct Conversions
|
|
111
|
+
|
|
112
|
+
Some conversions are direct:
|
|
113
|
+
|
|
114
|
+
[source,text]
|
|
115
|
+
----
|
|
116
|
+
SVG → Inkscape → EPS/PS/EMF/PDF
|
|
117
|
+
EMF → emf2svg → SVG
|
|
118
|
+
----
|
|
119
|
+
|
|
120
|
+
=== Two-Step Conversions
|
|
121
|
+
|
|
122
|
+
Other conversions use intermediate formats:
|
|
123
|
+
|
|
124
|
+
[source,text]
|
|
125
|
+
----
|
|
126
|
+
EPS/PS → Ghostscript → PDF → Inkscape → SVG
|
|
127
|
+
----
|
|
128
|
+
|
|
129
|
+
This preserves BoundingBox information from EPS/PS files.
|
|
130
|
+
|
|
131
|
+
== Component Interaction
|
|
132
|
+
|
|
133
|
+
[.flowchart]
|
|
134
|
+
----
|
|
135
|
+
User Code
|
|
136
|
+
│
|
|
137
|
+
├─→ Vectory::Eps.from_path()
|
|
138
|
+
│ │
|
|
139
|
+
│ ├─→ Vectory::FileMagic.detect_format()
|
|
140
|
+
│ │
|
|
141
|
+
│ └─→ Vectory::Eps.new()
|
|
142
|
+
│
|
|
143
|
+
└─→ Vectory::Eps#to_svg()
|
|
144
|
+
│
|
|
145
|
+
├─→ Vectory::Pdf#to_pdf() ──→ GhostscriptWrapper
|
|
146
|
+
│
|
|
147
|
+
└─→ Vectory::Pdf#to_svg() ──→ InkscapeWrapper
|
|
148
|
+
----
|
|
149
|
+
|
|
150
|
+
== File System Operations
|
|
151
|
+
|
|
152
|
+
=== Path Tracking
|
|
153
|
+
|
|
154
|
+
Each image tracks two paths:
|
|
155
|
+
|
|
156
|
+
[source,ruby]
|
|
157
|
+
----
|
|
158
|
+
eps = Vectory::Eps.from_path("input.eps")
|
|
159
|
+
|
|
160
|
+
# Where it was loaded from (read-only)
|
|
161
|
+
eps.initial_path # => "input.eps"
|
|
162
|
+
|
|
163
|
+
# Where it's currently written
|
|
164
|
+
eps.path # Raises NotWrittenToDiskError if not written
|
|
165
|
+
|
|
166
|
+
eps.write("output.eps")
|
|
167
|
+
eps.path # => "output.eps"
|
|
168
|
+
----
|
|
169
|
+
|
|
170
|
+
=== Temporary Files
|
|
171
|
+
|
|
172
|
+
Vectory manages temporary files for conversions:
|
|
173
|
+
|
|
174
|
+
[source,ruby]
|
|
175
|
+
----
|
|
176
|
+
# Temporary files created automatically
|
|
177
|
+
eps = Vectory::Eps.from_path("diagram.eps")
|
|
178
|
+
svg = eps.to_svg # Creates temp PDF, then temp SVG
|
|
179
|
+
|
|
180
|
+
# Write to final location
|
|
181
|
+
svg.write("diagram.svg")
|
|
182
|
+
----
|
|
183
|
+
|
|
184
|
+
Temp files are cleaned up automatically.
|
|
185
|
+
|
|
186
|
+
== Error Handling
|
|
187
|
+
|
|
188
|
+
Structured error hierarchy:
|
|
189
|
+
|
|
190
|
+
[source,ruby]
|
|
191
|
+
----
|
|
192
|
+
Vectory::Error # Base error
|
|
193
|
+
├── SystemCallError # External tool failures
|
|
194
|
+
├── NotWrittenToDiskError # Path access before write
|
|
195
|
+
├── ParsingError # Content parsing failures
|
|
196
|
+
├── InkscapeNotFoundError # Inkscape unavailable
|
|
197
|
+
├── ConversionError # Conversion failures
|
|
198
|
+
└── InkscapeQueryError # Dimension query failures
|
|
199
|
+
----
|
|
200
|
+
|
|
201
|
+
== Performance Considerations
|
|
202
|
+
|
|
203
|
+
=== Caching
|
|
204
|
+
|
|
205
|
+
[source,ruby]
|
|
206
|
+
----
|
|
207
|
+
# Tool version cached
|
|
208
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
209
|
+
inkscape.version # Detected once, cached
|
|
210
|
+
|
|
211
|
+
# Tool path cached
|
|
212
|
+
inkscape.executable # Discovered once, cached
|
|
213
|
+
----
|
|
214
|
+
|
|
215
|
+
=== Lazy Evaluation
|
|
216
|
+
|
|
217
|
+
[source,ruby]
|
|
218
|
+
----
|
|
219
|
+
# Version detected only when needed
|
|
220
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
221
|
+
# Version not detected until first use
|
|
222
|
+
puts inkscape.version # Detection happens here
|
|
223
|
+
----
|
|
224
|
+
|
|
225
|
+
=== Process Management
|
|
226
|
+
|
|
227
|
+
[source,ruby]
|
|
228
|
+
----
|
|
229
|
+
# Non-blocking timeout on Windows
|
|
230
|
+
spawn("taskkill", "/pid", pid, "/f",
|
|
231
|
+
%i[out err] => File::NULL)
|
|
232
|
+
|
|
233
|
+
# Blocking timeout on Unix
|
|
234
|
+
Process.kill(:TERM, pid)
|
|
235
|
+
----
|
|
236
|
+
|
|
237
|
+
== Extensibility
|
|
238
|
+
|
|
239
|
+
=== Adding New Formats
|
|
240
|
+
|
|
241
|
+
To add a new format:
|
|
242
|
+
|
|
243
|
+
. Create format class inheriting from `Vectory::Vector`
|
|
244
|
+
. Implement `#to_*` conversion methods
|
|
245
|
+
. Add format detection to `FileMagic`
|
|
246
|
+
|
|
247
|
+
[source,ruby]
|
|
248
|
+
----
|
|
249
|
+
class Vectory::NewFormat < Vectory::Vector
|
|
250
|
+
def to_svg
|
|
251
|
+
# Convert to SVG
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def self.from_path(path)
|
|
255
|
+
# Load from file
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
----
|
|
259
|
+
|
|
260
|
+
=== Adding New Conversions
|
|
261
|
+
|
|
262
|
+
Add conversion methods to existing format classes:
|
|
263
|
+
|
|
264
|
+
[source,ruby]
|
|
265
|
+
----
|
|
266
|
+
class Vectory::Eps < Vectory::Vector
|
|
267
|
+
def to_new_format
|
|
268
|
+
# Convert to new format
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
----
|
|
272
|
+
|
|
273
|
+
== See Also
|
|
274
|
+
|
|
275
|
+
* link:class-hierarchy/[Class Hierarchy] - Detailed class structure
|
|
276
|
+
* link:conversion-pipeline/[Conversion Pipeline] - How conversions work
|
|
277
|
+
* link:wrapper-pattern/[Wrapper Pattern] - Tool abstraction details
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Understanding
|
|
4
|
+
nav_order: 5
|
|
5
|
+
has_children: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Learn how Vectory works internally.
|
|
9
|
+
|
|
10
|
+
== Overview
|
|
11
|
+
|
|
12
|
+
This section explains Vectory's internal architecture, design decisions, and implementation details. Understanding these concepts will help you use Vectory effectively and troubleshoot issues.
|
|
13
|
+
|
|
14
|
+
== Architecture
|
|
15
|
+
|
|
16
|
+
link:architecture[**Architecture Overview**]::
|
|
17
|
+
Class hierarchy and design patterns used in Vectory.
|
|
18
|
+
+
|
|
19
|
+
Object-oriented design with inheritance, singleton pattern for tool wrappers, and model-based architecture.
|
|
20
|
+
|
|
21
|
+
link:class-hierarchy[**Class Hierarchy**]::
|
|
22
|
+
Detailed explanation of the `Vectory::Image` inheritance tree.
|
|
23
|
+
+
|
|
24
|
+
`Vectory::Image` → `Vectory::Vector` → Format-specific classes.
|
|
25
|
+
|
|
26
|
+
link:conversion-pipeline[**Conversion Pipeline**]::
|
|
27
|
+
How conversions flow through intermediate formats.
|
|
28
|
+
+
|
|
29
|
+
Direct conversions vs. two-step conversions through PDF.
|
|
30
|
+
|
|
31
|
+
== External Tools
|
|
32
|
+
|
|
33
|
+
link:external-dependencies[**External Dependencies Overview**]::
|
|
34
|
+
Comprehensive guide to all external tools and gems.
|
|
35
|
+
+
|
|
36
|
+
Inkscape, Ghostscript, emf2svg, moxml - their purposes and how Vectory integrates with them.
|
|
37
|
+
|
|
38
|
+
link:inkscape-wrapper[**Inkscape Wrapper**]::
|
|
39
|
+
Singleton pattern implementation for Inkscape integration.
|
|
40
|
+
+
|
|
41
|
+
Version detection, command generation, timeout management, 0.x vs 1.x+ syntax differences.
|
|
42
|
+
|
|
43
|
+
link:ghostscript-wrapper[**Ghostscript Wrapper**]::
|
|
44
|
+
Ghostscript integration for EPS/PS to PDF conversion.
|
|
45
|
+
+
|
|
46
|
+
BoundingBox preservation, PDF generation, command-line options.
|
|
47
|
+
|
|
48
|
+
link:emf2svg-integration[**emf2svg Integration**]::
|
|
49
|
+
EMF format support via the emf2svg gem.
|
|
50
|
+
+
|
|
51
|
+
Windows EMF conversion, cross-platform compatibility.
|
|
52
|
+
|
|
53
|
+
link:moxml-integration[**moxml Integration**]::
|
|
54
|
+
XML/HTML parsing for Metanorma SVG mapping.
|
|
55
|
+
+
|
|
56
|
+
Link rewriting, ID preservation, namespace handling.
|
|
57
|
+
|
|
58
|
+
== Design Patterns
|
|
59
|
+
|
|
60
|
+
link:wrapper-pattern[**Wrapper Pattern**]::
|
|
61
|
+
Abstracting external tool dependencies.
|
|
62
|
+
+
|
|
63
|
+
`InkscapeWrapper` and `GhostscriptWrapper` provide clean interfaces.
|
|
64
|
+
|
|
65
|
+
link:singleton[**Singleton Pattern**]::
|
|
66
|
+
Shared Inkscape instance across conversions.
|
|
67
|
+
+
|
|
68
|
+
Version detection caching, resource efficiency.
|
|
69
|
+
|
|
70
|
+
link:factory-methods[**Factory Methods**]::
|
|
71
|
+
Class methods for creating image objects.
|
|
72
|
+
+
|
|
73
|
+
`from_path`, `from_content`, `from_datauri`, `from_node`.
|
|
74
|
+
|
|
75
|
+
== File System Operations
|
|
76
|
+
|
|
77
|
+
link:file-paths[**Path Management**]::
|
|
78
|
+
`initial_path` vs `path` distinction.
|
|
79
|
+
+
|
|
80
|
+
Original file location vs. current write location.
|
|
81
|
+
|
|
82
|
+
link:temp-files[**Temporary File Handling**]::
|
|
83
|
+
How Vectory manages intermediate files.
|
|
84
|
+
+
|
|
85
|
+
Automatic cleanup, temp directory usage.
|
|
86
|
+
|
|
87
|
+
== Error Handling
|
|
88
|
+
|
|
89
|
+
link:error-classes[**Error Class Hierarchy**]::
|
|
90
|
+
Exception types and when they're raised.
|
|
91
|
+
+
|
|
92
|
+
`SystemCallError`, `NotWrittenToDiskError`, `ParsingError`, etc.
|
|
93
|
+
|
|
94
|
+
link:timeout-handling[**Timeout Management**]::
|
|
95
|
+
How Vectory handles hung external processes.
|
|
96
|
+
+
|
|
97
|
+
Platform-specific timeout implementation (Unix vs Windows).
|
|
98
|
+
|
|
99
|
+
== Format Detection
|
|
100
|
+
|
|
101
|
+
link:magic-numbers[**Magic Number Detection**]::
|
|
102
|
+
How file formats are detected from content.
|
|
103
|
+
+
|
|
104
|
+
EPS, PS, EMF magic numbers; SVG content inspection.
|
|
105
|
+
|
|
106
|
+
link:file-type-heuristics[**File Type Heuristics**]::
|
|
107
|
+
Fallback strategies when magic numbers aren't enough.
|
|
108
|
+
|
|
109
|
+
== Performance
|
|
110
|
+
|
|
111
|
+
link:caching[**Caching Strategies**]::
|
|
112
|
+
Version detection caching, tool discovery caching.
|
|
113
|
+
|
|
114
|
+
link:batch-optimization[**Batch Processing**]::
|
|
115
|
+
Optimizations for processing multiple files.
|
|
116
|
+
|
|
117
|
+
== Platform-Specific Behavior
|
|
118
|
+
|
|
119
|
+
link:windows-support[**Windows Support**]::
|
|
120
|
+
Special considerations for Windows platforms.
|
|
121
|
+
+
|
|
122
|
+
Path handling, process management, Inkscape version compatibility.
|
|
123
|
+
|
|
124
|
+
link:macos-support[**macOS Support**]::
|
|
125
|
+
Display environment configuration for headless operation.
|
|
126
|
+
+
|
|
127
|
+
`DISPLAY=""` for Inkscape timeout prevention.
|
|
128
|
+
|
|
129
|
+
link:linux-support[**Linux Support**]::
|
|
130
|
+
Standard Unix behavior for Vectory.
|
|
131
|
+
|
|
132
|
+
== Technical Constraints
|
|
133
|
+
|
|
134
|
+
link:format-limitations[**Format Limitations**]::
|
|
135
|
+
Known limitations of each supported format.
|
|
136
|
+
+
|
|
137
|
+
Conversion artifacts, precision issues.
|
|
138
|
+
|
|
139
|
+
link:tool-versions[**Tool Version Requirements**]::
|
|
140
|
+
Inkscape and Ghostscript version compatibility.
|
|
141
|
+
+
|
|
142
|
+
Inkscape 0.x vs 1.x+ syntax differences, known issues.
|
|
143
|
+
|
|
144
|
+
== See Also
|
|
145
|
+
|
|
146
|
+
* link:../features/[Features] - What Vectory can do
|
|
147
|
+
* link:../guides/[Guides] - How to use Vectory
|
|
148
|
+
* link:../reference/[Reference] - Complete API documentation
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Inkscape Wrapper
|
|
4
|
+
parent: Understanding
|
|
5
|
+
nav_order: 2
|
|
6
|
+
---
|
|
7
|
+
= Inkscape Wrapper
|
|
8
|
+
|
|
9
|
+
Singleton pattern implementation for Inkscape integration.
|
|
10
|
+
|
|
11
|
+
== Overview
|
|
12
|
+
|
|
13
|
+
The `InkscapeWrapper` class provides a clean interface to Inkscape's command-line tools. It uses the singleton pattern to share a single instance across all conversions.
|
|
14
|
+
|
|
15
|
+
== Singleton Pattern
|
|
16
|
+
|
|
17
|
+
[source,ruby]
|
|
18
|
+
----
|
|
19
|
+
class InkscapeWrapper
|
|
20
|
+
include Singleton
|
|
21
|
+
|
|
22
|
+
def initialize
|
|
23
|
+
@executable = nil
|
|
24
|
+
@version = nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
----
|
|
28
|
+
|
|
29
|
+
[source,ruby]
|
|
30
|
+
----
|
|
31
|
+
# Get singleton instance
|
|
32
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
33
|
+
|
|
34
|
+
# Same instance returned on subsequent calls
|
|
35
|
+
inkscape2 = Vectory::InkscapeWrapper.instance
|
|
36
|
+
puts inkscape.equal?(inkscape2) # => true
|
|
37
|
+
----
|
|
38
|
+
|
|
39
|
+
== Version Detection
|
|
40
|
+
|
|
41
|
+
=== Automatic Detection
|
|
42
|
+
|
|
43
|
+
Inkscape version is detected on first access:
|
|
44
|
+
|
|
45
|
+
[source,ruby]
|
|
46
|
+
----
|
|
47
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
48
|
+
|
|
49
|
+
# Version detected automatically
|
|
50
|
+
puts inkscape.version # => "1.3.0"
|
|
51
|
+
----
|
|
52
|
+
|
|
53
|
+
=== Version Caching
|
|
54
|
+
|
|
55
|
+
Version is cached after first detection:
|
|
56
|
+
|
|
57
|
+
[source,ruby]
|
|
58
|
+
----
|
|
59
|
+
# First call: detects version
|
|
60
|
+
version1 = inkscape.version
|
|
61
|
+
|
|
62
|
+
# Subsequent calls: returns cached value
|
|
63
|
+
version2 = inkscape.version
|
|
64
|
+
|
|
65
|
+
puts version1.equal?(version2) # => true
|
|
66
|
+
----
|
|
67
|
+
|
|
68
|
+
=== Version Checking
|
|
69
|
+
|
|
70
|
+
[source,ruby]
|
|
71
|
+
----
|
|
72
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
73
|
+
|
|
74
|
+
# Check if modern version (1.x)
|
|
75
|
+
if inkscape.modern?
|
|
76
|
+
puts "Using Inkscape 1.x syntax"
|
|
77
|
+
else
|
|
78
|
+
puts "Using Inkscape 0.x syntax"
|
|
79
|
+
end
|
|
80
|
+
----
|
|
81
|
+
|
|
82
|
+
== Command Generation
|
|
83
|
+
|
|
84
|
+
=== Version-Specific Syntax
|
|
85
|
+
|
|
86
|
+
Inkscape 0.x and 1.x use different command-line syntax:
|
|
87
|
+
|
|
88
|
+
[source,ruby]
|
|
89
|
+
----
|
|
90
|
+
# Inkscape 0.x syntax
|
|
91
|
+
inkscape --file=input.svg --export-eps=output.eps
|
|
92
|
+
|
|
93
|
+
# Inkscape 1.x syntax
|
|
94
|
+
inkscape input.svg --export-type=eps --output-file=output.eps
|
|
95
|
+
----
|
|
96
|
+
|
|
97
|
+
Vectory generates appropriate commands automatically:
|
|
98
|
+
|
|
99
|
+
[source,ruby]
|
|
100
|
+
----
|
|
101
|
+
class InkscapeWrapper
|
|
102
|
+
def command(input, output, format)
|
|
103
|
+
if modern?
|
|
104
|
+
# Inkscape 1.x syntax
|
|
105
|
+
[executable, input, "--export-type=#{format}",
|
|
106
|
+
"--output-file=#{output}"]
|
|
107
|
+
else
|
|
108
|
+
# Inkscape 0.x syntax
|
|
109
|
+
[executable, "--file=#{input}",
|
|
110
|
+
"--export-#{format}=#{output}"]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
----
|
|
115
|
+
|
|
116
|
+
== Conversion Methods
|
|
117
|
+
|
|
118
|
+
=== SVG to Other Formats
|
|
119
|
+
|
|
120
|
+
[source,ruby]
|
|
121
|
+
----
|
|
122
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
123
|
+
|
|
124
|
+
# SVG to PDF
|
|
125
|
+
inkscape.svg_to_pdf("input.svg", "output.pdf")
|
|
126
|
+
|
|
127
|
+
# SVG to EPS
|
|
128
|
+
inkscape.svg_to_eps("input.svg", "output.eps")
|
|
129
|
+
|
|
130
|
+
# SVG to PS
|
|
131
|
+
inkscape.svg_to_ps("input.svg", "output.ps")
|
|
132
|
+
|
|
133
|
+
# SVG to EMF
|
|
134
|
+
inkscape.svg_to_emf("input.svg", "output.emf")
|
|
135
|
+
----
|
|
136
|
+
|
|
137
|
+
=== Other Formats to SVG
|
|
138
|
+
|
|
139
|
+
[source,ruby]
|
|
140
|
+
----
|
|
141
|
+
# PDF to SVG
|
|
142
|
+
inkscape.pdf_to_svg("input.pdf", "output.svg")
|
|
143
|
+
|
|
144
|
+
# EPS to SVG
|
|
145
|
+
inkscape.eps_to_svg("input.eps", "output.svg")
|
|
146
|
+
|
|
147
|
+
# PS to SVG
|
|
148
|
+
inkscape.ps_to_svg("input.ps", "output.svg")
|
|
149
|
+
----
|
|
150
|
+
|
|
151
|
+
== Dimension Queries
|
|
152
|
+
|
|
153
|
+
=== Query Without Rendering
|
|
154
|
+
|
|
155
|
+
[source,ruby]
|
|
156
|
+
----
|
|
157
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
158
|
+
|
|
159
|
+
# Query dimensions without full conversion
|
|
160
|
+
width, height = inkscape.query_dimensions("diagram.svg")
|
|
161
|
+
puts "Size: #{width}x#{height}"
|
|
162
|
+
----
|
|
163
|
+
|
|
164
|
+
=== Implementation
|
|
165
|
+
|
|
166
|
+
Uses Inkscape's `--query-width` and `--query-height` options:
|
|
167
|
+
|
|
168
|
+
[source,ruby]
|
|
169
|
+
----
|
|
170
|
+
def query_dimensions(input)
|
|
171
|
+
output = `#{executable} --query-width --query-height #{input}`
|
|
172
|
+
# Parse output to extract dimensions
|
|
173
|
+
parse_dimensions(output)
|
|
174
|
+
end
|
|
175
|
+
----
|
|
176
|
+
|
|
177
|
+
== Error Handling
|
|
178
|
+
|
|
179
|
+
=== Tool Not Found
|
|
180
|
+
|
|
181
|
+
[source,ruby]
|
|
182
|
+
----
|
|
183
|
+
begin
|
|
184
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
185
|
+
inkscape.version
|
|
186
|
+
rescue Vectory::InkscapeNotFoundError => e
|
|
187
|
+
puts "Inkscape not found: #{e.message}"
|
|
188
|
+
puts "Install Inkscape or set INKSCAPE_PATH"
|
|
189
|
+
end
|
|
190
|
+
----
|
|
191
|
+
|
|
192
|
+
=== Execution Failures
|
|
193
|
+
|
|
194
|
+
[source,ruby]
|
|
195
|
+
----
|
|
196
|
+
begin
|
|
197
|
+
inkscape.svg_to_pdf("input.svg", "output.pdf")
|
|
198
|
+
rescue Vectory::SystemCallError => e
|
|
199
|
+
puts "Conversion failed: #{e.message}"
|
|
200
|
+
puts "Exit code: #{e.exit_code}"
|
|
201
|
+
end
|
|
202
|
+
----
|
|
203
|
+
|
|
204
|
+
== Platform-Specific Behavior
|
|
205
|
+
|
|
206
|
+
=== macOS
|
|
207
|
+
|
|
208
|
+
Vectory automatically sets `DISPLAY=""` to prevent timeouts:
|
|
209
|
+
|
|
210
|
+
[source,ruby]
|
|
211
|
+
----
|
|
212
|
+
# Automatic on macOS
|
|
213
|
+
ENV["DISPLAY"] = "" if RUBY_PLATFORM =~ /darwin/
|
|
214
|
+
----
|
|
215
|
+
|
|
216
|
+
=== Windows
|
|
217
|
+
|
|
218
|
+
* Inkscape 1.3.1+ has EPS/PS issues
|
|
219
|
+
* Use Inkscape 1.3.0 for best compatibility
|
|
220
|
+
|
|
221
|
+
=== Linux
|
|
222
|
+
|
|
223
|
+
Standard Unix behavior applies.
|
|
224
|
+
|
|
225
|
+
== Configuration
|
|
226
|
+
|
|
227
|
+
=== Custom Inkscape Path
|
|
228
|
+
|
|
229
|
+
[source,bash]
|
|
230
|
+
----
|
|
231
|
+
# Set environment variable
|
|
232
|
+
export INKSCAPE_PATH="/custom/path/to/inkscape"
|
|
233
|
+
----
|
|
234
|
+
|
|
235
|
+
[source,ruby]
|
|
236
|
+
----
|
|
237
|
+
# Or set in code
|
|
238
|
+
ENV["INKSCAPE_PATH"] = "/custom/path/to/inkscape"
|
|
239
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
240
|
+
puts inkscape.executable # => "/custom/path/to/inkscape"
|
|
241
|
+
----
|
|
242
|
+
|
|
243
|
+
## Performance
|
|
244
|
+
|
|
245
|
+
=== Singleton Efficiency
|
|
246
|
+
|
|
247
|
+
[source,ruby]
|
|
248
|
+
----
|
|
249
|
+
# Single instance shared across conversions
|
|
250
|
+
1000.times do
|
|
251
|
+
svg = Vectory::Eps.from_path("file#{i}.eps").to_svg
|
|
252
|
+
end
|
|
253
|
+
# InkscapeWrapper.instance created once, reused 1000 times
|
|
254
|
+
----
|
|
255
|
+
|
|
256
|
+
=== Version Detection Cost
|
|
257
|
+
|
|
258
|
+
[source,ruby]
|
|
259
|
+
----
|
|
260
|
+
# Version detected once
|
|
261
|
+
inkscape = Vectory::InkscapeWrapper.instance
|
|
262
|
+
version1 = inkscape.version # Detects: ~50ms
|
|
263
|
+
version2 = inkscape.version # Cached: ~0ms
|
|
264
|
+
----
|
|
265
|
+
|
|
266
|
+
== See Also
|
|
267
|
+
|
|
268
|
+
* link:../architecture/[Architecture Overview] - Design patterns
|
|
269
|
+
* link:ghostscript-wrapper/[Ghostscript Wrapper] - Other tool wrapper
|
|
270
|
+
* link:../features/external-dependencies/[External Dependencies] - Tool requirements
|