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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +59 -0
  3. data/.github/workflows/links.yml +99 -0
  4. data/.github/workflows/rake.yml +5 -1
  5. data/.github/workflows/release.yml +7 -3
  6. data/.gitignore +5 -0
  7. data/.rubocop.yml +11 -3
  8. data/.rubocop_todo.yml +252 -0
  9. data/Gemfile +4 -2
  10. data/README.adoc +23 -1
  11. data/Rakefile +13 -0
  12. data/docs/Gemfile +18 -0
  13. data/docs/_config.yml +179 -0
  14. data/docs/features/conversion.adoc +205 -0
  15. data/docs/features/external-dependencies.adoc +305 -0
  16. data/docs/features/format-detection.adoc +173 -0
  17. data/docs/features/index.adoc +205 -0
  18. data/docs/getting-started/core-concepts.adoc +214 -0
  19. data/docs/getting-started/index.adoc +37 -0
  20. data/docs/getting-started/installation.adoc +318 -0
  21. data/docs/getting-started/quick-start.adoc +160 -0
  22. data/docs/guides/error-handling.adoc +400 -0
  23. data/docs/guides/index.adoc +197 -0
  24. data/docs/index.adoc +146 -0
  25. data/docs/lychee.toml +25 -0
  26. data/docs/reference/api.adoc +355 -0
  27. data/docs/reference/index.adoc +189 -0
  28. data/docs/understanding/architecture.adoc +277 -0
  29. data/docs/understanding/index.adoc +148 -0
  30. data/docs/understanding/inkscape-wrapper.adoc +270 -0
  31. data/lib/vectory/capture.rb +165 -37
  32. data/lib/vectory/cli.rb +2 -0
  33. data/lib/vectory/configuration.rb +177 -0
  34. data/lib/vectory/conversion/ghostscript_strategy.rb +77 -0
  35. data/lib/vectory/conversion/inkscape_strategy.rb +124 -0
  36. data/lib/vectory/conversion/strategy.rb +58 -0
  37. data/lib/vectory/conversion.rb +104 -0
  38. data/lib/vectory/datauri.rb +1 -1
  39. data/lib/vectory/emf.rb +17 -5
  40. data/lib/vectory/eps.rb +45 -3
  41. data/lib/vectory/errors.rb +25 -0
  42. data/lib/vectory/file_magic.rb +2 -2
  43. data/lib/vectory/ghostscript_wrapper.rb +160 -0
  44. data/lib/vectory/image_resize.rb +98 -12
  45. data/lib/vectory/inkscape_wrapper.rb +205 -0
  46. data/lib/vectory/pdf.rb +76 -0
  47. data/lib/vectory/platform.rb +105 -0
  48. data/lib/vectory/ps.rb +47 -3
  49. data/lib/vectory/svg.rb +46 -3
  50. data/lib/vectory/svg_document.rb +40 -24
  51. data/lib/vectory/system_call.rb +36 -9
  52. data/lib/vectory/vector.rb +3 -23
  53. data/lib/vectory/version.rb +1 -1
  54. data/lib/vectory.rb +16 -11
  55. metadata +34 -3
  56. data/lib/vectory/inkscape_converter.rb +0 -141
@@ -0,0 +1,173 @@
1
+ ---
2
+ layout: default
3
+ title: Format Detection
4
+ parent: Features
5
+ nav_order: 2
6
+ ---
7
+ = Format Detection
8
+
9
+ How Vectory automatically detects vector image formats.
10
+
11
+ == Overview
12
+
13
+ Vectory can automatically detect image formats from file content, not just file extensions. This ensures reliable format identification even when files have incorrect or missing extensions.
14
+
15
+ == Detection Methods
16
+
17
+ === Magic Number Detection
18
+
19
+ Vectory uses file signatures (magic numbers) to identify binary formats:
20
+
21
+ [cols="1,2a"]
22
+ |===
23
+ |Format|Magic Number
24
+
25
+ |EPS
26
+ |%!PS-Adobe-3.0 EPSF-3.0
27
+
28
+ |PS
29
+ |%!PS-Adobe-3.0 (without EPSF)
30
+
31
+ |EMF
32
+ |\x01\x00\x00\x00 (EMF header signature)
33
+ |===
34
+
35
+ [source,ruby]
36
+ ----
37
+ # Automatic detection from file
38
+ image = Vectory::Image.from_path("unknown_file")
39
+ puts image.class # => Vectory::Eps, Vectory::Ps, Vectory::Emf, etc.
40
+ ----
41
+
42
+ === Content Inspection
43
+
44
+ SVG files are detected by XML content inspection:
45
+
46
+ [source,ruby]
47
+ ----
48
+ # Detects <svg> tag in XML content
49
+ svg = Vectory::Image.from_path("no_extension_file")
50
+ puts svg.class # => Vectory::Svg
51
+ ----
52
+
53
+ === Explicit Factory Methods
54
+
55
+ You can also explicitly specify the format:
56
+
57
+ [source,ruby]
58
+ ----
59
+ # Explicit format selection
60
+ eps = Vectory::Eps.from_path("diagram")
61
+ svg = Vectory::Svg.from_path("diagram")
62
+ ----
63
+
64
+ == Format Detection API
65
+
66
+ === FileMagic.detect_format
67
+
68
+ Detect format from content string:
69
+
70
+ [source,ruby]
71
+ ----
72
+ content = File.read("unknown_file")
73
+ format = Vectory::FileMagic.detect_format(content)
74
+
75
+ case format
76
+ when :eps
77
+ puts "This is an EPS file"
78
+ when :ps
79
+ puts "This is a PS file"
80
+ when :emf
81
+ puts "This is an EMF file"
82
+ when :svg
83
+ puts "This is an SVG file"
84
+ else
85
+ puts "Unknown format"
86
+ end
87
+ ----
88
+
89
+ === from_path Auto-Detection
90
+
91
+ [source,ruby]
92
+ ----
93
+ # Auto-detects format from file path and content
94
+ image = Vectory::Image.from_path("diagram")
95
+
96
+ if image.is_a?(Vectory::Eps)
97
+ puts "Detected EPS format"
98
+ elsif image.is_a?(Vectory::Svg)
99
+ puts "Detected SVG format"
100
+ end
101
+ ----
102
+
103
+ == Detection Priority
104
+
105
+ Vectory uses a cascading detection strategy:
106
+
107
+ . **Magic numbers** for EPS, PS, EMF (most reliable)
108
+ . **XML content inspection** for SVG (looks for `<svg>` tag)
109
+ . **File extension** as fallback (`.eps`, `.ps`, `.emf`, `.svg`)
110
+
111
+ [source,ruby]
112
+ ----
113
+ # Detection priority example
114
+ content = "%!PS-Adobe-3.0 EPSF-3.0"
115
+ format = Vectory::FileMagic.detect_format(content)
116
+ puts format # => :eps (magic number detected)
117
+ ----
118
+
119
+ == Edge Cases
120
+
121
+ === EPS vs PS Detection
122
+
123
+ The presence of `EPSF-3.0` distinguishes EPS from PS:
124
+
125
+ [source,ruby]
126
+ ----
127
+ eps_content = "%!PS-Adobe-3.0 EPSF-3.0"
128
+ ps_content = "%!PS-Adobe-3.0"
129
+
130
+ Vectory::FileMagic.detect_format(eps_content) # => :eps
131
+ Vectory::FileMagic.detect_format(ps_content) # => :ps
132
+ ----
133
+
134
+ === Invalid Files
135
+
136
+ [source,ruby]
137
+ ----
138
+ begin
139
+ image = Vectory::Image.from_path("corrupt_file")
140
+ rescue Vectory::ParsingError => e
141
+ puts "Failed to detect format: #{e.message}"
142
+ end
143
+ ----
144
+
145
+ === Empty Files
146
+
147
+ [source,ruby]
148
+ ----
149
+ empty_content = ""
150
+ format = Vectory::FileMagic.detect_format(empty_content)
151
+ puts format # => :unknown
152
+ ----
153
+
154
+ == Performance
155
+
156
+ Format detection is fast and efficient:
157
+
158
+ * **Magic number check**: First few bytes only
159
+ * **XML inspection**: Checks for `<svg>` tag
160
+ * **No full file parsing**: Detection is separate from conversion
161
+
162
+ [source,ruby]
163
+ ----
164
+ # Detection is fast even for large files
165
+ large_eps = Vectory::Image.from_path("100mb_diagram.eps")
166
+ # Detection completes in milliseconds
167
+ ----
168
+
169
+ == See Also
170
+
171
+ * link:conversion/[Conversion Features] - What happens after detection
172
+ * link:../understanding/magic-numbers/[Magic Numbers] - Technical details
173
+ * link:../reference/api/[API Reference] - Detection methods
@@ -0,0 +1,205 @@
1
+ ---
2
+ layout: default
3
+ title: Features
4
+ nav_order: 4
5
+ has_children: true
6
+ ---
7
+ = Features
8
+
9
+ Explore Vectory's conversion capabilities and format support.
10
+
11
+ == Overview
12
+
13
+ Vectory provides pairwise vector image conversion between EPS, PS, EMF, SVG, and PDF formats. This section covers all supported formats, conversion methods, and special features.
14
+
15
+ == Supported Formats
16
+
17
+ === Input Formats
18
+
19
+ link:formats/eps[**EPS (Encapsulated PostScript)**]::
20
+ PostScript-based vector format with bounding box support.
21
+
22
+ link:formats/ps[**PS (PostScript)**]::
23
+ Full PostScript document format.
24
+
25
+ link:formats/emf[**EMF (Enhanced Metafile)**]::
26
+ Windows vector metafile format.
27
+
28
+ link:formats/svg[**SVG (Scalable Vector Graphics)**]::
29
+ XML-based vector format for web graphics.
30
+
31
+ === Output Formats
32
+
33
+ All input formats can be converted to:
34
+ * link:formats/eps[EPS]
35
+ * link:formats/ps[PS]
36
+ * link:formats/emf[EMF]
37
+ * link:formats/svg[SVG]
38
+ * link:formats/pdf[PDF] (intermediate format)
39
+
40
+ == Key Features
41
+
42
+ link:conversion/[**Pairwise Conversion**]::
43
+ Convert any supported format to any other format.
44
+ +
45
+ Transparent conversion through PDF intermediate format when needed.
46
+
47
+ link:format-detection/[**Automatic Format Detection**]::
48
+ Detect image format from file content, not just extension.
49
+ +
50
+ Uses magic numbers for EPS, PS, EMF; content inspection for SVG.
51
+
52
+ link:dimension-query/[**Dimension Queries**]::
53
+ Query image dimensions without full rendering.
54
+ +
55
+ Fast dimension retrieval using external tools.
56
+
57
+ link:content-sources/[**Multiple Input Sources**]::
58
+ Load images from files, content strings, or data URIs.
59
+
60
+ link:speed/[**Performance**]::
61
+ Efficient caching and external tool management.
62
+ +
63
+ Singleton pattern for Inkscape wrapper, version detection.
64
+
65
+ == Conversion Paths
66
+
67
+ Vectory uses different external tools depending on the source and target formats.
68
+
69
+ === Direct Conversions (via Inkscape)
70
+
71
+ * **SVG → EPS/PS/EMF/PDF** (via Inkscape export)
72
+ * **EPS/PS/EMF/PDF → SVG** (via Inkscape import)
73
+
74
+ Inkscape handles direct conversions to and from SVG format.
75
+
76
+ === Direct Conversions (via emf2svg)
77
+
78
+ * **EMF → SVG** (via emf2svg gem)
79
+
80
+ The emf2svg gem provides native EMF to SVG conversion without external tool dependencies.
81
+
82
+ === Two-Step Conversions (via Ghostscript + Inkscape)
83
+
84
+ * **EPS → PDF → SVG** (Ghostscript for EPS→PDF, Inkscape for PDF→SVG)
85
+ * **PS → PDF → SVG** (Ghostscript for PS→PDF, Inkscape for PDF→SVG)
86
+
87
+ This two-step process preserves BoundingBox information from EPS/PS files.
88
+
89
+ === Conversion Tool Matrix
90
+
91
+ [cols="2,2a"]
92
+ |===
93
+ |Conversion|Tool Used
94
+
95
+ |SVG ↔ EPS
96
+ |Inkscape
97
+
98
+ |SVG ↔ PS
99
+ |Inkscape
100
+
101
+ |SVG ↔ EMF
102
+ |Inkscape (SVG→EMF), emf2svg (EMF→SVG)
103
+
104
+ |SVG ↔ PDF
105
+ |Inkscape
106
+
107
+ |EPS → PDF
108
+ |Ghostscript (preserves BoundingBox)
109
+
110
+ |PS → PDF
111
+ |Ghostscript (preserves BoundingBox)
112
+
113
+ |EPS/PS → SVG
114
+ |Ghostscript + Inkscape (two-step)
115
+
116
+ |EMF → SVG
117
+ |emf2svg gem
118
+ |===
119
+
120
+ See link:conversion/[Conversion Features] for detailed conversion paths and options.
121
+
122
+ == External Tool Integration
123
+
124
+ Vectory relies on external tools for actual conversion operations. The library provides a unified Ruby interface while delegating the heavy lifting to specialized tools.
125
+
126
+ link:inkscape-version/[**Inkscape Version Handling**]::
127
+ Automatic version detection for 0.x vs 1.x+ syntax differences.
128
+ +
129
+ Vectory detects the installed Inkscape version and uses appropriate command-line syntax.
130
+
131
+ link:external-dependencies/[**External Dependencies**]::
132
+ Comprehensive guide to all external tools and gems.
133
+ +
134
+ * **Inkscape** - Primary conversion engine for SVG, EPS, PS, EMF, PDF
135
+ * **Ghostscript** - EPS/PS to PDF conversion with BoundingBox preservation
136
+ * **emf2svg** - EMF format support via Ruby gem
137
+ * **moxml** - XML/HTML parsing for Metanorma integration
138
+
139
+ link:timeout-handling/[**Timeout and Error Handling**]::
140
+ Robust error handling for external tool failures.
141
+ +
142
+ Automatic tool discovery, timeout management, platform-specific process handling.
143
+
144
+ == Special Features
145
+
146
+ link:metadata/[**Metadata Preservation**]::
147
+ Preserves document metadata during conversions.
148
+
149
+ link:bounding-box/[**Bounding Box Handling**]::
150
+ Maintains EPS/PS bounding box information through conversion pipeline.
151
+
152
+ link:metanorma-integration/[**Metanorma Integration**]::
153
+ SVG mapping features for XML/HTML document processing.
154
+ +
155
+ Link rewriting, ID preservation, namespace handling.
156
+
157
+ == Quick Examples
158
+
159
+ === Convert EPS to SVG
160
+
161
+ [source,ruby]
162
+ ----
163
+ require 'vectory'
164
+
165
+ svg = Vectory::Eps.from_path("diagram.eps").to_svg
166
+ svg.write("diagram.svg")
167
+ ----
168
+
169
+ === Detect Format from Content
170
+
171
+ [source,ruby]
172
+ ----
173
+ content = File.read("unknown_file")
174
+ format = Vectory::FileMagic.detect_format(content)
175
+ # => :eps, :ps, :emf, :svg, or :unknown
176
+ ----
177
+
178
+ === Query Dimensions
179
+
180
+ [source,ruby]
181
+ ----
182
+ eps = Vectory::Eps.from_path("diagram.eps")
183
+ width, height = eps.dimensions
184
+ puts "Size: #{width}x#{height}"
185
+ ----
186
+
187
+ == Common Use Cases
188
+
189
+ **Batch conversion**::
190
+ Use link:guides/batch-processing[batch processing guide] for multiple files.
191
+
192
+ **Format validation**::
193
+ Use link:format-detection[format detection] to verify file types.
194
+
195
+ **Dimension extraction**::
196
+ Use link:dimension-query[dimension queries] for fast size retrieval.
197
+
198
+ **Metanorma documents**::
199
+ Use link:metanorma-integration[SVG mapping] for XML integration.
200
+
201
+ == See Also
202
+
203
+ * link:../understanding/[Understanding Vectory] - How features work internally
204
+ * link:../guides/[Guides] - Practical how-to guides
205
+ * link:../reference/[Reference] - Complete API documentation
@@ -0,0 +1,214 @@
1
+ ---
2
+ layout: default
3
+ title: Core Concepts
4
+ parent: Getting Started
5
+ nav_order: 3
6
+ ---
7
+
8
+ Understanding Vectory's architecture and design principles.
9
+
10
+ == Purpose
11
+
12
+ This section explains Vectory's core concepts including its class hierarchy, conversion pipeline, and design patterns. Understanding these concepts will help you use Vectory effectively.
13
+
14
+ == Class Hierarchy
15
+
16
+ Vectory uses an object-oriented, inheritance-based architecture:
17
+
18
+ [source,ruby]
19
+ ----
20
+ Vectory::Image # Base class for all images
21
+ └── Vectory::Vector # Abstract base for vector formats
22
+ ├── Vectory::Eps # Encapsulated PostScript
23
+ ├── Vectory::Ps # PostScript
24
+ ├── Vectory::Emf # Enhanced Metafile
25
+ ├── Vectory::Svg # Scalable Vector Graphics
26
+ └── Vectory::Pdf # PDF (intermediate format)
27
+ ----
28
+
29
+ Each format class inherits from `Vectory::Vector`, which provides common functionality for vector image manipulation.
30
+
31
+ == Factory Methods
32
+
33
+ All format classes support four factory methods for creating image objects:
34
+
35
+ === from_path
36
+
37
+ Load from a file path:
38
+
39
+ [source,ruby]
40
+ ----
41
+ eps = Vectory::Eps.from_path("diagram.eps")
42
+ ----
43
+
44
+ === from_content
45
+
46
+ Load from a content string:
47
+
48
+ [source,ruby]
49
+ ----
50
+ content = File.read("diagram.eps")
51
+ eps = Vectory::Eps.from_content(content)
52
+ ----
53
+
54
+ === from_datauri
55
+
56
+ Load from a data URI:
57
+
58
+ [source,ruby]
59
+ ----
60
+ uri = "data:image/svg+xml;base64,PHN2Zy..."
61
+ svg = Vectory::Svg.from_datauri(uri)
62
+ ----
63
+
64
+ === from_node
65
+
66
+ Load from an XML node (Metanorma integration):
67
+
68
+ [source,ruby]
69
+ ----
70
+ # Used internally by Metanorma's SVG mapping
71
+ ----
72
+
73
+ == Conversion Methods
74
+
75
+ Each format class can convert to any other format:
76
+
77
+ [source,ruby]
78
+ ----
79
+ # Convert EPS to SVG
80
+ svg = Vectory::Eps.from_path("diagram.eps").to_svg
81
+
82
+ # Convert SVG to EMF
83
+ emf = Vectory::Svg.from_path("drawing.svg").to_emf
84
+
85
+ # Convert EMF to PDF
86
+ pdf = Vectory::Emf.from_path("chart.emf").to_pdf
87
+ ----
88
+
89
+ == Conversion Pipeline
90
+
91
+ Vectory uses a flexible conversion pipeline:
92
+
93
+ === Direct Conversions
94
+
95
+ Some conversions are direct:
96
+ * **SVG → EPS/PS/EMF/PDF**: Via Inkscape
97
+ * **EMF → SVG**: Via emf2svg
98
+
99
+ === Two-Step Conversions
100
+
101
+ Other conversions use an intermediate PDF format:
102
+ * **EPS/PS → PDF → SVG**: Via Ghostscript + Inkscape
103
+
104
+ This two-step conversion ensures:
105
+ * BoundingBox preservation from EPS/PS
106
+ * High-quality output from Inkscape
107
+
108
+ == Properties
109
+
110
+ All image objects have these properties:
111
+
112
+ [source,ruby]
113
+ ----
114
+ eps = Vectory::Eps.from_path("diagram.eps")
115
+
116
+ # Raw content
117
+ content = eps.content
118
+
119
+ # Original file location
120
+ original_path = eps.initial_path
121
+
122
+ # Current file location (raises error if not written)
123
+ current_path = eps.path # => NotWrittenToDiskError
124
+
125
+ # Image dimensions
126
+ width, height = eps.dimensions
127
+
128
+ # Write to file
129
+ output_path = eps.write("output.eps")
130
+ ----
131
+
132
+ == File System Operations
133
+
134
+ Vectory distinguishes between two file paths:
135
+
136
+ * **`initial_path`**: Where the file was originally loaded from (read-only)
137
+ * **`path`**: Where the file is currently written (raises error if not yet written)
138
+
139
+ [source,ruby]
140
+ ----
141
+ eps = Vectory::Eps.from_path("input.eps")
142
+ puts eps.initial_path # => "input.eps"
143
+
144
+ puts eps.path # => raises NotWrittenToDiskError
145
+
146
+ eps.write("output.eps")
147
+ puts eps.path # => "output.eps"
148
+ ----
149
+
150
+ Writing without arguments uses a temporary directory:
151
+
152
+ [source,ruby]
153
+ ----
154
+ eps = Vectory::Eps.from_path("input.eps")
155
+ temp_path = eps.write # => "/tmp/vectory_20250119..."
156
+ ----
157
+
158
+ == Format Detection
159
+
160
+ Vectory detects formats using:
161
+ * **Magic numbers** for EPS, PS, EMF (file signatures)
162
+ * **Content inspection** for SVG (XML structure)
163
+ * **File extension** as fallback
164
+
165
+ [source,ruby]
166
+ ----
167
+ # Auto-detect from file
168
+ image = Vectory::Image.from_path("unknown_file")
169
+ puts image.class # => Vectory::Eps, Vectory::Svg, etc.
170
+
171
+ # Detect format from content
172
+ content = File.read("unknown_file")
173
+ format = Vectory::FileMagic.detect_format(content)
174
+ puts format # => :eps, :ps, :emf, :svg, or :unknown
175
+ ----
176
+
177
+ == Error Handling
178
+
179
+ Vectory uses a structured error hierarchy:
180
+
181
+ [source,ruby]
182
+ ----
183
+ Vectory::Error # Base error class
184
+ ├── SystemCallError # External tool execution failures
185
+ ├── NotWrittenToDiskError # Accessing path before write
186
+ ├── ParsingError # Content parsing failures
187
+ ├── InkscapeNotFoundError # Inkscape not available
188
+ ├── ConversionError # Conversion failures with diagnostics
189
+ └── InkscapeQueryError # Dimension query failures
190
+ ----
191
+
192
+ == External Tool Integration
193
+
194
+ Vectory uses the https://en.wikipedia.org/wiki/Singleton_pattern[Singleton pattern] for external tool wrappers:
195
+
196
+ [source,ruby]
197
+ ----
198
+ # Shared Inkscape instance across conversions
199
+ inkscape = Vectory::InkscapeWrapper.instance
200
+
201
+ # Version is cached
202
+ puts inkscape.version # => "1.3.0" (cached)
203
+
204
+ # Tool discovery is done once
205
+ puts inkscape.executable # => "/usr/bin/inkscape"
206
+ ----
207
+
208
+ This design improves performance and ensures consistent behavior.
209
+
210
+ == Next Steps
211
+
212
+ . Explore link:../features/[Features] for supported formats and conversions
213
+ . Check link:../guides/[Guides] for practical examples
214
+ . See link:../understanding/[Understanding] for internal architecture details
@@ -0,0 +1,37 @@
1
+ ---
2
+ layout: default
3
+ title: Getting Started
4
+ nav_order: 2
5
+ has_children: true
6
+ ---
7
+ = Getting Started
8
+
9
+ Get up and running with Vectory quickly.
10
+
11
+ == Overview
12
+
13
+ This section will help you install Vectory and perform your first vector image conversions.
14
+
15
+ == Prerequisites
16
+
17
+ Before installing Vectory, ensure you have the following:
18
+
19
+ * **Ruby** 3.1 or later
20
+ * **Inkscape** 0.92 or later (recommended: 1.x)
21
+ * **Ghostscript** (for EPS/PS conversions)
22
+
23
+ See link:installation[Installation] for detailed setup instructions.
24
+
25
+ == Quick Start Path
26
+
27
+ . link:installation[Install Vectory]
28
+ . link:quick-start[Try basic conversions]
29
+ . link:core-concepts[Understand core concepts]
30
+
31
+ == Next Steps
32
+
33
+ After getting started, explore:
34
+
35
+ * link:../features/[Features] - All supported formats and conversions
36
+ * link:../guides/[Guides] - Practical how-to guides
37
+ * link:../reference/[Reference] - Complete API documentation