postsvg 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rubocop.yml +19 -0
- data/.rubocop_todo.yml +141 -0
- data/Gemfile +15 -0
- data/LICENSE +25 -0
- data/README.adoc +473 -0
- data/Rakefile +10 -0
- data/docs/POSTSCRIPT.adoc +13 -0
- data/docs/postscript/fundamentals.adoc +356 -0
- data/docs/postscript/graphics-model.adoc +406 -0
- data/docs/postscript/implementation-notes.adoc +314 -0
- data/docs/postscript/index.adoc +153 -0
- data/docs/postscript/operators/arithmetic.adoc +461 -0
- data/docs/postscript/operators/control-flow.adoc +230 -0
- data/docs/postscript/operators/dictionary.adoc +191 -0
- data/docs/postscript/operators/graphics-state.adoc +528 -0
- data/docs/postscript/operators/index.adoc +288 -0
- data/docs/postscript/operators/painting.adoc +475 -0
- data/docs/postscript/operators/path-construction.adoc +553 -0
- data/docs/postscript/operators/stack-manipulation.adoc +374 -0
- data/docs/postscript/operators/transformations.adoc +479 -0
- data/docs/postscript/svg-mapping.adoc +369 -0
- data/exe/postsvg +6 -0
- data/lib/postsvg/cli.rb +103 -0
- data/lib/postsvg/colors.rb +33 -0
- data/lib/postsvg/converter.rb +214 -0
- data/lib/postsvg/errors.rb +11 -0
- data/lib/postsvg/graphics_state.rb +158 -0
- data/lib/postsvg/interpreter.rb +891 -0
- data/lib/postsvg/matrix.rb +106 -0
- data/lib/postsvg/parser/postscript_parser.rb +87 -0
- data/lib/postsvg/parser/transform.rb +21 -0
- data/lib/postsvg/parser.rb +18 -0
- data/lib/postsvg/path_builder.rb +101 -0
- data/lib/postsvg/svg_generator.rb +78 -0
- data/lib/postsvg/tokenizer.rb +161 -0
- data/lib/postsvg/version.rb +5 -0
- data/lib/postsvg.rb +78 -0
- data/postsvg.gemspec +38 -0
- data/scripts/regenerate_fixtures.rb +28 -0
- metadata +118 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
= PostScript Graphics Model
|
|
2
|
+
|
|
3
|
+
== General
|
|
4
|
+
|
|
5
|
+
The PostScript graphics model provides a powerful framework for describing
|
|
6
|
+
two-dimensional vector graphics. It consists of three main components:
|
|
7
|
+
|
|
8
|
+
* Current path - The geometric shape being constructed
|
|
9
|
+
* Graphics state - Parameters affecting how shapes are rendered
|
|
10
|
+
* Painting model - How paths are converted to visible output
|
|
11
|
+
|
|
12
|
+
Understanding this model is essential for working with PostScript graphics.
|
|
13
|
+
|
|
14
|
+
== Current Path
|
|
15
|
+
|
|
16
|
+
=== General
|
|
17
|
+
|
|
18
|
+
The current path is a sequence of connected and disconnected geometric shapes
|
|
19
|
+
that define the outline of an object. The path is not visible until you apply
|
|
20
|
+
a painting operation like `stroke` or `fill`.
|
|
21
|
+
|
|
22
|
+
A path consists of one or more subpaths, where each subpath is either open or
|
|
23
|
+
closed.
|
|
24
|
+
|
|
25
|
+
=== Path construction
|
|
26
|
+
|
|
27
|
+
Paths are built incrementally using path construction operators:
|
|
28
|
+
|
|
29
|
+
[source,postscript]
|
|
30
|
+
----
|
|
31
|
+
newpath % Initialize empty path
|
|
32
|
+
10 10 moveto % Begin new subpath at (10, 10)
|
|
33
|
+
90 10 lineto % Add line segment to (90, 10)
|
|
34
|
+
90 90 lineto % Add line segment to (90, 90)
|
|
35
|
+
10 90 lineto % Add line segment to (10, 90)
|
|
36
|
+
closepath % Close subpath back to start
|
|
37
|
+
----
|
|
38
|
+
|
|
39
|
+
This creates a rectangular path with four line segments.
|
|
40
|
+
|
|
41
|
+
=== Path segments
|
|
42
|
+
|
|
43
|
+
Straight lines:: Created with `moveto` and `lineto`
|
|
44
|
+
Curves:: Created with `curveto` using Bézier curves
|
|
45
|
+
Arcs:: Created with `arc` and `arcn` for circular segments
|
|
46
|
+
|
|
47
|
+
[example]
|
|
48
|
+
====
|
|
49
|
+
[source,postscript]
|
|
50
|
+
----
|
|
51
|
+
newpath
|
|
52
|
+
50 50 moveto % Start point
|
|
53
|
+
50 150 curveto % Control point 1
|
|
54
|
+
150 150 curveto % Control point 2
|
|
55
|
+
150 50 lineto % End point: curved then straight
|
|
56
|
+
----
|
|
57
|
+
|
|
58
|
+
This creates a path with both curved and straight segments.
|
|
59
|
+
====
|
|
60
|
+
|
|
61
|
+
=== Subpaths
|
|
62
|
+
|
|
63
|
+
A path can contain multiple subpaths, each started with `moveto`:
|
|
64
|
+
|
|
65
|
+
[example]
|
|
66
|
+
====
|
|
67
|
+
[source,postscript]
|
|
68
|
+
----
|
|
69
|
+
newpath
|
|
70
|
+
% First subpath: outer rectangle
|
|
71
|
+
0 0 moveto
|
|
72
|
+
100 0 lineto
|
|
73
|
+
100 100 lineto
|
|
74
|
+
0 100 lineto
|
|
75
|
+
closepath
|
|
76
|
+
|
|
77
|
+
% Second subpath: inner rectangle (hole)
|
|
78
|
+
25 25 moveto
|
|
79
|
+
75 25 lineto
|
|
80
|
+
75 75 lineto
|
|
81
|
+
25 75 lineto
|
|
82
|
+
closepath
|
|
83
|
+
|
|
84
|
+
fill % Fills outer, creates hole with inner
|
|
85
|
+
----
|
|
86
|
+
|
|
87
|
+
Multiple subpaths enable complex shapes with holes.
|
|
88
|
+
====
|
|
89
|
+
|
|
90
|
+
== Graphics State
|
|
91
|
+
|
|
92
|
+
=== General
|
|
93
|
+
|
|
94
|
+
The graphics state is a collection of parameters that control how graphics
|
|
95
|
+
operations are performed. It includes colors, line attributes, clipping paths,
|
|
96
|
+
and transformation matrices.
|
|
97
|
+
|
|
98
|
+
The graphics state can be saved and restored using `gsave` and `grestore`,
|
|
99
|
+
enabling temporary changes without losing the previous state.
|
|
100
|
+
|
|
101
|
+
=== Graphics state parameters
|
|
102
|
+
|
|
103
|
+
==== Current transformation matrix (CTM)
|
|
104
|
+
|
|
105
|
+
A 3×2 matrix that maps user space coordinates to device space:
|
|
106
|
+
|
|
107
|
+
[source]
|
|
108
|
+
----
|
|
109
|
+
[ a b 0 ]
|
|
110
|
+
[ c d 0 ]
|
|
111
|
+
[ e f 1 ]
|
|
112
|
+
----
|
|
113
|
+
|
|
114
|
+
Where `[a b c d e f]` represents the transformation.
|
|
115
|
+
|
|
116
|
+
==== Color parameters
|
|
117
|
+
|
|
118
|
+
Fill color:: Color used for `fill` operations
|
|
119
|
+
Stroke color:: Color used for `stroke` operations
|
|
120
|
+
|
|
121
|
+
Both are set with color operators like `setrgbcolor` or `setgray`.
|
|
122
|
+
|
|
123
|
+
==== Line parameters
|
|
124
|
+
|
|
125
|
+
Line width:: Thickness of stroked lines (set with `setlinewidth`)
|
|
126
|
+
Line cap:: Shape of line ends (set with `setlinecap`)
|
|
127
|
+
Line join:: Shape of line corners (set with `setlinejoin`)
|
|
128
|
+
Miter limit:: Limit for miter joins (set with `setmiterlimit`)
|
|
129
|
+
Dash pattern:: Pattern for dashed lines (set with `setdash`)
|
|
130
|
+
|
|
131
|
+
==== Clipping path
|
|
132
|
+
|
|
133
|
+
The clipping path restricts the visible region. Only graphics within the
|
|
134
|
+
clipping path are rendered.
|
|
135
|
+
|
|
136
|
+
=== Graphics state stack
|
|
137
|
+
|
|
138
|
+
[example]
|
|
139
|
+
====
|
|
140
|
+
[source,postscript]
|
|
141
|
+
----
|
|
142
|
+
1 setlinewidth % Set line width to 1
|
|
143
|
+
0 0 0 setrgbcolor % Set color to black
|
|
144
|
+
|
|
145
|
+
gsave % Save current state
|
|
146
|
+
5 setlinewidth % Change to width 5
|
|
147
|
+
1 0 0 setrgbcolor % Change to red
|
|
148
|
+
% Draw with thick red lines
|
|
149
|
+
grestore % Restore: back to width 1, black
|
|
150
|
+
|
|
151
|
+
% Now drawing with original settings
|
|
152
|
+
----
|
|
153
|
+
|
|
154
|
+
The `gsave`/`grestore` pair maintains state isolation.
|
|
155
|
+
====
|
|
156
|
+
|
|
157
|
+
== Painting Model
|
|
158
|
+
|
|
159
|
+
=== General
|
|
160
|
+
|
|
161
|
+
Painting operations convert the current path into visible marks on the page.
|
|
162
|
+
The two primary operations are stroking and filling.
|
|
163
|
+
|
|
164
|
+
[[stroke-operation]]
|
|
165
|
+
=== Stroking
|
|
166
|
+
|
|
167
|
+
The `stroke` operator paints a line along the current path using the current
|
|
168
|
+
stroke color and line attributes.
|
|
169
|
+
|
|
170
|
+
[source,postscript]
|
|
171
|
+
----
|
|
172
|
+
newpath
|
|
173
|
+
10 10 moveto
|
|
174
|
+
90 90 lineto
|
|
175
|
+
2 setlinewidth % Set line width
|
|
176
|
+
1 0 0 setrgbcolor % Set color to red
|
|
177
|
+
stroke % Paint the line
|
|
178
|
+
----
|
|
179
|
+
|
|
180
|
+
Stroking uses:
|
|
181
|
+
|
|
182
|
+
* Stroke color
|
|
183
|
+
* Line width
|
|
184
|
+
* Line cap style
|
|
185
|
+
* Line join style
|
|
186
|
+
* Dash pattern
|
|
187
|
+
|
|
188
|
+
[[fill-operation]]
|
|
189
|
+
=== Filling
|
|
190
|
+
|
|
191
|
+
The `fill` operator fills the interior of the current path with the current
|
|
192
|
+
fill color.
|
|
193
|
+
|
|
194
|
+
[source,postscript]
|
|
195
|
+
----
|
|
196
|
+
newpath
|
|
197
|
+
50 50 moveto
|
|
198
|
+
150 50 lineto
|
|
199
|
+
100 150 lineto
|
|
200
|
+
closepath
|
|
201
|
+
0 0 1 setrgbcolor % Set color to blue
|
|
202
|
+
fill % Fill the triangle
|
|
203
|
+
----
|
|
204
|
+
|
|
205
|
+
=== Fill rules
|
|
206
|
+
|
|
207
|
+
PostScript supports two fill rules for determining what is "inside" a path:
|
|
208
|
+
|
|
209
|
+
Non-zero winding rule:: Default rule, used by `fill`
|
|
210
|
+
Even-odd rule:: Alternative rule, used by `eofill`
|
|
211
|
+
|
|
212
|
+
[example]
|
|
213
|
+
====
|
|
214
|
+
[source,postscript]
|
|
215
|
+
----
|
|
216
|
+
% Two overlapping squares
|
|
217
|
+
newpath
|
|
218
|
+
0 0 moveto 100 0 lineto 100 100 lineto 0 100 lineto closepath
|
|
219
|
+
50 50 moveto 150 50 lineto 150 150 lineto 50 150 lineto closepath
|
|
220
|
+
|
|
221
|
+
fill % Uses non-zero winding: both squares filled
|
|
222
|
+
% OR
|
|
223
|
+
eofill % Uses even-odd: overlap creates hole
|
|
224
|
+
----
|
|
225
|
+
|
|
226
|
+
The even-odd rule treats overlapping regions differently than the non-zero
|
|
227
|
+
winding rule.
|
|
228
|
+
====
|
|
229
|
+
|
|
230
|
+
=== Clipping
|
|
231
|
+
|
|
232
|
+
The `clip` operator establishes a clipping path that restricts subsequent
|
|
233
|
+
drawing operations.
|
|
234
|
+
|
|
235
|
+
[example]
|
|
236
|
+
====
|
|
237
|
+
[source,postscript]
|
|
238
|
+
----
|
|
239
|
+
gsave
|
|
240
|
+
% Define clipping region
|
|
241
|
+
newpath
|
|
242
|
+
50 50 100 0 360 arc % Circle
|
|
243
|
+
clip % Establish as clip path
|
|
244
|
+
|
|
245
|
+
% Draw something - only visible within circle
|
|
246
|
+
newpath
|
|
247
|
+
0 0 moveto
|
|
248
|
+
200 200 lineto
|
|
249
|
+
stroke
|
|
250
|
+
grestore % Remove clipping restriction
|
|
251
|
+
----
|
|
252
|
+
|
|
253
|
+
Clipping is useful for masking graphics to specific regions.
|
|
254
|
+
====
|
|
255
|
+
|
|
256
|
+
== Coordinate Transformations
|
|
257
|
+
|
|
258
|
+
=== General
|
|
259
|
+
|
|
260
|
+
Coordinate transformations modify the current transformation matrix (CTM),
|
|
261
|
+
affecting how coordinates are interpreted in subsequent operations.
|
|
262
|
+
|
|
263
|
+
=== Transformation types
|
|
264
|
+
|
|
265
|
+
[[translation]]
|
|
266
|
+
==== Translation
|
|
267
|
+
|
|
268
|
+
Move the origin to a new location:
|
|
269
|
+
|
|
270
|
+
[source,postscript]
|
|
271
|
+
----
|
|
272
|
+
100 50 translate % Move origin to (100, 50)
|
|
273
|
+
0 0 moveto % Now at (100, 50) in original space
|
|
274
|
+
----
|
|
275
|
+
|
|
276
|
+
[[scaling]]
|
|
277
|
+
==== Scaling
|
|
278
|
+
|
|
279
|
+
Scale the coordinate system:
|
|
280
|
+
|
|
281
|
+
[source,postscript]
|
|
282
|
+
----
|
|
283
|
+
2 2 scale % Double all dimensions
|
|
284
|
+
50 50 moveto % Actually at (100, 100)
|
|
285
|
+
25 0 rlineto % Draws line of length 50
|
|
286
|
+
----
|
|
287
|
+
|
|
288
|
+
[[rotation]]
|
|
289
|
+
==== Rotation
|
|
290
|
+
|
|
291
|
+
Rotate the coordinate system:
|
|
292
|
+
|
|
293
|
+
[source,postscript]
|
|
294
|
+
----
|
|
295
|
+
45 rotate % Rotate 45 degrees counterclockwise
|
|
296
|
+
100 0 moveto % Moves at 45-degree angle
|
|
297
|
+
----
|
|
298
|
+
|
|
299
|
+
=== Transformation order
|
|
300
|
+
|
|
301
|
+
Transformations are cumulative and apply in the order specified:
|
|
302
|
+
|
|
303
|
+
[example]
|
|
304
|
+
====
|
|
305
|
+
[source,postscript]
|
|
306
|
+
----
|
|
307
|
+
gsave
|
|
308
|
+
100 100 translate % Step 1: Move origin to (100, 100)
|
|
309
|
+
45 rotate % Step 2: Rotate around new origin
|
|
310
|
+
2 2 scale % Step 3: Scale in rotated space
|
|
311
|
+
|
|
312
|
+
% Draw a square - it will be:
|
|
313
|
+
% - Doubled in size (scale)
|
|
314
|
+
% - Rotated 45 degrees (rotate)
|
|
315
|
+
% - Positioned at (100, 100) (translate)
|
|
316
|
+
newpath
|
|
317
|
+
0 0 moveto
|
|
318
|
+
50 0 lineto
|
|
319
|
+
50 50 lineto
|
|
320
|
+
0 50 lineto
|
|
321
|
+
closepath
|
|
322
|
+
stroke
|
|
323
|
+
grestore
|
|
324
|
+
----
|
|
325
|
+
|
|
326
|
+
The order matters: translating then rotating gives different results than
|
|
327
|
+
rotating then translating.
|
|
328
|
+
====
|
|
329
|
+
|
|
330
|
+
=== Transformation matrices
|
|
331
|
+
|
|
332
|
+
Advanced users can manipulate the CTM directly using matrix operators:
|
|
333
|
+
|
|
334
|
+
[source,postscript]
|
|
335
|
+
----
|
|
336
|
+
[a b c d e f] concat % Concatenate matrix to CTM
|
|
337
|
+
matrix currentmatrix % Get current CTM
|
|
338
|
+
[...] setmatrix % Set CTM directly
|
|
339
|
+
----
|
|
340
|
+
|
|
341
|
+
== Device Independence
|
|
342
|
+
|
|
343
|
+
=== General
|
|
344
|
+
|
|
345
|
+
PostScript's graphics model is device-independent, meaning the same PostScript
|
|
346
|
+
code produces consistent output on different devices (printers, displays).
|
|
347
|
+
|
|
348
|
+
The CTM handles conversion from user space (coordinates in your program) to
|
|
349
|
+
device space (pixels or dots on the output device).
|
|
350
|
+
|
|
351
|
+
=== Resolution independence
|
|
352
|
+
|
|
353
|
+
[example]
|
|
354
|
+
====
|
|
355
|
+
A circle with radius 50 points:
|
|
356
|
+
|
|
357
|
+
[source,postscript]
|
|
358
|
+
----
|
|
359
|
+
50 50 50 0 360 arc
|
|
360
|
+
----
|
|
361
|
+
|
|
362
|
+
This produces:
|
|
363
|
+
|
|
364
|
+
* On 72 dpi screen: circle with ~50 pixel radius
|
|
365
|
+
* On 300 dpi printer: circle with ~208 pixel radius
|
|
366
|
+
* On 1200 dpi imagesetter: circle with ~833 pixel radius
|
|
367
|
+
|
|
368
|
+
The physical size (approximately 0.69 inches diameter) remains constant.
|
|
369
|
+
====
|
|
370
|
+
|
|
371
|
+
== Paint Order
|
|
372
|
+
|
|
373
|
+
=== General
|
|
374
|
+
|
|
375
|
+
PostScript follows a painter's algorithm: later operations paint over earlier
|
|
376
|
+
ones.
|
|
377
|
+
|
|
378
|
+
[example]
|
|
379
|
+
====
|
|
380
|
+
[source,postscript]
|
|
381
|
+
----
|
|
382
|
+
% Draw red square
|
|
383
|
+
newpath
|
|
384
|
+
0 0 moveto 100 0 lineto 100 100 lineto 0 100 lineto closepath
|
|
385
|
+
1 0 0 setrgbcolor
|
|
386
|
+
fill
|
|
387
|
+
|
|
388
|
+
% Draw blue circle - will overlap red square
|
|
389
|
+
newpath
|
|
390
|
+
50 50 30 0 360 arc
|
|
391
|
+
0 0 1 setrgbcolor
|
|
392
|
+
fill
|
|
393
|
+
----
|
|
394
|
+
|
|
395
|
+
The blue circle appears on top of the red square because it was drawn later.
|
|
396
|
+
====
|
|
397
|
+
|
|
398
|
+
== See Also
|
|
399
|
+
|
|
400
|
+
* link:fundamentals.adoc[Language Fundamentals] - PostScript language basics
|
|
401
|
+
* link:operators/path-construction.adoc[Path Construction] - Building paths
|
|
402
|
+
* link:operators/painting.adoc[Painting Operators] - Stroking and filling
|
|
403
|
+
* link:operators/graphics-state.adoc[Graphics State] - State management
|
|
404
|
+
* link:operators/transformations.adoc[Transformations] - Coordinate transforms
|
|
405
|
+
* link:svg-mapping.adoc[SVG Mapping] - How PostScript maps to SVG
|
|
406
|
+
* link:index.adoc[Back to PostScript Quick Reference]
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
= Postsvg Implementation Notes
|
|
2
|
+
|
|
3
|
+
== General
|
|
4
|
+
|
|
5
|
+
This document describes implementation-specific details of the Postsvg library,
|
|
6
|
+
including supported features, current limitations, architecture overview, and
|
|
7
|
+
testing approach.
|
|
8
|
+
|
|
9
|
+
== Supported Features
|
|
10
|
+
|
|
11
|
+
=== Fully implemented operators
|
|
12
|
+
|
|
13
|
+
==== Path construction
|
|
14
|
+
|
|
15
|
+
* `newpath` - Initialize empty path
|
|
16
|
+
* `moveto` - Begin new subpath
|
|
17
|
+
* `lineto` - Append straight line
|
|
18
|
+
* `curveto` - Append cubic Bézier curve
|
|
19
|
+
* `closepath` - Close current subpath
|
|
20
|
+
|
|
21
|
+
==== Painting
|
|
22
|
+
|
|
23
|
+
* `stroke` - Stroke current path
|
|
24
|
+
* `fill` - Fill current path with non-zero winding rule
|
|
25
|
+
|
|
26
|
+
==== Graphics state
|
|
27
|
+
|
|
28
|
+
* `gsave` - Save graphics state
|
|
29
|
+
* `grestore` - Restore graphics state
|
|
30
|
+
* `setlinewidth` - Set line width
|
|
31
|
+
* `setrgbcolor` - Set RGB color
|
|
32
|
+
* `setgray` - Set grayscale color
|
|
33
|
+
|
|
34
|
+
==== Transformations
|
|
35
|
+
|
|
36
|
+
* `translate` - Translate coordinate system
|
|
37
|
+
* `scale` - Scale coordinate system
|
|
38
|
+
* `rotate` - Rotate coordinate system
|
|
39
|
+
|
|
40
|
+
==== Stack and arithmetic
|
|
41
|
+
|
|
42
|
+
* Basic stack operations: `dup`, `pop`, `exch`
|
|
43
|
+
* Arithmetic operators: `add`, `sub`, `mul`, `div`
|
|
44
|
+
|
|
45
|
+
=== Partially implemented
|
|
46
|
+
|
|
47
|
+
==== Path construction
|
|
48
|
+
|
|
49
|
+
* `arc` - Basic circular arc support (counterclockwise)
|
|
50
|
+
* `arcn` - Basic circular arc support (clockwise)
|
|
51
|
+
|
|
52
|
+
Limited to simple cases; complex arc operations may not work correctly.
|
|
53
|
+
|
|
54
|
+
==== Control flow
|
|
55
|
+
|
|
56
|
+
* `if`, `ifelse` - Basic conditionals
|
|
57
|
+
* `for`, `repeat` - Basic loops
|
|
58
|
+
|
|
59
|
+
Limited support for procedures; complex control structures may fail.
|
|
60
|
+
|
|
61
|
+
== Current Limitations
|
|
62
|
+
|
|
63
|
+
=== Not yet implemented
|
|
64
|
+
|
|
65
|
+
==== Text operations
|
|
66
|
+
|
|
67
|
+
* `show` - Show text string
|
|
68
|
+
* `findfont` - Find font
|
|
69
|
+
* `setfont` - Set current font
|
|
70
|
+
* `scalefont` - Scale font
|
|
71
|
+
|
|
72
|
+
Text in PostScript files is ignored.
|
|
73
|
+
|
|
74
|
+
==== Image operations
|
|
75
|
+
|
|
76
|
+
* `image` - Paint sampled image
|
|
77
|
+
* `imagemask` - Paint image mask
|
|
78
|
+
|
|
79
|
+
Images are not supported.
|
|
80
|
+
|
|
81
|
+
==== Pattern and gradient operations
|
|
82
|
+
|
|
83
|
+
* `makepattern` - Create pattern
|
|
84
|
+
* `setpattern` - Set pattern as color
|
|
85
|
+
|
|
86
|
+
Patterns and gradients are not supported.
|
|
87
|
+
|
|
88
|
+
==== Advanced graphics state
|
|
89
|
+
|
|
90
|
+
* `setlinecap` - Set line cap style
|
|
91
|
+
* `setlinejoin` - Set line join style
|
|
92
|
+
* `setdash` - Set dash pattern
|
|
93
|
+
* `clip`, `eoclip` - Clipping paths
|
|
94
|
+
|
|
95
|
+
These operators are recognized but may not affect output.
|
|
96
|
+
|
|
97
|
+
==== Dictionary and scope
|
|
98
|
+
|
|
99
|
+
* Limited dictionary support
|
|
100
|
+
* Procedure definitions work only in simple cases
|
|
101
|
+
|
|
102
|
+
=== Known issues
|
|
103
|
+
|
|
104
|
+
==== Arc conversion
|
|
105
|
+
|
|
106
|
+
Arcs are converted to line segments rather than SVG arc commands in some cases,
|
|
107
|
+
which may increase output size.
|
|
108
|
+
|
|
109
|
+
==== Transformation accuracy
|
|
110
|
+
|
|
111
|
+
Complex transformation sequences may accumulate floating-point errors.
|
|
112
|
+
|
|
113
|
+
==== Color precision
|
|
114
|
+
|
|
115
|
+
Colors are converted to 8-bit RGB hex values, potentially losing precision from
|
|
116
|
+
PostScript's floating-point representation.
|
|
117
|
+
|
|
118
|
+
== Architecture Overview
|
|
119
|
+
|
|
120
|
+
=== Component diagram
|
|
121
|
+
|
|
122
|
+
[source]
|
|
123
|
+
----
|
|
124
|
+
┌────────────────────────────────────────┐
|
|
125
|
+
│ Postsvg Module │
|
|
126
|
+
│ │
|
|
127
|
+
│ convert(ps_content) → svg_string │
|
|
128
|
+
│ convert_file(input, output) │
|
|
129
|
+
└────────────┬───────────────────────────┘
|
|
130
|
+
│
|
|
131
|
+
┌───────┴────────┐
|
|
132
|
+
│ │
|
|
133
|
+
┌────▼─────┐ ┌─────▼────────┐
|
|
134
|
+
│ Parser │ │ Converter │
|
|
135
|
+
│ │ │ │
|
|
136
|
+
│ Parslet │───>│ Interpreter │
|
|
137
|
+
│ Grammar │ │ │
|
|
138
|
+
└──────────┘ └──────┬───────┘
|
|
139
|
+
│
|
|
140
|
+
┌────────┴────────┐
|
|
141
|
+
│ │
|
|
142
|
+
┌─────▼──────┐ ┌──────▼────────┐
|
|
143
|
+
│ Graphics │ │ SVG │
|
|
144
|
+
│ State │──>│ Generator │
|
|
145
|
+
└────────────┘ └───────────────┘
|
|
146
|
+
----
|
|
147
|
+
|
|
148
|
+
=== Parser (Parslet-based)
|
|
149
|
+
|
|
150
|
+
Location:: `lib/postsvg/parser/postscript_parser.rb`
|
|
151
|
+
Purpose:: Tokenizes and parses PostScript syntax
|
|
152
|
+
|
|
153
|
+
The parser uses Parslet, a PEG (Parsing Expression Grammar) library, to build
|
|
154
|
+
an abstract syntax tree from PostScript source code.
|
|
155
|
+
|
|
156
|
+
=== Converter (Interpreter)
|
|
157
|
+
|
|
158
|
+
Location:: `lib/postsvg/converter.rb`
|
|
159
|
+
Purpose:: Interprets PostScript commands
|
|
160
|
+
|
|
161
|
+
The converter maintains:
|
|
162
|
+
|
|
163
|
+
* Operand stack for PostScript execution
|
|
164
|
+
* Dictionary for variable storage
|
|
165
|
+
* Graphics state object
|
|
166
|
+
|
|
167
|
+
=== Graphics State
|
|
168
|
+
|
|
169
|
+
Location:: `lib/postsvg/graphics_state.rb`
|
|
170
|
+
Purpose:: Tracks graphics parameters
|
|
171
|
+
|
|
172
|
+
Maintains:
|
|
173
|
+
|
|
174
|
+
* Current path segments
|
|
175
|
+
* Fill and stroke colors
|
|
176
|
+
* Line width
|
|
177
|
+
* Transformation matrix
|
|
178
|
+
* Graphics state stack
|
|
179
|
+
|
|
180
|
+
=== SVG Generator
|
|
181
|
+
|
|
182
|
+
Location:: `lib/postsvg/svg_generator.rb`
|
|
183
|
+
Purpose:: Generates SVG output
|
|
184
|
+
|
|
185
|
+
Converts the interpreted graphics operations into SVG XML format.
|
|
186
|
+
|
|
187
|
+
== Testing Approach
|
|
188
|
+
|
|
189
|
+
=== Test organization
|
|
190
|
+
|
|
191
|
+
[source]
|
|
192
|
+
----
|
|
193
|
+
spec/
|
|
194
|
+
├── postsvg_spec.rb # Integration tests
|
|
195
|
+
├── fixtures/
|
|
196
|
+
│ ├── ps2svg/ # Reference files from ps2svg
|
|
197
|
+
│ │ ├── colors.ps
|
|
198
|
+
│ │ ├── colors_expected.svg
|
|
199
|
+
│ │ └── ...
|
|
200
|
+
│ └── eps2svg/ # Reference files from vectory
|
|
201
|
+
│ ├── img.eps
|
|
202
|
+
│ └── ref.svg
|
|
203
|
+
└── postsvg/
|
|
204
|
+
├── parser_spec.rb # Parser unit tests
|
|
205
|
+
├── graphics_state_spec.rb # Graphics state tests
|
|
206
|
+
├── converter_spec.rb # Converter tests (TBD)
|
|
207
|
+
└── svg_generator_spec.rb # Generator tests (TBD)
|
|
208
|
+
----
|
|
209
|
+
|
|
210
|
+
=== Reference test suites
|
|
211
|
+
|
|
212
|
+
Postsvg uses test fixtures from two sources:
|
|
213
|
+
|
|
214
|
+
1. **ps2svg TypeScript project** - Comprehensive PostScript test cases
|
|
215
|
+
2. **vectory gem** - Real-world EPS/PS files
|
|
216
|
+
|
|
217
|
+
Tests verify that Postsvg produces equivalent SVG output to these reference
|
|
218
|
+
implementations.
|
|
219
|
+
|
|
220
|
+
== Usage Patterns
|
|
221
|
+
|
|
222
|
+
=== Basic conversion
|
|
223
|
+
|
|
224
|
+
[source,ruby]
|
|
225
|
+
----
|
|
226
|
+
require 'postsvg'
|
|
227
|
+
|
|
228
|
+
# Convert PostScript string to SVG
|
|
229
|
+
ps_content = File.read('input.ps')
|
|
230
|
+
svg_output = Postsvg.convert(ps_content)
|
|
231
|
+
|
|
232
|
+
# Save result
|
|
233
|
+
File.write('output.svg', svg_output)
|
|
234
|
+
----
|
|
235
|
+
|
|
236
|
+
=== File conversion
|
|
237
|
+
|
|
238
|
+
[source,ruby]
|
|
239
|
+
----
|
|
240
|
+
# Convert file directly
|
|
241
|
+
Postsvg.convert_file('input.eps', 'output.svg')
|
|
242
|
+
|
|
243
|
+
# Or get SVG without saving
|
|
244
|
+
svg = Postsvg.convert_file('input.ps')
|
|
245
|
+
----
|
|
246
|
+
|
|
247
|
+
=== Error handling
|
|
248
|
+
|
|
249
|
+
[source,ruby]
|
|
250
|
+
----
|
|
251
|
+
begin
|
|
252
|
+
svg = Postsvg.convert(ps_content)
|
|
253
|
+
rescue Postsvg::ParseError => e
|
|
254
|
+
puts "Parse error: #{e.message}"
|
|
255
|
+
rescue Postsvg::ConversionError => e
|
|
256
|
+
puts "Conversion error: #{e.message}"
|
|
257
|
+
end
|
|
258
|
+
----
|
|
259
|
+
|
|
260
|
+
== Performance Considerations
|
|
261
|
+
|
|
262
|
+
=== Memory usage
|
|
263
|
+
|
|
264
|
+
The converter maintains the entire graphics state in memory. Large PostScript
|
|
265
|
+
files with many path operations may consume significant memory.
|
|
266
|
+
|
|
267
|
+
=== Processing speed
|
|
268
|
+
|
|
269
|
+
Parslet-based parsing is slower than hand-written parsers but provides better
|
|
270
|
+
error handling and maintainability.
|
|
271
|
+
|
|
272
|
+
For batch conversion of many files, consider parallel processing.
|
|
273
|
+
|
|
274
|
+
=== Output size
|
|
275
|
+
|
|
276
|
+
SVG output may be larger than the original PostScript for files with:
|
|
277
|
+
|
|
278
|
+
* Many small line segments
|
|
279
|
+
* Complex curves converted to multiple segments
|
|
280
|
+
* Repeated path operations
|
|
281
|
+
|
|
282
|
+
== Future Enhancements
|
|
283
|
+
|
|
284
|
+
=== Planned features
|
|
285
|
+
|
|
286
|
+
* Complete arc implementation using SVG arc commands
|
|
287
|
+
* Text rendering support
|
|
288
|
+
* Clipping path support
|
|
289
|
+
* Dash pattern support
|
|
290
|
+
* Better procedure handling
|
|
291
|
+
|
|
292
|
+
=== Architectural improvements
|
|
293
|
+
|
|
294
|
+
* Incremental SVG generation to reduce memory usage
|
|
295
|
+
* Optional path simplification
|
|
296
|
+
* Caching of repeated patterns
|
|
297
|
+
* Better error recovery
|
|
298
|
+
|
|
299
|
+
== Contributing
|
|
300
|
+
|
|
301
|
+
When contributing to Postsvg:
|
|
302
|
+
|
|
303
|
+
1. Follow Ribose Ruby coding standards
|
|
304
|
+
2. Add tests for new operators
|
|
305
|
+
3. Update this documentation
|
|
306
|
+
4. Ensure all tests pass: `bundle exec rspec`
|
|
307
|
+
5. Run linter: `bundle exec rubocop`
|
|
308
|
+
|
|
309
|
+
== See Also
|
|
310
|
+
|
|
311
|
+
* link:fundamentals.adoc[PostScript Fundamentals]
|
|
312
|
+
* link:svg-mapping.adoc[SVG Mapping]
|
|
313
|
+
* link:../../README.adoc[Postsvg README]
|
|
314
|
+
* link:index.adoc[Back to PostScript Quick Reference]
|