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,903 @@
1
+ = Conversion Pipeline
2
+ :page-nav_order: 1
3
+ :page-parent: Core Concepts
4
+
5
+ == Purpose
6
+
7
+ This document provides a detailed explanation of Postsvg's three-stage conversion pipeline that transforms PostScript into SVG. Understanding this pipeline is essential for comprehending how Postsvg processes PostScript commands and generates standards-compliant SVG output.
8
+
9
+ == References
10
+
11
+ * link:../index.adoc[Documentation Home]
12
+ * link:../concepts.adoc[Core Concepts Overview]
13
+ * link:../architecture.adoc[Architecture Documentation]
14
+ * link:../architecture/conversion-pipeline.adoc[Conversion Pipeline Implementation]
15
+ * link:postscript-language.adoc[PostScript Language Fundamentals]
16
+
17
+ == Concepts
18
+
19
+ **Three-Stage Architecture**:: The conversion process is divided into tokenization, interpretation, and generation stages for clear separation of concerns.
20
+
21
+ **Tokenization**:: The process of breaking PostScript source code into discrete tokens (numbers, operators, strings, etc.).
22
+
23
+ **Interpretation**:: Executing PostScript tokens using a stack-based model to build graphics operations.
24
+
25
+ **SVG Generation**:: Transforming interpreted graphics operations into standards-compliant SVG markup.
26
+
27
+ **Pipeline Flow**:: Data flows unidirectionally from PostScript source through tokens to execution context to SVG output.
28
+
29
+ == Pipeline Overview
30
+
31
+ The conversion pipeline implements a clean three-stage architecture that separates concerns and enables independent testing and optimization of each stage.
32
+
33
+ .Three-Stage Conversion Pipeline
34
+ [source]
35
+ ----
36
+ ┌─────────────────────────────────────────────────────────────────────┐
37
+ │ STAGE 1: TOKENIZATION │
38
+ │ │
39
+ │ PostScript Source (.ps/.eps) │
40
+ │ │ │
41
+ │ ▼ │
42
+ │ ┌─────────────┐ │
43
+ │ │ Tokenizer │ Lexical Analysis │
44
+ │ └──────┬──────┘ │
45
+ │ │ │
46
+ │ ▼ │
47
+ │ Token Stream: [ │
48
+ │ { type: "number", value: 100 }, │
49
+ │ { type: "number", value: 200 }, │
50
+ │ { type: "operator", value: "moveto" }, │
51
+ │ ... │
52
+ │ ] │
53
+ └─────────────────────────────────────────────────────────────────────┘
54
+
55
+
56
+ ┌─────────────────────────────────────────────────────────────────────┐
57
+ │ STAGE 2: INTERPRETATION │
58
+ │ │
59
+ │ Token Stream │
60
+ │ │ │
61
+ │ ▼ │
62
+ │ ┌─────────────────┐ │
63
+ │ │ Interpreter │ Stack-Based Execution │
64
+ │ └────────┬────────┘ │
65
+ │ │ │
66
+ │ ├──> ┌──────────────────┐ │
67
+ │ │ │ ExecutionContext │ │
68
+ │ │ │ - Operand Stack │ │
69
+ │ │ │ - Dict Stack │ │
70
+ │ │ │ - Graphics State│ │
71
+ │ │ └──────────────────┘ │
72
+ │ │ │
73
+ │ ▼ │
74
+ │ Graphics Operations: [ │
75
+ │ { type: :moveto, x: 100, y: 200 }, │
76
+ │ { type: :lineto, x: 300, y: 200 }, │
77
+ │ { type: :stroke, color: "#000000" }, │
78
+ │ ... │
79
+ │ ] │
80
+ └─────────────────────────────────────────────────────────────────────┘
81
+
82
+
83
+ ┌─────────────────────────────────────────────────────────────────────┐
84
+ │ STAGE 3: SVG GENERATION │
85
+ │ │
86
+ │ Graphics Operations │
87
+ │ │ │
88
+ │ ▼ │
89
+ │ ┌──────────────┐ │
90
+ │ │ SVG Generator│ XML Document Construction │
91
+ │ └──────┬───────┘ │
92
+ │ │ │
93
+ │ ▼ │
94
+ │ SVG Document: │
95
+ │ <?xml version="1.0"?> │
96
+ │ <svg viewBox="0 0 400 400"> │
97
+ │ <path d="M 100 200 L 300 200" stroke="#000" /> │
98
+ │ </svg> │
99
+ └─────────────────────────────────────────────────────────────────────┘
100
+ ----
101
+
102
+ == Stage 1: Tokenization
103
+
104
+ === Purpose of Tokenization
105
+
106
+ Tokenization (also called lexical analysis) breaks the PostScript source into meaningful units called tokens. This stage handles:
107
+
108
+ * Character-by-character parsing of PostScript syntax
109
+ * Recognition of PostScript language elements
110
+ * Creation of typed token objects
111
+ * String and comment processing
112
+
113
+ === Token Types
114
+
115
+ The [`Tokenizer`](../../lib/postsvg/tokenizer.rb:8) recognizes these token types:
116
+
117
+ **Numeric Tokens**::
118
+ Numbers in PostScript can be integers or floating-point values.
119
+ +
120
+ [source,postscript]
121
+ ----
122
+ 42 % Integer → { type: "number", value: 42 }
123
+ 3.14159 % Float → { type: "number", value: 3.14159 }
124
+ -10 % Negative → { type: "number", value: -10 }
125
+ 1.5e-3 % Scientific → { type: "number", value: 0.0015 }
126
+ ----
127
+
128
+ **Operator Tokens**::
129
+ PostScript commands that perform operations.
130
+ +
131
+ [source,postscript]
132
+ ----
133
+ moveto % → { type: "operator", value: "moveto" }
134
+ lineto % → { type: "operator", value: "lineto" }
135
+ stroke % → { type: "operator", value: "stroke" }
136
+ ----
137
+
138
+ **Name Tokens**::
139
+ Identifiers starting with `/` used for dictionary keys and variable names.
140
+ +
141
+ [source,postscript]
142
+ ----
143
+ /MyFont % → { type: "name", value: "MyFont" }
144
+ /x % → { type: "name", value: "x" }
145
+ ----
146
+
147
+ **String Tokens**::
148
+ Text enclosed in parentheses or angle brackets.
149
+ +
150
+ [source,postscript]
151
+ ----
152
+ (Hello) % → { type: "string", value: "Hello" }
153
+ <48656c6c6f> % Hex → { type: "hex_string", value: "Hello" }
154
+ ----
155
+
156
+ **Delimiter Tokens**::
157
+ Brackets and braces for arrays and procedures.
158
+ +
159
+ [source,postscript]
160
+ ----
161
+ [ % → { type: "brace", value: "[" }
162
+ ] % → { type: "brace", value: "]" }
163
+ { % → { type: "brace", value: "{" }
164
+ } % → { type: "brace", value: "}" }
165
+ ----
166
+
167
+ === Tokenization Process
168
+
169
+ .Tokenization Example
170
+ [example]
171
+ ====
172
+ Given this PostScript code:
173
+
174
+ [source,postscript]
175
+ ----
176
+ 10 20 moveto
177
+ 30 40 lineto
178
+ stroke
179
+ ----
180
+
181
+ The tokenizer produces this token stream:
182
+
183
+ [source,ruby]
184
+ ----
185
+ [
186
+ { type: "number", value: 10 },
187
+ { type: "number", value: 20 },
188
+ { type: "operator", value: "moveto" },
189
+ { type: "number", value: 30 },
190
+ { type: "number", value: 40 },
191
+ { type: "operator", value: "lineto" },
192
+ { type: "operator", value: "stroke" }
193
+ ]
194
+ ----
195
+ ====
196
+
197
+ === Handling Complex Structures
198
+
199
+ **Procedures (Executable Arrays)**::
200
+ Code blocks enclosed in braces are tokenized as procedure boundaries.
201
+ +
202
+ [source,postscript]
203
+ ----
204
+ { 100 100 moveto 200 200 lineto stroke }
205
+ ----
206
+ +
207
+ Becomes:
208
+ +
209
+ [source,ruby]
210
+ ----
211
+ [
212
+ { type: "brace", value: "{" },
213
+ { type: "number", value: 100 },
214
+ { type: "number", value: 100 },
215
+ { type: "operator", value: "moveto" },
216
+ { type: "number", value: 200 },
217
+ { type: "number", value: 200 },
218
+ { type: "operator", value: "lineto" },
219
+ { type: "operator", value: "stroke" },
220
+ { type: "brace", value: "}" }
221
+ ]
222
+ ----
223
+
224
+ **Arrays**::
225
+ Data arrays enclosed in square brackets.
226
+ +
227
+ [source,postscript]
228
+ ----
229
+ [1 2 3 4]
230
+ ----
231
+ +
232
+ Becomes:
233
+ +
234
+ [source,ruby]
235
+ ----
236
+ [
237
+ { type: "brace", value: "[" },
238
+ { type: "number", value: 1 },
239
+ { type: "number", value: 2 },
240
+ { type: "number", value: 3 },
241
+ { type: "number", value: 4 },
242
+ { type: "brace", value: "]" }
243
+ ]
244
+ ----
245
+
246
+ **Comments**::
247
+ Comments starting with `%` are ignored during tokenization.
248
+ +
249
+ [source,postscript]
250
+ ----
251
+ 100 200 moveto % Move to starting point
252
+ ----
253
+ +
254
+ The comment is stripped; only the command tokens remain.
255
+
256
+ === Tokenizer Implementation
257
+
258
+ The tokenizer is implemented in [`lib/postsvg/tokenizer.rb`](../../lib/postsvg/tokenizer.rb:1).
259
+
260
+ .Using the Tokenizer
261
+ [example]
262
+ ====
263
+ [source,ruby]
264
+ ----
265
+ require 'postsvg'
266
+
267
+ ps_code = <<~PS
268
+ newpath
269
+ 50 50 moveto
270
+ 150 50 lineto
271
+ 100 150 lineto
272
+ closepath
273
+ fill
274
+ PS
275
+
276
+ tokens = Postsvg::Tokenizer.tokenize(ps_code)
277
+
278
+ tokens.each do |token|
279
+ puts "#{token[:type].ljust(10)} → #{token[:value]}"
280
+ end
281
+
282
+ # Output:
283
+ # operator → newpath
284
+ # number → 50
285
+ # number → 50
286
+ # operator → moveto
287
+ # number → 150
288
+ # number → 50
289
+ # operator → lineto
290
+ # number → 100
291
+ # number → 150
292
+ # operator → lineto
293
+ # operator → closepath
294
+ # operator → fill
295
+ ----
296
+ ====
297
+
298
+ == Stage 2: Interpretation
299
+
300
+ === Purpose of Interpretation
301
+
302
+ The interpretation stage executes PostScript tokens using a stack-based model. This stage:
303
+
304
+ * Maintains operand and dictionary stacks
305
+ * Executes operators through the command pattern
306
+ * Builds graphics state progressively
307
+ * Handles control flow (loops, conditionals)
308
+ * Manages graphics state stack (gsave/grestore)
309
+
310
+ === Execution Model
311
+
312
+ PostScript uses a **stack-based execution model** where:
313
+
314
+ 1. Operands are pushed onto the operand stack
315
+ 2. Operators pop operands, perform operations, and push results
316
+ 3. The graphics state accumulates drawing commands
317
+
318
+ .Stack-Based Execution
319
+ [example]
320
+ ====
321
+ [source,postscript]
322
+ ----
323
+ 10 20 add % Push 10, push 20, add (pop 2, push 30)
324
+ 3 mul % Push 3, multiply (pop 30 and 3, push 90)
325
+ ----
326
+
327
+ Execution trace:
328
+
329
+ [source]
330
+ ----
331
+ Step 1: Push 10
332
+ Stack: [10]
333
+
334
+ Step 2: Push 20
335
+ Stack: [10, 20]
336
+
337
+ Step 3: Execute 'add'
338
+ Pop: 20, 10
339
+ Calculate: 10 + 20 = 30
340
+ Push: 30
341
+ Stack: [30]
342
+
343
+ Step 4: Push 3
344
+ Stack: [30, 3]
345
+
346
+ Step 5: Execute 'mul'
347
+ Pop: 3, 30
348
+ Calculate: 30 * 3 = 90
349
+ Push: 90
350
+ Stack: [90]
351
+ ----
352
+ ====
353
+
354
+ === Execution Context
355
+
356
+ The [`ExecutionContext`](../../lib/postsvg/execution_context.rb:9) maintains the runtime state:
357
+
358
+ **Operand Stack**:: Values waiting for operators
359
+ **Dictionary Stack**:: Variable definitions and scopes
360
+ **Graphics State**:: Current drawing parameters
361
+ **Graphics State Stack**:: Saved states from `gsave`
362
+ **SVG Output Buffer**:: Accumulating SVG elements
363
+
364
+ .Execution Context Structure
365
+ [source]
366
+ ----
367
+ ExecutionContext
368
+ ├── Operand Stack
369
+ │ ├── 100
370
+ │ ├── 200
371
+ │ └── "Hello"
372
+
373
+ ├── Dictionary Stack
374
+ │ ├── System Dictionary (built-in operators)
375
+ │ └── User Dictionary (user definitions)
376
+
377
+ ├── Graphics State
378
+ │ ├── Current Path: [(M 10,10), (L 90,10)]
379
+ │ ├── Current Point: (90, 10)
380
+ │ ├── Line Width: 1.0
381
+ │ ├── Stroke Color: #000000
382
+ │ ├── Fill Color: #808080
383
+ │ └── Transformation Matrix: [1 0 0 1 0 0]
384
+
385
+ ├── Graphics State Stack (for gsave/grestore)
386
+ │ ├── [Saved State 1]
387
+ │ └── [Saved State 2]
388
+
389
+ └── SVG Output Buffer
390
+ ├── <path d="M 10 10 L 90 10" stroke="#000"/>
391
+ └── <path d="M 50 50 L 150 150" fill="#808080"/>
392
+ ----
393
+
394
+ === Command Pattern
395
+
396
+ Each PostScript operator is implemented as a command object following the Command Pattern.
397
+
398
+ .Command Pattern Example
399
+ [example]
400
+ ====
401
+ The `moveto` operator is implemented in [`lib/postsvg/commands/path/moveto.rb`](../../lib/postsvg/commands/path/moveto.rb:1):
402
+
403
+ [source,ruby]
404
+ ----
405
+ module Postsvg
406
+ module Commands
407
+ module Path
408
+ class Moveto < Base
409
+ def execute(context)
410
+ y = context.pop_number # Pop Y coordinate
411
+ x = context.pop_number # Pop X coordinate
412
+ context.graphics_state.moveto(x, y)
413
+ end
414
+ end
415
+ end
416
+ end
417
+ end
418
+ ----
419
+
420
+ When the interpreter encounters a `moveto` token:
421
+
422
+ 1. Lookup command object in registry
423
+ 2. Call `command.execute(context)`
424
+ 3. Command pops operands from stack
425
+ 4. Command updates graphics state
426
+ ====
427
+
428
+ === Interpreter Implementation
429
+
430
+ The interpreter is implemented in [`lib/postsvg/interpreter.rb`](../../lib/postsvg/interpreter.rb:1).
431
+
432
+ .Interpreting PostScript
433
+ [example]
434
+ ====
435
+ [source,ruby]
436
+ ----
437
+ require 'postsvg'
438
+
439
+ # PostScript code
440
+ ps_code = <<~PS
441
+ newpath
442
+ 100 100 moveto
443
+ 200 100 lineto
444
+ 200 200 lineto
445
+ 100 200 lineto
446
+ closepath
447
+ 0.5 setgray
448
+ fill
449
+ PS
450
+
451
+ # Tokenize
452
+ tokens = Postsvg::Tokenizer.tokenize(ps_code)
453
+
454
+ # Interpret
455
+ interpreter = Postsvg::Interpreter.new
456
+ bbox = { llx: 0, lly: 0, urx: 300, ury: 300, width: 300, height: 300 }
457
+ result = interpreter.interpret(tokens, bbox)
458
+
459
+ # Result contains:
460
+ # - :svg - Complete SVG document
461
+ # - :paths - Individual path elements
462
+ # - :elements - All SVG elements
463
+ puts result[:paths].length # 1 path (the filled square)
464
+ ----
465
+ ====
466
+
467
+ === Graphics State Management
468
+
469
+ The graphics state tracks all drawing parameters:
470
+
471
+ **Path State**::
472
+ * Current path segments
473
+ * Current point (x, y)
474
+ * Path closed or open
475
+
476
+ **Color State**::
477
+ * Fill color (RGB, grayscale, CMYK)
478
+ * Stroke color
479
+ * Color space
480
+
481
+ **Line Attributes**::
482
+ * Line width
483
+ * Line cap style (butt, round, square)
484
+ * Line join style (miter, round, bevel)
485
+ * Dash pattern and offset
486
+ * Miter limit
487
+
488
+ **Transformation Matrix**::
489
+ * Current transformation matrix (CTM)
490
+ * Enables translation, rotation, scaling
491
+
492
+ .Graphics State Stack Operations
493
+ [example]
494
+ ====
495
+ [source,postscript]
496
+ ----
497
+ gsave % Save current state
498
+ 2 setlinewidth
499
+ 1 0 0 setrgbcolor
500
+ newpath
501
+ 50 50 moveto
502
+ 150 50 lineto
503
+ stroke
504
+ grestore % Restore previous state
505
+
506
+ % Line width and color are back to original values
507
+ ----
508
+
509
+ The graphics state stack allows nesting:
510
+
511
+ [source]
512
+ ----
513
+ Initial State: linewidth=1, color=black
514
+
515
+ ├─ gsave → Save State 1
516
+ │ Modified: linewidth=2, color=red
517
+ │ │
518
+ │ ├─ gsave → Save State 2
519
+ │ │ Modified: linewidth=5, color=blue
520
+ │ │ (draw with blue, width 5)
521
+ │ │
522
+ │ └─ grestore → Restore to State 1
523
+ │ Back to: linewidth=2, color=red
524
+ │ (draw with red, width 2)
525
+
526
+ └─ grestore → Restore to Initial State
527
+ Back to: linewidth=1, color=black
528
+ ----
529
+ ====
530
+
531
+ == Stage 3: SVG Generation
532
+
533
+ === Purpose of SVG Generation
534
+
535
+ The SVG generation stage transforms the accumulated graphics operations into standards-compliant SVG markup. This stage handles:
536
+
537
+ * Coordinate system transformation (PostScript → SVG)
538
+ * Path data formatting
539
+ * Style attribute generation
540
+ * SVG document structure
541
+ * Optimization and cleanup
542
+
543
+ === SVG Document Structure
544
+
545
+ The generator creates well-formed SVG documents:
546
+
547
+ [source,xml]
548
+ ----
549
+ <?xml version="1.0" encoding="UTF-8"?>
550
+ <svg xmlns="http://www.w3.org/2000/svg"
551
+ width="400"
552
+ height="400"
553
+ viewBox="0 0 400 400">
554
+ <defs>
555
+ <!-- Patterns, gradients, clip paths -->
556
+ </defs>
557
+ <g transform="translate(0 400) scale(1 -1)">
558
+ <!-- Graphics elements with Y-axis flip -->
559
+ <path d="M 100 100 L 300 100" stroke="#000000" stroke-width="1"/>
560
+ </g>
561
+ </svg>
562
+ ----
563
+
564
+ **Key Elements:**
565
+
566
+ `xmlns` attribute:: SVG namespace declaration
567
+ `viewBox`:: Coordinate system bounds from BoundingBox
568
+ `width`/`height`:: Physical dimensions
569
+ `<defs>`:: Reusable definitions (patterns, gradients)
570
+ `<g>` transform:: Y-axis flip for coordinate system conversion
571
+
572
+ === Coordinate System Transformation
573
+
574
+ PostScript and SVG use different coordinate systems:
575
+
576
+ **PostScript**: Origin at bottom-left, Y increases upward
577
+ **SVG**: Origin at top-left, Y increases downward
578
+
579
+ .Coordinate System Difference
580
+ [source]
581
+ ----
582
+ PostScript (0,0 at bottom-left) SVG (0,0 at top-left)
583
+
584
+ 100 ┌────────┐ 400 ┌────────┐ 0
585
+ │ │ 0 │ │
586
+ │ ● │ │ ● │
587
+ 0 └────────┘ └────────┘ 400
588
+ 0 400 0 400
589
+
590
+ Point (200, 50) in PostScript
591
+ becomes (200, 350) in SVG
592
+ ----
593
+
594
+ The generator applies a transformation:
595
+
596
+ [source,xml]
597
+ ----
598
+ <g transform="translate(0 400) scale(1 -1)">
599
+ <!-- Graphics here use PostScript coordinates -->
600
+ </g>
601
+ ----
602
+
603
+ This transform:
604
+ 1. Translates origin to bottom-left
605
+ 2. Flips Y-axis (scale Y by -1)
606
+
607
+ === Path Generation
608
+
609
+ PostScript paths are converted to SVG path data:
610
+
611
+ .Path Conversion
612
+ [example]
613
+ ====
614
+ PostScript path operations:
615
+
616
+ [source,postscript]
617
+ ----
618
+ newpath
619
+ 100 100 moveto
620
+ 200 100 lineto
621
+ 200 200 lineto
622
+ 100 200 lineto
623
+ closepath
624
+ ----
625
+
626
+ Becomes SVG path data:
627
+
628
+ [source,xml]
629
+ ----
630
+ <path d="M 100 100 L 200 100 L 200 200 L 100 200 Z"/>
631
+ ----
632
+
633
+ Path command mapping:
634
+
635
+ * `moveto` → `M x y`
636
+ * `lineto` → `L x y`
637
+ * `curveto` → `C x1 y1 x2 y2 x3 y3`
638
+ * `closepath` → `Z`
639
+ ====
640
+
641
+ === Style Generation
642
+
643
+ Graphics state attributes become SVG style attributes:
644
+
645
+ **Stroke Operations**::
646
+ [source,xml]
647
+ ----
648
+ <path d="..."
649
+ stroke="#000000"
650
+ stroke-width="2"
651
+ fill="none"/>
652
+ ----
653
+
654
+ **Fill Operations**::
655
+ [source,xml]
656
+ ----
657
+ <path d="..."
658
+ fill="#808080"
659
+ stroke="none"/>
660
+ ----
661
+
662
+ **Line Attributes**::
663
+ [source,xml]
664
+ ----
665
+ <path d="..."
666
+ stroke-linecap="round"
667
+ stroke-linejoin="miter"
668
+ stroke-dasharray="5,3"/>
669
+ ----
670
+
671
+ === SVG Generator Implementation
672
+
673
+ The generator is implemented in [`lib/postsvg/svg_generator.rb`](../../lib/postsvg/svg_generator.rb:1).
674
+
675
+ .Using the SVG Generator
676
+ [example]
677
+ ====
678
+ [source,ruby]
679
+ ----
680
+ require 'postsvg'
681
+
682
+ # The generator is used internally by the interpreter
683
+ generator = Postsvg::SvgGenerator.new
684
+
685
+ # Add paths during interpretation
686
+ generator.add_path(
687
+ [{ type: :moveto, x: 10, y: 10 },
688
+ { type: :lineto, x: 90, y: 90 }],
689
+ graphics_state,
690
+ :stroke
691
+ )
692
+
693
+ # Generate final SVG
694
+ svg = generator.generate(
695
+ width: 100,
696
+ height: 100,
697
+ viewbox: "0 0 100 100"
698
+ )
699
+
700
+ puts svg
701
+ # <?xml version="1.0" encoding="UTF-8"?>
702
+ # <svg xmlns="http://www.w3.org/2000/svg" ...>
703
+ # <path d="M 10 10 L 90 90" stroke="#000" .../>
704
+ # </svg>
705
+ ----
706
+ ====
707
+
708
+ == Pipeline Data Flow
709
+
710
+ === Complete Flow Example
711
+
712
+ .End-to-End Conversion
713
+ [example]
714
+ ====
715
+ Starting with PostScript:
716
+
717
+ [source,postscript]
718
+ ----
719
+ %!PS-Adobe-3.0
720
+ %%BoundingBox: 0 0 200 200
721
+ newpath
722
+ 50 50 moveto
723
+ 150 150 lineto
724
+ 1 0 0 setrgbcolor
725
+ 2 setlinewidth
726
+ stroke
727
+ showpage
728
+ ----
729
+
730
+ **Stage 1: Tokenization**
731
+
732
+ [source,ruby]
733
+ ----
734
+ tokens = [
735
+ { type: "operator", value: "newpath" },
736
+ { type: "number", value: 50 },
737
+ { type: "number", value: 50 },
738
+ { type: "operator", value: "moveto" },
739
+ { type: "number", value: 150 },
740
+ { type: "number", value: 150 },
741
+ { type: "operator", value: "lineto" },
742
+ { type: "number", value: 1 },
743
+ { type: "number", value: 0 },
744
+ { type: "number", value: 0 },
745
+ { type: "operator", value: "setrgbcolor" },
746
+ { type: "number", value: 2 },
747
+ { type: "operator", value: "setlinewidth" },
748
+ { type: "operator", value: "stroke" },
749
+ { type: "operator", value: "showpage" }
750
+ ]
751
+ ----
752
+
753
+ **Stage 2: Interpretation**
754
+
755
+ [source,ruby]
756
+ ----
757
+ # Execute tokens sequentially:
758
+ # - newpath: Clear current path
759
+ # - 50, 50, moveto: Set current point to (50, 50)
760
+ # - 150, 150, lineto: Add line to (150, 150)
761
+ # - 1, 0, 0, setrgbcolor: Set stroke color to red
762
+ # - 2, setlinewidth: Set line width to 2
763
+ # - stroke: Paint the path with current stroke settings
764
+
765
+ graphics_operations = [
766
+ { type: :path, segments: [
767
+ { type: :moveto, x: 50, y: 50 },
768
+ { type: :lineto, x: 150, y: 150 }
769
+ ],
770
+ operation: :stroke,
771
+ stroke_color: "#FF0000",
772
+ stroke_width: 2
773
+ }
774
+ ]
775
+ ----
776
+
777
+ **Stage 3: SVG Generation**
778
+
779
+ [source,xml]
780
+ ----
781
+ <?xml version="1.0" encoding="UTF-8"?>
782
+ <svg xmlns="http://www.w3.org/2000/svg"
783
+ width="200"
784
+ height="200"
785
+ viewBox="0 0 200 200">
786
+ <g transform="translate(0 200) scale(1 -1)">
787
+ <path d="M 50 50 L 150 150"
788
+ stroke="#FF0000"
789
+ stroke-width="2"
790
+ fill="none"/>
791
+ </g>
792
+ </svg>
793
+ ----
794
+ ====
795
+
796
+ == Error Handling
797
+
798
+ === Strict vs. Lenient Modes
799
+
800
+ The pipeline supports two error handling modes:
801
+
802
+ **Strict Mode**::
803
+ Fails immediately on unknown operators or execution errors.
804
+ +
805
+ [source,ruby]
806
+ ----
807
+ interpreter = Postsvg::Interpreter.new(strict_mode: true)
808
+ begin
809
+ result = interpreter.interpret(tokens, bbox)
810
+ rescue Postsvg::UnsupportedOperatorError => e
811
+ puts "Unsupported: #{e.message}"
812
+ end
813
+ ----
814
+
815
+ **Lenient Mode**::
816
+ Continues processing, adding SVG comments for errors.
817
+ +
818
+ [source,ruby]
819
+ ----
820
+ interpreter = Postsvg::Interpreter.new(strict_mode: false)
821
+ result = interpreter.interpret(tokens, bbox)
822
+ # SVG may contain: <!-- Unhandled operator: customop -->
823
+ ----
824
+
825
+ === Error Recovery
826
+
827
+ In lenient mode, the pipeline recovers from:
828
+
829
+ * Unknown operators
830
+ * Stack underflow
831
+ * Type mismatches
832
+ * Invalid coordinates
833
+
834
+ .Error Recovery Example
835
+ [example]
836
+ ====
837
+ [source,postscript]
838
+ ----
839
+ 10 moveto % ERROR: moveto needs 2 args
840
+ 100 200 moveto % This succeeds
841
+ ----
842
+
843
+ Lenient mode output:
844
+
845
+ [source,xml]
846
+ ----
847
+ <!-- Error executing moveto: insufficient operands -->
848
+ <path d="M 100 200 ..." />
849
+ ----
850
+ ====
851
+
852
+ == Performance Characteristics
853
+
854
+ === Pipeline Performance
855
+
856
+ **Tokenization**: O(n) where n = character count
857
+
858
+ * Single-pass character scanning
859
+ * Minimal backtracking
860
+ * Efficient string handling
861
+
862
+ **Interpretation**: O(t × c) where:
863
+
864
+ * t = token count
865
+ * c = average command complexity
866
+
867
+ **SVG Generation**: O(p) where p = path complexity
868
+
869
+ * Linear path traversal
870
+ * Minimal string operations
871
+
872
+ === Optimization Strategies
873
+
874
+ **Token Stream Optimization**::
875
+ * Tokens created once, reused
876
+ * No token copying
877
+
878
+ **Execution Optimization**::
879
+ * Hash-based command lookup: O(1)
880
+ * Stack operations: O(1)
881
+ * State snapshots for gsave/grestore
882
+
883
+ **SVG Optimization**::
884
+ * Path data minimization
885
+ * Redundant attribute removal
886
+ * ClipPath deduplication
887
+
888
+ == Next Steps
889
+
890
+ * Explore link:graphics-state.adoc[Graphics State Management] for state tracking details
891
+ * Review link:coordinate-systems.adoc[Coordinate Systems] for transformation details
892
+ * See link:path-operations.adoc[Path Operations] for path construction
893
+ * Check link:svg-generation.adoc[SVG Generation] for output details
894
+ * Read link:../architecture/conversion-pipeline.adoc[Pipeline Implementation] for code details
895
+
896
+ == Bibliography
897
+
898
+ * link:../architecture/conversion-pipeline.adoc[Conversion Pipeline Implementation]
899
+ * link:postscript-language.adoc[PostScript Language Fundamentals]
900
+ * link:graphics-state.adoc[Graphics State Management]
901
+ * link:../api-reference/interpreter.adoc[Interpreter API Reference]
902
+ * link:https://www.adobe.com/jp/print/postscript/pdfs/PLRM.pdf[PostScript Language Reference Manual]
903
+ * link:https://www.w3.org/TR/SVG/[SVG Specification]