postsvg 0.1.0 → 0.3.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.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +105 -0
  3. data/CLAUDE.md +173 -0
  4. data/Gemfile +2 -3
  5. data/README.adoc +456 -179
  6. data/Rakefile +100 -0
  7. data/TODO.roadmap/00-architecture.md +139 -0
  8. data/TODO.roadmap/01-autoload-migration.md +39 -0
  9. data/TODO.roadmap/02-isolate-dormant-code.md +51 -0
  10. data/TODO.roadmap/03-domain-model.md +66 -0
  11. data/TODO.roadmap/04-lexer.md +40 -0
  12. data/TODO.roadmap/05-parser.md +43 -0
  13. data/TODO.roadmap/06-graphics-state.md +45 -0
  14. data/TODO.roadmap/07-matrix-and-color.md +37 -0
  15. data/TODO.roadmap/08-svg-builder.md +51 -0
  16. data/TODO.roadmap/09-renderer.md +52 -0
  17. data/TODO.roadmap/10-visitor.md +58 -0
  18. data/TODO.roadmap/11-operator-coverage.md +103 -0
  19. data/TODO.roadmap/12-svg-domain.md +62 -0
  20. data/TODO.roadmap/13-translation-handlers.md +69 -0
  21. data/TODO.roadmap/14-ps-serializer.md +49 -0
  22. data/TODO.roadmap/15-cli-and-public-api.md +47 -0
  23. data/TODO.roadmap/16-specs.md +84 -0
  24. data/TODO.roadmap/17-docs-sync.md +47 -0
  25. data/TODO.roadmap/18-performance-and-determinism.md +47 -0
  26. data/TODO.roadmap/19-error-model.md +45 -0
  27. data/TODO.roadmap/20-font-and-text.md +46 -0
  28. data/TODO.roadmap/21-images.md +45 -0
  29. data/TODO.roadmap/22-forms-and-resources.md +25 -0
  30. data/TODO.roadmap/23-level2-level3.md +52 -0
  31. data/TODO.roadmap/24-ci-and-release.md +42 -0
  32. data/TODO.roadmap/README.md +77 -0
  33. data/docs/.gitignore +29 -0
  34. data/docs/CHANGELOG.md +114 -0
  35. data/docs/COMPLETE_DOCUMENTATION_STATUS.md +376 -0
  36. data/docs/DEPLOYMENT.md +456 -0
  37. data/docs/DEPLOYMENT_INSTRUCTIONS.md +229 -0
  38. data/docs/DOCUMENTATION_PLAN.md +425 -0
  39. data/docs/FINAL_SUMMARY.md +657 -0
  40. data/docs/Gemfile +15 -0
  41. data/docs/README.md +327 -0
  42. data/docs/_config.yml +99 -0
  43. data/docs/advanced-topics.adoc +370 -0
  44. data/docs/api-reference/colors.adoc +705 -0
  45. data/docs/api-reference/converter.adoc +699 -0
  46. data/docs/api-reference/execution-context.adoc +1210 -0
  47. data/docs/api-reference/graphics-state.adoc +1070 -0
  48. data/docs/api-reference/interpreter.adoc +810 -0
  49. data/docs/api-reference/matrix.adoc +1179 -0
  50. data/docs/api-reference/path-builder.adoc +1284 -0
  51. data/docs/api-reference/postsvg-module.adoc +388 -0
  52. data/docs/api-reference/svg-generator.adoc +891 -0
  53. data/docs/api-reference/tokenizer.adoc +925 -0
  54. data/docs/api-reference.adoc +221 -0
  55. data/docs/architecture/command-registry.adoc +1191 -0
  56. data/docs/architecture/conversion-pipeline.adoc +746 -0
  57. data/docs/architecture/design-decisions.adoc +999 -0
  58. data/docs/architecture/generator-stage.adoc +1115 -0
  59. data/docs/architecture/graphics-state-model.adoc +1089 -0
  60. data/docs/architecture/interpreter-stage.adoc +1125 -0
  61. data/docs/architecture/parser-stage.adoc +1051 -0
  62. data/docs/architecture.adoc +354 -0
  63. data/docs/cli-reference/batch-command.adoc +616 -0
  64. data/docs/cli-reference/check-command.adoc +677 -0
  65. data/docs/cli-reference/cli-options.adoc +802 -0
  66. data/docs/cli-reference/convert-command.adoc +462 -0
  67. data/docs/cli-reference/version-command.adoc +296 -0
  68. data/docs/cli-reference.adoc +317 -0
  69. data/docs/concepts/conversion-pipeline.adoc +903 -0
  70. data/docs/concepts/coordinate-systems.adoc +836 -0
  71. data/docs/concepts/graphics-state.adoc +861 -0
  72. data/docs/concepts/path-operations.adoc +1076 -0
  73. data/docs/concepts/postscript-language.adoc +859 -0
  74. data/docs/concepts/svg-generation.adoc +937 -0
  75. data/docs/concepts.adoc +198 -0
  76. data/docs/contributing.adoc +443 -0
  77. data/docs/development.adoc +420 -0
  78. data/docs/faq.adoc +493 -0
  79. data/docs/getting-started/basic-usage.adoc +538 -0
  80. data/docs/getting-started/common-workflows.adoc +577 -0
  81. data/docs/getting-started/first-conversion.adoc +492 -0
  82. data/docs/getting-started/installation.adoc +534 -0
  83. data/docs/getting-started.adoc +94 -0
  84. data/docs/index.adoc +248 -0
  85. data/docs/optimization.adoc +196 -0
  86. data/docs/ps2svg_compatibility.adoc +149 -0
  87. data/docs/quick-reference.adoc +453 -0
  88. data/docs/sitemap.adoc +337 -0
  89. data/docs/troubleshooting.adoc +486 -0
  90. data/docs/validation.adoc +772 -0
  91. data/exe/postsvg +1 -0
  92. data/lib/postsvg/cli.rb +104 -57
  93. data/lib/postsvg/color.rb +132 -0
  94. data/lib/postsvg/errors.rb +68 -3
  95. data/lib/postsvg/format_number.rb +22 -0
  96. data/lib/postsvg/graphics_context.rb +80 -0
  97. data/lib/postsvg/graphics_stack.rb +43 -0
  98. data/lib/postsvg/model/literals/array.rb +41 -0
  99. data/lib/postsvg/model/literals/dictionary.rb +34 -0
  100. data/lib/postsvg/model/literals/hex.rb +37 -0
  101. data/lib/postsvg/model/literals/name.rb +40 -0
  102. data/lib/postsvg/model/literals/number.rb +36 -0
  103. data/lib/postsvg/model/literals/procedure.rb +41 -0
  104. data/lib/postsvg/model/literals/string.rb +30 -0
  105. data/lib/postsvg/model/literals.rb +19 -0
  106. data/lib/postsvg/model/operator.rb +58 -0
  107. data/lib/postsvg/model/operators/arithmetic.rb +264 -0
  108. data/lib/postsvg/model/operators/boolean.rb +182 -0
  109. data/lib/postsvg/model/operators/color.rb +74 -0
  110. data/lib/postsvg/model/operators/container.rb +186 -0
  111. data/lib/postsvg/model/operators/control_flow.rb +119 -0
  112. data/lib/postsvg/model/operators/device.rb +21 -0
  113. data/lib/postsvg/model/operators/dictionary.rb +118 -0
  114. data/lib/postsvg/model/operators/font.rb +121 -0
  115. data/lib/postsvg/model/operators/graphics_state.rb +84 -0
  116. data/lib/postsvg/model/operators/painting.rb +29 -0
  117. data/lib/postsvg/model/operators/path.rb +169 -0
  118. data/lib/postsvg/model/operators/stack.rb +72 -0
  119. data/lib/postsvg/model/operators/transformations.rb +103 -0
  120. data/lib/postsvg/model/operators.rb +89 -0
  121. data/lib/postsvg/model/program.rb +68 -0
  122. data/lib/postsvg/model/token.rb +43 -0
  123. data/lib/postsvg/model.rb +17 -0
  124. data/lib/postsvg/options.rb +29 -0
  125. data/lib/postsvg/renderer.rb +85 -0
  126. data/lib/postsvg/serializer.rb +325 -0
  127. data/lib/postsvg/source/ast_builder.rb +308 -0
  128. data/lib/postsvg/source/lexer.rb +322 -0
  129. data/lib/postsvg/source/operand_stack.rb +55 -0
  130. data/lib/postsvg/source.rb +21 -0
  131. data/lib/postsvg/svg/attribute_parser.rb +45 -0
  132. data/lib/postsvg/svg/clip_path_registry.rb +44 -0
  133. data/lib/postsvg/svg/document.rb +22 -0
  134. data/lib/postsvg/svg/element.rb +84 -0
  135. data/lib/postsvg/svg/elements/circle.rb +36 -0
  136. data/lib/postsvg/svg/elements/clip_path.rb +26 -0
  137. data/lib/postsvg/svg/elements/defs.rb +24 -0
  138. data/lib/postsvg/svg/elements/ellipse.rb +38 -0
  139. data/lib/postsvg/svg/elements/group.rb +37 -0
  140. data/lib/postsvg/svg/elements/image.rb +35 -0
  141. data/lib/postsvg/svg/elements/line.rb +36 -0
  142. data/lib/postsvg/svg/elements/path.rb +32 -0
  143. data/lib/postsvg/svg/elements/polygon.rb +12 -0
  144. data/lib/postsvg/svg/elements/polyline.rb +32 -0
  145. data/lib/postsvg/svg/elements/rect.rb +44 -0
  146. data/lib/postsvg/svg/elements/svg.rb +39 -0
  147. data/lib/postsvg/svg/elements/text.rb +42 -0
  148. data/lib/postsvg/svg/elements.rb +31 -0
  149. data/lib/postsvg/svg/paint.rb +34 -0
  150. data/lib/postsvg/svg/parser.rb +39 -0
  151. data/lib/postsvg/svg/path_data/command.rb +27 -0
  152. data/lib/postsvg/svg/path_data/parser.rb +82 -0
  153. data/lib/postsvg/svg/path_data.rb +17 -0
  154. data/lib/postsvg/svg/stroke.rb +31 -0
  155. data/lib/postsvg/svg/transform_list.rb +59 -0
  156. data/lib/postsvg/svg.rb +22 -0
  157. data/lib/postsvg/svg_builder.rb +249 -0
  158. data/lib/postsvg/translation/arc_converter.rb +86 -0
  159. data/lib/postsvg/translation/bounding_box.rb +59 -0
  160. data/lib/postsvg/translation/context.rb +34 -0
  161. data/lib/postsvg/translation/handler_registry.rb +38 -0
  162. data/lib/postsvg/translation/handlers/circle_handler.rb +28 -0
  163. data/lib/postsvg/translation/handlers/clip_path_handler.rb +13 -0
  164. data/lib/postsvg/translation/handlers/defs_handler.rb +15 -0
  165. data/lib/postsvg/translation/handlers/ellipse_handler.rb +31 -0
  166. data/lib/postsvg/translation/handlers/group_handler.rb +21 -0
  167. data/lib/postsvg/translation/handlers/image_handler.rb +20 -0
  168. data/lib/postsvg/translation/handlers/line_handler.rb +23 -0
  169. data/lib/postsvg/translation/handlers/open_handler.rb +18 -0
  170. data/lib/postsvg/translation/handlers/path_handler.rb +356 -0
  171. data/lib/postsvg/translation/handlers/polygon_handler.rb +33 -0
  172. data/lib/postsvg/translation/handlers/polyline_handler.rb +31 -0
  173. data/lib/postsvg/translation/handlers/rect_handler.rb +27 -0
  174. data/lib/postsvg/translation/handlers/shared.rb +110 -0
  175. data/lib/postsvg/translation/handlers/svg_handler.rb +25 -0
  176. data/lib/postsvg/translation/handlers/text_handler.rb +56 -0
  177. data/lib/postsvg/translation/handlers.rb +25 -0
  178. data/lib/postsvg/translation/ps_renderer.rb +105 -0
  179. data/lib/postsvg/translation/record_emitter.rb +35 -0
  180. data/lib/postsvg/translation.rb +17 -0
  181. data/lib/postsvg/version.rb +1 -1
  182. data/lib/postsvg/visitors/ps_visitor/arithmetic.rb +125 -0
  183. data/lib/postsvg/visitors/ps_visitor/boolean.rb +105 -0
  184. data/lib/postsvg/visitors/ps_visitor/color.rb +53 -0
  185. data/lib/postsvg/visitors/ps_visitor/common.rb +66 -0
  186. data/lib/postsvg/visitors/ps_visitor/container.rb +164 -0
  187. data/lib/postsvg/visitors/ps_visitor/control_flow.rb +110 -0
  188. data/lib/postsvg/visitors/ps_visitor/device.rb +20 -0
  189. data/lib/postsvg/visitors/ps_visitor/dictionary.rb +89 -0
  190. data/lib/postsvg/visitors/ps_visitor/font.rb +93 -0
  191. data/lib/postsvg/visitors/ps_visitor/graphics_state.rb +55 -0
  192. data/lib/postsvg/visitors/ps_visitor/painting.rb +90 -0
  193. data/lib/postsvg/visitors/ps_visitor/path.rb +112 -0
  194. data/lib/postsvg/visitors/ps_visitor/stack.rb +47 -0
  195. data/lib/postsvg/visitors/ps_visitor/transformations.rb +101 -0
  196. data/lib/postsvg/visitors/ps_visitor.rb +208 -0
  197. data/lib/postsvg/visitors.rb +9 -0
  198. data/lib/postsvg.rb +93 -59
  199. data/lychee.toml +86 -0
  200. metadata +216 -11
  201. data/postsvg.gemspec +0 -38
@@ -0,0 +1,538 @@
1
+
2
+ = Basic Usage
3
+ :page-nav_order: 2
4
+ :page-parent: Getting Started
5
+
6
+ == Purpose
7
+
8
+ This guide demonstrates the fundamental ways to use Postsvg for converting PostScript and EPS files to SVG format. You'll learn both command-line and Ruby API usage patterns that cover most common use cases.
9
+
10
+ == References
11
+
12
+ * link:../index.adoc[Documentation Home]
13
+ * link:../getting-started.adoc[Getting Started Overview]
14
+ * link:installation.adoc[Installation Guide]
15
+ * link:first-conversion.adoc[First Conversion Tutorial]
16
+ * link:../api-reference.adoc[API Reference]
17
+ * link:../cli-reference.adoc[CLI Reference]
18
+
19
+ == Concepts
20
+
21
+ **Command-Line Interface (CLI)**:: The `postsvg` command that allows conversion without writing Ruby code.
22
+
23
+ **Ruby API**:: Programmatic interface for integrating Postsvg into Ruby applications.
24
+
25
+ **PostScript File**:: Text file containing PostScript commands (`.ps` extension).
26
+
27
+ **EPS File**:: Encapsulated PostScript file designed for embedding graphics (`.eps` extension).
28
+
29
+ **SVG Output**:: Scalable Vector Graphics format, an XML-based vector image format.
30
+
31
+ == Command-Line Usage
32
+
33
+ === Basic File Conversion
34
+
35
+ The simplest way to convert a file:
36
+
37
+ [source,sh]
38
+ ----
39
+ postsvg convert input.ps output.svg <1>
40
+ ----
41
+ <1> Convert `input.ps` to `output.svg`
42
+
43
+ Where,
44
+
45
+ `input.ps`:: Source PostScript or EPS file to convert
46
+ `output.svg`:: Destination SVG file to create
47
+
48
+ .Convert EPS to SVG
49
+ [example]
50
+ ====
51
+ [source,sh]
52
+ ----
53
+ # Convert diagram.eps to diagram.svg
54
+ postsvg convert diagram.eps diagram.svg
55
+
56
+ # Output:
57
+ # Successfully converted diagram.eps to diagram.svg
58
+ ----
59
+ ====
60
+
61
+ === Convert to Standard Output
62
+
63
+ Output SVG to stdout instead of a file:
64
+
65
+ [source,sh]
66
+ ----
67
+ postsvg convert input.ps <1>
68
+ ----
69
+ <1> Writes SVG to stdout
70
+
71
+ .Redirect output to file
72
+ [example]
73
+ ====
74
+ [source,sh]
75
+ ----
76
+ # Convert and redirect to file
77
+ postsvg convert input.ps > output.svg
78
+
79
+ # Convert and pipe to another command
80
+ postsvg convert input.ps | xmllint --format - > formatted.svg
81
+ ----
82
+ ====
83
+
84
+ === Batch Conversion
85
+
86
+ Convert multiple files at once:
87
+
88
+ [source,sh]
89
+ ----
90
+ postsvg batch INPUT_DIR [OUTPUT_DIR] <1>
91
+ ----
92
+ <1> Convert all PS/EPS files in `INPUT_DIR`
93
+
94
+ Where,
95
+
96
+ `INPUT_DIR`:: Directory containing `.ps` and `.eps` files
97
+ `OUTPUT_DIR`:: (Optional) Directory for SVG output. If omitted, SVG files are created in `INPUT_DIR`
98
+
99
+ .Batch convert directory
100
+ [example]
101
+ ====
102
+ [source,sh]
103
+ ----
104
+ # Convert all files in ps_files/ to svg_files/
105
+ postsvg batch ps_files/ svg_files/
106
+
107
+ # Output:
108
+ # Found 3 file(s) to convert
109
+ # ✓ ps_files/diagram1.ps → svg_files/diagram1.svg
110
+ # ✓ ps_files/diagram2.eps → svg_files/diagram2.svg
111
+ # ✓ ps_files/figure.ps → svg_files/figure.svg
112
+ ----
113
+ ====
114
+
115
+ === File Validation
116
+
117
+ Check files before conversion:
118
+
119
+ [source,sh]
120
+ ----
121
+ postsvg check FILE... <1>
122
+ ----
123
+ <1> Validate one or more PostScript files
124
+
125
+ .Validate before converting
126
+ [example]
127
+ ====
128
+ [source,sh]
129
+ ----
130
+ # Validate single file
131
+ postsvg check document.ps
132
+ # ✓ document.ps - Valid PostScript file
133
+
134
+ # Validate multiple files
135
+ postsvg check *.ps
136
+ # ✓ file1.ps - Valid PostScript file
137
+ # ✓ file2.ps - Valid PostScript file
138
+ # ✗ file3.ps - Syntax error: Unmatched delimiter
139
+ ----
140
+ ====
141
+
142
+ == Ruby API Usage
143
+
144
+ === Basic Conversion
145
+
146
+ Convert PostScript content to SVG:
147
+
148
+ [source,ruby]
149
+ ----
150
+ require 'postsvg' <1>
151
+
152
+ ps_content = File.read('input.ps') <2>
153
+ svg_content = Postsvg.convert(ps_content) <3>
154
+
155
+ File.write('output.svg', svg_content) <4>
156
+ ----
157
+ <1> Load the Postsvg library
158
+ <2> Read PostScript content
159
+ <3> Convert to SVG
160
+ <4> Save SVG to file
161
+
162
+ Where,
163
+
164
+ [`Postsvg.convert(ps_content)`](../../lib/postsvg.rb:13):: Converts PostScript string to SVG string
165
+
166
+ Returns:: SVG markup as a string
167
+
168
+ .Convert in-memory content
169
+ [example]
170
+ ====
171
+ [source,ruby]
172
+ ----
173
+ require 'postsvg'
174
+
175
+ ps_content = <<~PS
176
+ %!PS-Adobe-3.0 EPSF-3.0
177
+ %%BoundingBox: 0 0 100 100
178
+ newpath
179
+ 50 50 moveto
180
+ 90 50 lineto
181
+ stroke
182
+ PS
183
+
184
+ svg = Postsvg.convert(ps_content)
185
+ puts svg
186
+ # Outputs SVG markup
187
+ ----
188
+ ====
189
+
190
+ === File-Based Conversion
191
+
192
+ Convert files directly:
193
+
194
+ [source,ruby]
195
+ ----
196
+ require 'postsvg'
197
+
198
+ Postsvg.convert_file('input.ps', 'output.svg') <1>
199
+ ----
200
+ <1> Read, convert, and save in one call
201
+
202
+ Where,
203
+
204
+ [`Postsvg.convert_file(input_path, output_path)`](../../lib/postsvg.rb:24):: Convert file to file
205
+
206
+ `input_path`:: Path to PostScript or EPS file
207
+ `output_path`:: (Optional) Path for SVG output. If omitted, returns SVG content without saving.
208
+
209
+ Returns:: SVG content string, or output path if `output_path` provided
210
+
211
+ .Get SVG content without saving
212
+ [example]
213
+ ====
214
+ [source,ruby]
215
+ ----
216
+ require 'postsvg'
217
+
218
+ # Get SVG content
219
+ svg_content = Postsvg.convert_file('input.ps')
220
+
221
+ # Process further
222
+ if svg_content.include?('<path')
223
+ puts "Conversion successful"
224
+ end
225
+
226
+ # Or save later
227
+ File.write('output.svg', svg_content)
228
+ ----
229
+ ====
230
+
231
+ === Using the Converter Class
232
+
233
+ For more control over conversion:
234
+
235
+ [source,ruby]
236
+ ----
237
+ require 'postsvg'
238
+
239
+ ps_content = File.read('input.ps')
240
+ converter = Postsvg::Converter.new(ps_content) <1>
241
+ svg_output = converter.convert <2>
242
+
243
+ File.write('output.svg', svg_output)
244
+ ----
245
+ <1> Create converter instance
246
+ <2> Perform conversion
247
+
248
+ Where,
249
+
250
+ [`Postsvg::Converter.new(ps_content, strict_mode: false)`](../../lib/postsvg/converter.rb:12):: Create converter with options
251
+
252
+ `ps_content`:: PostScript content as string
253
+ `strict_mode`:: (Optional) Enable strict validation (default: `false`)
254
+
255
+ .Using strict mode
256
+ [example]
257
+ ====
258
+ [source,ruby]
259
+ ----
260
+ require 'postsvg'
261
+
262
+ ps_content = File.read('input.ps')
263
+
264
+ # Enable strict mode - fail on unknown operators
265
+ converter = Postsvg::Converter.new(
266
+ ps_content,
267
+ strict_mode: true
268
+ )
269
+
270
+ begin
271
+ svg = converter.convert
272
+ puts "Conversion successful"
273
+ rescue Postsvg::Error => e
274
+ puts "Error: #{e.message}"
275
+ end
276
+ ----
277
+ ====
278
+
279
+ == Common Usage Patterns
280
+
281
+ === Workflow 1: Validate Then Convert
282
+
283
+ [source,sh]
284
+ ----
285
+ # 1. Validate file
286
+ postsvg check document.ps
287
+
288
+ # 2. If valid, convert
289
+ if [ $? -eq 0 ]; then
290
+ postsvg convert document.ps document.svg
291
+ fi
292
+ ----
293
+
294
+ === Workflow 2: Batch Process Directory
295
+
296
+ [source,ruby]
297
+ ----
298
+ require 'postsvg'
299
+
300
+ Dir.glob('*.ps').each do |ps_file|
301
+ svg_file = ps_file.sub('.ps', '.svg')
302
+
303
+ begin
304
+ Postsvg.convert_file(ps_file, svg_file)
305
+ puts "✓ Converted #{ps_file}"
306
+ rescue Postsvg::Error => e
307
+ puts "✗ Failed #{ps_file}: #{e.message}"
308
+ end
309
+ end
310
+ ----
311
+
312
+ === Workflow 3: Convert with Error Handling
313
+
314
+ [source,ruby]
315
+ ----
316
+ require 'postsvg'
317
+
318
+ def safe_convert(input_file, output_file)
319
+ ps_content = File.read(input_file)
320
+
321
+ # Try strict mode first
322
+ converter = Postsvg::Converter.new(ps_content, strict_mode: true)
323
+ svg = converter.convert
324
+
325
+ File.write(output_file, svg)
326
+ puts "Success: #{output_file}"
327
+ rescue Postsvg::Error => e
328
+ puts "Error: #{e.message}"
329
+ puts "Trying lenient mode..."
330
+
331
+ # Fall back to lenient mode
332
+ converter = Postsvg::Converter.new(ps_content, strict_mode: false)
333
+ svg = converter.convert
334
+ File.write(output_file, svg)
335
+ operators ignored)"
336
+ rescue StandardError => e
337
+ puts "Failed: #{e.message}"
338
+ end
339
+
340
+ safe_convert('input.ps', 'output.svg')
341
+ ----
342
+ ====
343
+
344
+ === Workflow 4: Process Web Uploads
345
+
346
+ [source,ruby]
347
+ ----
348
+ require 'postsvg'
349
+
350
+ def convert_uploaded_file(upload)
351
+ # Read uploaded content
352
+ ps_content = upload.read
353
+
354
+ # Convert to SVG
355
+ svg = Postsvg.convert(ps_content)
356
+
357
+ # Create temporary SVG file
358
+ svg_file = Tempfile.new(['converted', '.svg'])
359
+ svg_file.write(svg)
360
+ svg_file.rewind
361
+
362
+ svg_file
363
+ end
364
+
365
+ # In Rails controller
366
+ def convert
367
+ uploaded = params[:file]
368
+ svg_file = convert_uploaded_file(uploaded)
369
+ send_file svg_file.path,
370
+ filename: 'converted.svg',
371
+ type: 'image/svg+xml'
372
+ end
373
+ ----
374
+
375
+ == Output Examples
376
+
377
+ === Simple Rectangle
378
+
379
+ **Input PostScript:**
380
+ [source,postscript]
381
+ ----
382
+ %!PS-Adobe-3.0 EPSF-3.0
383
+ %%BoundingBox: 0 0 100 100
384
+ newpath
385
+ 10 10 moveto
386
+ 90 10 lineto
387
+ 90 90 lineto
388
+ 10 90 lineto
389
+ closepath
390
+ 0.5 setgray
391
+ fill
392
+ ----
393
+
394
+ **Output SVG:**
395
+ [source,xml]
396
+ ----
397
+ <?xml version="1.0" standalone="no"?>
398
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
399
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
400
+ <svg xmlns="http://www.w3.org/2000/svg"
401
+ width="100" height="100"
402
+ viewBox="0 0 100 100">
403
+ <path d="M 10 90 L 90 90 L 90 10 L 10 10 Z"
404
+ fill="#808080"/>
405
+ </svg>
406
+ ----
407
+
408
+ === Stroked Path
409
+
410
+ **Input PostScript:**
411
+ [source,postscript]
412
+ ----
413
+ %!PS-Adobe-3.0
414
+ %%BoundingBox: 0 0 200 200
415
+ newpath
416
+ 50 50 moveto
417
+ 150 150 lineto
418
+ 2 setlinewidth
419
+ 1 0 0 setrgbcolor
420
+ stroke
421
+ ----
422
+
423
+ **Output SVG:**
424
+ [source,xml]
425
+ ----
426
+ <svg xmlns="http://www.w3.org/2000/svg"
427
+ width="200" height="200"
428
+ viewBox="0 0 200 200">
429
+ <path d="M 50 150 L 150 50"
430
+ stroke="#ff0000"
431
+ stroke-width="2"
432
+ fill="none"/>
433
+ </svg>
434
+ ----
435
+
436
+ == Error Handling
437
+
438
+ === Handling Conversion Errors
439
+
440
+ [source,ruby]
441
+ ----
442
+ require 'postsvg'
443
+
444
+ begin
445
+ svg = Postsvg.convert(ps_content)
446
+ File.write('output.svg', svg)
447
+ rescue Postsvg::ParseError => e
448
+ puts "Parse error: #{e.message}"
449
+ rescue Postsvg::ConversionError => e
450
+ puts "Conversion error: #{e.message}"
451
+ rescue Postsvg::Error => e
452
+ puts "Error: #{e.message}"
453
+ rescue StandardError => e
454
+ puts "Unexpected error: #{e.message}"
455
+ end
456
+ ----
457
+
458
+ === Validating Before Conversion
459
+
460
+ [source,ruby]
461
+ ----
462
+ require 'postsvg'
463
+
464
+ def validate_and_convert(ps_file, svg_file)
465
+ # First validate
466
+ result = system("postsvg check #{ps_file}")
467
+
468
+ unless result
469
+ puts "Validation failed"
470
+ return false
471
+ end
472
+
473
+ # Then convert
474
+ Postsvg.convert_file(ps_file, svg_file)
475
+ true
476
+ rescue => e
477
+ puts "Error: #{e.message}"
478
+ false
479
+ end
480
+ ----
481
+
482
+ == Performance Tips
483
+
484
+ === Batch Processing Efficiently
485
+
486
+ [source,ruby]
487
+ ----
488
+ require 'postsvg'
489
+
490
+ files = Dir.glob('*.ps')
491
+
492
+ # Process in batches to manage memory
493
+ files.each_slice(10) do |batch|
494
+ batch.each do |file|
495
+ Postsvg.convert_file(file, file.sub('.ps', '.svg'))
496
+ end
497
+
498
+ # Force garbage collection between batches
499
+ GC.start
500
+ end
501
+ ----
502
+
503
+ === Reusing Converter Instance
504
+
505
+ [source,ruby]
506
+ ----
507
+ require 'postsvg'
508
+
509
+ # Don't do this - creates new instance each time
510
+ files.each do |file|
511
+ Postsvg.convert_file(file, file.sub('.ps', '.svg'))
512
+ end
513
+
514
+ # Better - though note Converter is not thread-safe
515
+ ps_files.map do |ps_content|
516
+ converter = Postsvg::Converter.new(ps_content)
517
+ converter.convert
518
+ end
519
+ ----
520
+
521
+ == Next Steps
522
+
523
+ After mastering basic usage:
524
+
525
+ * Follow the link:first-conversion.adoc[First Conversion Tutorial] for a detailed walkthrough
526
+ * Learn link:common-workflows.adoc[Common Workflows] for real-world scenarios
527
+ * Explore link:../cli-reference.adoc[CLI Reference] for all command options
528
+ * Read link:../api-reference.adoc[API Reference] for advanced API usage
529
+ * Check link:../troubleshooting.adoc[Troubleshooting] if you encounter issues
530
+
531
+ == Bibliography
532
+
533
+ * link:installation.adoc[Installation Guide]
534
+ * link:first-conversion.adoc[First Conversion Tutorial]
535
+ * link:../api-reference.adoc[API Reference]
536
+ * link:../cli-reference.adoc[CLI Reference]
537
+ * link:https://rubygems.org/gems/postsvg[Postsvg on RubyGems]
538
+ puts "Partial success: #{output_file} (some