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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +105 -0
- data/CLAUDE.md +173 -0
- data/Gemfile +2 -3
- data/README.adoc +456 -179
- data/Rakefile +100 -0
- data/TODO.roadmap/00-architecture.md +139 -0
- data/TODO.roadmap/01-autoload-migration.md +39 -0
- data/TODO.roadmap/02-isolate-dormant-code.md +51 -0
- data/TODO.roadmap/03-domain-model.md +66 -0
- data/TODO.roadmap/04-lexer.md +40 -0
- data/TODO.roadmap/05-parser.md +43 -0
- data/TODO.roadmap/06-graphics-state.md +45 -0
- data/TODO.roadmap/07-matrix-and-color.md +37 -0
- data/TODO.roadmap/08-svg-builder.md +51 -0
- data/TODO.roadmap/09-renderer.md +52 -0
- data/TODO.roadmap/10-visitor.md +58 -0
- data/TODO.roadmap/11-operator-coverage.md +103 -0
- data/TODO.roadmap/12-svg-domain.md +62 -0
- data/TODO.roadmap/13-translation-handlers.md +69 -0
- data/TODO.roadmap/14-ps-serializer.md +49 -0
- data/TODO.roadmap/15-cli-and-public-api.md +47 -0
- data/TODO.roadmap/16-specs.md +84 -0
- data/TODO.roadmap/17-docs-sync.md +47 -0
- data/TODO.roadmap/18-performance-and-determinism.md +47 -0
- data/TODO.roadmap/19-error-model.md +45 -0
- data/TODO.roadmap/20-font-and-text.md +46 -0
- data/TODO.roadmap/21-images.md +45 -0
- data/TODO.roadmap/22-forms-and-resources.md +25 -0
- data/TODO.roadmap/23-level2-level3.md +52 -0
- data/TODO.roadmap/24-ci-and-release.md +42 -0
- data/TODO.roadmap/README.md +77 -0
- data/docs/.gitignore +29 -0
- data/docs/CHANGELOG.md +114 -0
- data/docs/COMPLETE_DOCUMENTATION_STATUS.md +376 -0
- data/docs/DEPLOYMENT.md +456 -0
- data/docs/DEPLOYMENT_INSTRUCTIONS.md +229 -0
- data/docs/DOCUMENTATION_PLAN.md +425 -0
- data/docs/FINAL_SUMMARY.md +657 -0
- data/docs/Gemfile +15 -0
- data/docs/README.md +327 -0
- data/docs/_config.yml +99 -0
- data/docs/advanced-topics.adoc +370 -0
- data/docs/api-reference/colors.adoc +705 -0
- data/docs/api-reference/converter.adoc +699 -0
- data/docs/api-reference/execution-context.adoc +1210 -0
- data/docs/api-reference/graphics-state.adoc +1070 -0
- data/docs/api-reference/interpreter.adoc +810 -0
- data/docs/api-reference/matrix.adoc +1179 -0
- data/docs/api-reference/path-builder.adoc +1284 -0
- data/docs/api-reference/postsvg-module.adoc +388 -0
- data/docs/api-reference/svg-generator.adoc +891 -0
- data/docs/api-reference/tokenizer.adoc +925 -0
- data/docs/api-reference.adoc +221 -0
- data/docs/architecture/command-registry.adoc +1191 -0
- data/docs/architecture/conversion-pipeline.adoc +746 -0
- data/docs/architecture/design-decisions.adoc +999 -0
- data/docs/architecture/generator-stage.adoc +1115 -0
- data/docs/architecture/graphics-state-model.adoc +1089 -0
- data/docs/architecture/interpreter-stage.adoc +1125 -0
- data/docs/architecture/parser-stage.adoc +1051 -0
- data/docs/architecture.adoc +354 -0
- data/docs/cli-reference/batch-command.adoc +616 -0
- data/docs/cli-reference/check-command.adoc +677 -0
- data/docs/cli-reference/cli-options.adoc +802 -0
- data/docs/cli-reference/convert-command.adoc +462 -0
- data/docs/cli-reference/version-command.adoc +296 -0
- data/docs/cli-reference.adoc +317 -0
- data/docs/concepts/conversion-pipeline.adoc +903 -0
- data/docs/concepts/coordinate-systems.adoc +836 -0
- data/docs/concepts/graphics-state.adoc +861 -0
- data/docs/concepts/path-operations.adoc +1076 -0
- data/docs/concepts/postscript-language.adoc +859 -0
- data/docs/concepts/svg-generation.adoc +937 -0
- data/docs/concepts.adoc +198 -0
- data/docs/contributing.adoc +443 -0
- data/docs/development.adoc +420 -0
- data/docs/faq.adoc +493 -0
- data/docs/getting-started/basic-usage.adoc +538 -0
- data/docs/getting-started/common-workflows.adoc +577 -0
- data/docs/getting-started/first-conversion.adoc +492 -0
- data/docs/getting-started/installation.adoc +534 -0
- data/docs/getting-started.adoc +94 -0
- data/docs/index.adoc +248 -0
- data/docs/optimization.adoc +196 -0
- data/docs/ps2svg_compatibility.adoc +149 -0
- data/docs/quick-reference.adoc +453 -0
- data/docs/sitemap.adoc +337 -0
- data/docs/troubleshooting.adoc +486 -0
- data/docs/validation.adoc +772 -0
- data/exe/postsvg +1 -0
- data/lib/postsvg/cli.rb +104 -57
- data/lib/postsvg/color.rb +132 -0
- data/lib/postsvg/errors.rb +68 -3
- data/lib/postsvg/format_number.rb +22 -0
- data/lib/postsvg/graphics_context.rb +80 -0
- data/lib/postsvg/graphics_stack.rb +43 -0
- data/lib/postsvg/model/literals/array.rb +41 -0
- data/lib/postsvg/model/literals/dictionary.rb +34 -0
- data/lib/postsvg/model/literals/hex.rb +37 -0
- data/lib/postsvg/model/literals/name.rb +40 -0
- data/lib/postsvg/model/literals/number.rb +36 -0
- data/lib/postsvg/model/literals/procedure.rb +41 -0
- data/lib/postsvg/model/literals/string.rb +30 -0
- data/lib/postsvg/model/literals.rb +19 -0
- data/lib/postsvg/model/operator.rb +58 -0
- data/lib/postsvg/model/operators/arithmetic.rb +264 -0
- data/lib/postsvg/model/operators/boolean.rb +182 -0
- data/lib/postsvg/model/operators/color.rb +74 -0
- data/lib/postsvg/model/operators/container.rb +186 -0
- data/lib/postsvg/model/operators/control_flow.rb +119 -0
- data/lib/postsvg/model/operators/device.rb +21 -0
- data/lib/postsvg/model/operators/dictionary.rb +118 -0
- data/lib/postsvg/model/operators/font.rb +121 -0
- data/lib/postsvg/model/operators/graphics_state.rb +84 -0
- data/lib/postsvg/model/operators/painting.rb +29 -0
- data/lib/postsvg/model/operators/path.rb +169 -0
- data/lib/postsvg/model/operators/stack.rb +72 -0
- data/lib/postsvg/model/operators/transformations.rb +103 -0
- data/lib/postsvg/model/operators.rb +89 -0
- data/lib/postsvg/model/program.rb +68 -0
- data/lib/postsvg/model/token.rb +43 -0
- data/lib/postsvg/model.rb +17 -0
- data/lib/postsvg/options.rb +29 -0
- data/lib/postsvg/renderer.rb +85 -0
- data/lib/postsvg/serializer.rb +325 -0
- data/lib/postsvg/source/ast_builder.rb +308 -0
- data/lib/postsvg/source/lexer.rb +322 -0
- data/lib/postsvg/source/operand_stack.rb +55 -0
- data/lib/postsvg/source.rb +21 -0
- data/lib/postsvg/svg/attribute_parser.rb +45 -0
- data/lib/postsvg/svg/clip_path_registry.rb +44 -0
- data/lib/postsvg/svg/document.rb +22 -0
- data/lib/postsvg/svg/element.rb +84 -0
- data/lib/postsvg/svg/elements/circle.rb +36 -0
- data/lib/postsvg/svg/elements/clip_path.rb +26 -0
- data/lib/postsvg/svg/elements/defs.rb +24 -0
- data/lib/postsvg/svg/elements/ellipse.rb +38 -0
- data/lib/postsvg/svg/elements/group.rb +37 -0
- data/lib/postsvg/svg/elements/image.rb +35 -0
- data/lib/postsvg/svg/elements/line.rb +36 -0
- data/lib/postsvg/svg/elements/path.rb +32 -0
- data/lib/postsvg/svg/elements/polygon.rb +12 -0
- data/lib/postsvg/svg/elements/polyline.rb +32 -0
- data/lib/postsvg/svg/elements/rect.rb +44 -0
- data/lib/postsvg/svg/elements/svg.rb +39 -0
- data/lib/postsvg/svg/elements/text.rb +42 -0
- data/lib/postsvg/svg/elements.rb +31 -0
- data/lib/postsvg/svg/paint.rb +34 -0
- data/lib/postsvg/svg/parser.rb +39 -0
- data/lib/postsvg/svg/path_data/command.rb +27 -0
- data/lib/postsvg/svg/path_data/parser.rb +82 -0
- data/lib/postsvg/svg/path_data.rb +17 -0
- data/lib/postsvg/svg/stroke.rb +31 -0
- data/lib/postsvg/svg/transform_list.rb +59 -0
- data/lib/postsvg/svg.rb +22 -0
- data/lib/postsvg/svg_builder.rb +249 -0
- data/lib/postsvg/translation/arc_converter.rb +86 -0
- data/lib/postsvg/translation/bounding_box.rb +59 -0
- data/lib/postsvg/translation/context.rb +34 -0
- data/lib/postsvg/translation/handler_registry.rb +38 -0
- data/lib/postsvg/translation/handlers/circle_handler.rb +28 -0
- data/lib/postsvg/translation/handlers/clip_path_handler.rb +13 -0
- data/lib/postsvg/translation/handlers/defs_handler.rb +15 -0
- data/lib/postsvg/translation/handlers/ellipse_handler.rb +31 -0
- data/lib/postsvg/translation/handlers/group_handler.rb +21 -0
- data/lib/postsvg/translation/handlers/image_handler.rb +20 -0
- data/lib/postsvg/translation/handlers/line_handler.rb +23 -0
- data/lib/postsvg/translation/handlers/open_handler.rb +18 -0
- data/lib/postsvg/translation/handlers/path_handler.rb +356 -0
- data/lib/postsvg/translation/handlers/polygon_handler.rb +33 -0
- data/lib/postsvg/translation/handlers/polyline_handler.rb +31 -0
- data/lib/postsvg/translation/handlers/rect_handler.rb +27 -0
- data/lib/postsvg/translation/handlers/shared.rb +110 -0
- data/lib/postsvg/translation/handlers/svg_handler.rb +25 -0
- data/lib/postsvg/translation/handlers/text_handler.rb +56 -0
- data/lib/postsvg/translation/handlers.rb +25 -0
- data/lib/postsvg/translation/ps_renderer.rb +105 -0
- data/lib/postsvg/translation/record_emitter.rb +35 -0
- data/lib/postsvg/translation.rb +17 -0
- data/lib/postsvg/version.rb +1 -1
- data/lib/postsvg/visitors/ps_visitor/arithmetic.rb +125 -0
- data/lib/postsvg/visitors/ps_visitor/boolean.rb +105 -0
- data/lib/postsvg/visitors/ps_visitor/color.rb +53 -0
- data/lib/postsvg/visitors/ps_visitor/common.rb +66 -0
- data/lib/postsvg/visitors/ps_visitor/container.rb +164 -0
- data/lib/postsvg/visitors/ps_visitor/control_flow.rb +110 -0
- data/lib/postsvg/visitors/ps_visitor/device.rb +20 -0
- data/lib/postsvg/visitors/ps_visitor/dictionary.rb +89 -0
- data/lib/postsvg/visitors/ps_visitor/font.rb +93 -0
- data/lib/postsvg/visitors/ps_visitor/graphics_state.rb +55 -0
- data/lib/postsvg/visitors/ps_visitor/painting.rb +90 -0
- data/lib/postsvg/visitors/ps_visitor/path.rb +112 -0
- data/lib/postsvg/visitors/ps_visitor/stack.rb +47 -0
- data/lib/postsvg/visitors/ps_visitor/transformations.rb +101 -0
- data/lib/postsvg/visitors/ps_visitor.rb +208 -0
- data/lib/postsvg/visitors.rb +9 -0
- data/lib/postsvg.rb +93 -59
- data/lychee.toml +86 -0
- metadata +216 -11
- data/postsvg.gemspec +0 -38
|
@@ -0,0 +1,1076 @@
|
|
|
1
|
+
= Path Operations
|
|
2
|
+
:page-nav_order: 4
|
|
3
|
+
:page-parent: Core Concepts
|
|
4
|
+
|
|
5
|
+
== Purpose
|
|
6
|
+
|
|
7
|
+
This document explains path construction and manipulation in Postsvg, covering how PostScript path operators build vector paths and how these paths are painted and converted to SVG. Understanding path operations is fundamental to working with vector graphics in PostScript and SVG.
|
|
8
|
+
|
|
9
|
+
== References
|
|
10
|
+
|
|
11
|
+
* link:../index.adoc[Documentation Home]
|
|
12
|
+
* link:../concepts.adoc[Core Concepts Overview]
|
|
13
|
+
* link:graphics-state.adoc[Graphics State Management]
|
|
14
|
+
* link:coordinate-systems.adoc[Coordinate Systems]
|
|
15
|
+
* link:svg-generation.adoc[SVG Generation]
|
|
16
|
+
|
|
17
|
+
== Concepts
|
|
18
|
+
|
|
19
|
+
**Path**:: A sequence of connected and disconnected line and curve segments that defines a shape.
|
|
20
|
+
|
|
21
|
+
**Current Path**:: The path being constructed, stored in the graphics state until painted or cleared.
|
|
22
|
+
|
|
23
|
+
**Current Point**:: The endpoint of the last path segment, used as the starting point for the next segment.
|
|
24
|
+
|
|
25
|
+
**Subpath**:: A continuous sequence of connected segments within a path; paths can contain multiple subpaths.
|
|
26
|
+
|
|
27
|
+
**Path Painting**:: Rendering a path by stroking (outlining) or filling (interior coloring).
|
|
28
|
+
|
|
29
|
+
== Path Construction Model
|
|
30
|
+
|
|
31
|
+
=== The Path Building Process
|
|
32
|
+
|
|
33
|
+
PostScript builds paths incrementally through a sequence of operators:
|
|
34
|
+
|
|
35
|
+
.Path Construction Flow
|
|
36
|
+
[source]
|
|
37
|
+
----
|
|
38
|
+
┌──────────────────────────────────────────────────────────┐
|
|
39
|
+
│ 1. newpath → Clear current path │
|
|
40
|
+
│ Current path: [] │
|
|
41
|
+
│ Current point: undefined │
|
|
42
|
+
└──────────────────────────────────────────────────────────┘
|
|
43
|
+
↓
|
|
44
|
+
┌──────────────────────────────────────────────────────────┐
|
|
45
|
+
│ 2. moveto → Start new subpath │
|
|
46
|
+
│ Current path: [(M x,y)] │
|
|
47
|
+
│ Current point: (x, y) │
|
|
48
|
+
└──────────────────────────────────────────────────────────┘
|
|
49
|
+
↓
|
|
50
|
+
┌──────────────────────────────────────────────────────────┐
|
|
51
|
+
│ 3. lineto/curveto → Add segments │
|
|
52
|
+
│ Current path: [(M x1,y1)(L x2,y2)(L x3,y3)] │
|
|
53
|
+
│ Current point: (x3, y3) │
|
|
54
|
+
└──────────────────────────────────────────────────────────┘
|
|
55
|
+
↓
|
|
56
|
+
┌──────────────────────────────────────────────────────────┐
|
|
57
|
+
│ 4. closepath → Close subpath (optional) │
|
|
58
|
+
│ Current path: [(M x1,y1)(L x2,y2)(L x3,y3)(Z)] │
|
|
59
|
+
│ Current point: (x1, y1) - back to start │
|
|
60
|
+
└──────────────────────────────────────────────────────────┘
|
|
61
|
+
↓
|
|
62
|
+
┌──────────────────────────────────────────────────────────┐
|
|
63
|
+
│ 5. stroke/fill → Paint and clear path │
|
|
64
|
+
│ Current path: [] - cleared after painting │
|
|
65
|
+
│ Current point: undefined │
|
|
66
|
+
└──────────────────────────────────────────────────────────┘
|
|
67
|
+
----
|
|
68
|
+
|
|
69
|
+
=== Path State Components
|
|
70
|
+
|
|
71
|
+
The path state is maintained in the graphics state:
|
|
72
|
+
|
|
73
|
+
**Current Path**:: Array of path segments (moveto, lineto, curveto, closepath)
|
|
74
|
+
|
|
75
|
+
**Current Point**:: Last touched coordinate (x, y)
|
|
76
|
+
|
|
77
|
+
**Path Validity**:: Whether a current point is defined
|
|
78
|
+
|
|
79
|
+
.Path State Example
|
|
80
|
+
[example]
|
|
81
|
+
====
|
|
82
|
+
[source,postscript]
|
|
83
|
+
----
|
|
84
|
+
newpath
|
|
85
|
+
% Path: [], Current point: undefined
|
|
86
|
+
|
|
87
|
+
100 100 moveto
|
|
88
|
+
% Path: [(M 100,100)], Current point: (100,100)
|
|
89
|
+
|
|
90
|
+
200 100 lineto
|
|
91
|
+
% Path: [(M 100,100)(L 200,100)], Current point: (200,100)
|
|
92
|
+
|
|
93
|
+
200 200 lineto
|
|
94
|
+
% Path: [(M 100,100)(L 200,100)(L 200,200)], Current point: (200,200)
|
|
95
|
+
|
|
96
|
+
closepath
|
|
97
|
+
% Path: [(M 100,100)(L 200,100)(L 200,200)(Z)], Current point: (100,100)
|
|
98
|
+
----
|
|
99
|
+
====
|
|
100
|
+
|
|
101
|
+
== Path Construction Operators
|
|
102
|
+
|
|
103
|
+
=== newpath
|
|
104
|
+
|
|
105
|
+
Clears the current path and resets the current point to undefined.
|
|
106
|
+
|
|
107
|
+
**Syntax:**
|
|
108
|
+
|
|
109
|
+
[source,postscript]
|
|
110
|
+
----
|
|
111
|
+
- newpath -
|
|
112
|
+
----
|
|
113
|
+
|
|
114
|
+
**Purpose:**
|
|
115
|
+
|
|
116
|
+
* Start fresh path construction
|
|
117
|
+
* Clear any previously constructed path
|
|
118
|
+
* Prepare for a new shape
|
|
119
|
+
|
|
120
|
+
.Using newpath
|
|
121
|
+
[example]
|
|
122
|
+
====
|
|
123
|
+
[source,postscript]
|
|
124
|
+
----
|
|
125
|
+
% First shape
|
|
126
|
+
newpath
|
|
127
|
+
100 100 moveto
|
|
128
|
+
200 200 lineto
|
|
129
|
+
stroke % Paints and clears path
|
|
130
|
+
|
|
131
|
+
% Second shape (newpath not strictly needed after stroke)
|
|
132
|
+
newpath % Explicitly start new path
|
|
133
|
+
50 50 moveto
|
|
134
|
+
150 50 lineto
|
|
135
|
+
stroke
|
|
136
|
+
----
|
|
137
|
+
====
|
|
138
|
+
|
|
139
|
+
**Important:** `stroke` and `fill` automatically clear the current path, so `newpath` is often redundant after painting operations.
|
|
140
|
+
|
|
141
|
+
=== moveto
|
|
142
|
+
|
|
143
|
+
Begins a new subpath at the specified coordinates.
|
|
144
|
+
|
|
145
|
+
**Syntax:**
|
|
146
|
+
|
|
147
|
+
[source,postscript]
|
|
148
|
+
----
|
|
149
|
+
x y moveto -
|
|
150
|
+
----
|
|
151
|
+
|
|
152
|
+
**Behavior:**
|
|
153
|
+
|
|
154
|
+
* Sets current point to (x, y)
|
|
155
|
+
* Starts a new subpath
|
|
156
|
+
* Does not draw anything
|
|
157
|
+
* If path already exists, creates a disconnected subpath
|
|
158
|
+
|
|
159
|
+
.moveto Examples
|
|
160
|
+
[example]
|
|
161
|
+
====
|
|
162
|
+
[source,postscript]
|
|
163
|
+
----
|
|
164
|
+
% Start a path
|
|
165
|
+
100 100 moveto
|
|
166
|
+
200 100 lineto
|
|
167
|
+
stroke
|
|
168
|
+
% Draws single line from (100,100) to (200,100)
|
|
169
|
+
|
|
170
|
+
% Multiple subpaths
|
|
171
|
+
newpath
|
|
172
|
+
50 50 moveto % First subpath
|
|
173
|
+
150 50 lineto
|
|
174
|
+
200 100 moveto % Second subpath (disconnected)
|
|
175
|
+
200 200 lineto
|
|
176
|
+
stroke
|
|
177
|
+
% Draws two separate line segments
|
|
178
|
+
----
|
|
179
|
+
====
|
|
180
|
+
|
|
181
|
+
**Implementation:**
|
|
182
|
+
|
|
183
|
+
The [`Moveto`](../../lib/postsvg/commands/path/moveto.rb:59) command:
|
|
184
|
+
|
|
185
|
+
[source,ruby]
|
|
186
|
+
----
|
|
187
|
+
def execute(context)
|
|
188
|
+
y = context.pop_number
|
|
189
|
+
x = context.pop_number
|
|
190
|
+
context.path_builder.move_to(x, y)
|
|
191
|
+
context.current_x = x
|
|
192
|
+
context.current_y = y
|
|
193
|
+
end
|
|
194
|
+
----
|
|
195
|
+
|
|
196
|
+
=== rmoveto
|
|
197
|
+
|
|
198
|
+
Relative moveto - moves relative to current point.
|
|
199
|
+
|
|
200
|
+
**Syntax:**
|
|
201
|
+
|
|
202
|
+
[source,postscript]
|
|
203
|
+
----
|
|
204
|
+
dx dy rmoveto -
|
|
205
|
+
----
|
|
206
|
+
|
|
207
|
+
**Behavior:**
|
|
208
|
+
|
|
209
|
+
* Moves to (current_x + dx, current_y + dy)
|
|
210
|
+
* Requires existing current point
|
|
211
|
+
* Useful for relative positioning
|
|
212
|
+
|
|
213
|
+
.rmoveto Example
|
|
214
|
+
[example]
|
|
215
|
+
====
|
|
216
|
+
[source,postscript]
|
|
217
|
+
----
|
|
218
|
+
100 100 moveto % Current point: (100, 100)
|
|
219
|
+
50 0 rmoveto % Move to (150, 100)
|
|
220
|
+
0 50 rlineto % Line to (150, 150)
|
|
221
|
+
stroke
|
|
222
|
+
----
|
|
223
|
+
====
|
|
224
|
+
|
|
225
|
+
=== lineto
|
|
226
|
+
|
|
227
|
+
Adds a straight line from current point to specified coordinates.
|
|
228
|
+
|
|
229
|
+
**Syntax:**
|
|
230
|
+
|
|
231
|
+
[source,postscript]
|
|
232
|
+
----
|
|
233
|
+
x y lineto -
|
|
234
|
+
----
|
|
235
|
+
|
|
236
|
+
**Behavior:**
|
|
237
|
+
|
|
238
|
+
* Draws line from current point to (x, y)
|
|
239
|
+
* Sets current point to (x, y)
|
|
240
|
+
* Requires defined current point
|
|
241
|
+
* Extends current subpath
|
|
242
|
+
|
|
243
|
+
.lineto Examples
|
|
244
|
+
[example]
|
|
245
|
+
====
|
|
246
|
+
[source,postscript]
|
|
247
|
+
----
|
|
248
|
+
% Simple line
|
|
249
|
+
100 100 moveto
|
|
250
|
+
200 200 lineto
|
|
251
|
+
stroke
|
|
252
|
+
|
|
253
|
+
% Polyline (connected segments)
|
|
254
|
+
50 50 moveto
|
|
255
|
+
100 75 lineto
|
|
256
|
+
150 50 lineto
|
|
257
|
+
200 100 lineto
|
|
258
|
+
stroke
|
|
259
|
+
|
|
260
|
+
% Rectangle using lineto
|
|
261
|
+
100 100 moveto
|
|
262
|
+
200 100 lineto
|
|
263
|
+
200 200 lineto
|
|
264
|
+
100 200 lineto
|
|
265
|
+
closepath
|
|
266
|
+
fill
|
|
267
|
+
----
|
|
268
|
+
====
|
|
269
|
+
|
|
270
|
+
**Implementation:**
|
|
271
|
+
|
|
272
|
+
The [`Lineto`](../../lib/postsvg/commands/path/lineto.rb:57) command:
|
|
273
|
+
|
|
274
|
+
[source,ruby]
|
|
275
|
+
----
|
|
276
|
+
def execute(context)
|
|
277
|
+
y = context.pop_number
|
|
278
|
+
x = context.pop_number
|
|
279
|
+
context.path_builder.line_to(x, y)
|
|
280
|
+
context.current_x = x
|
|
281
|
+
context.current_y = y
|
|
282
|
+
end
|
|
283
|
+
----
|
|
284
|
+
|
|
285
|
+
=== rlineto
|
|
286
|
+
|
|
287
|
+
Relative lineto - adds line relative to current point.
|
|
288
|
+
|
|
289
|
+
**Syntax:**
|
|
290
|
+
|
|
291
|
+
[source,postscript]
|
|
292
|
+
----
|
|
293
|
+
dx dy rlineto -
|
|
294
|
+
----
|
|
295
|
+
|
|
296
|
+
.rlineto Example
|
|
297
|
+
[example]
|
|
298
|
+
====
|
|
299
|
+
[source,postscript]
|
|
300
|
+
----
|
|
301
|
+
% Draw square using relative coordinates
|
|
302
|
+
100 100 moveto % Start at (100,100)
|
|
303
|
+
100 0 rlineto % Right 100 to (200,100)
|
|
304
|
+
0 100 rlineto % Up 100 to (200,200)
|
|
305
|
+
-100 0 rlineto % Left 100 to (100,200)
|
|
306
|
+
closepath % Back to start
|
|
307
|
+
fill
|
|
308
|
+
----
|
|
309
|
+
====
|
|
310
|
+
|
|
311
|
+
=== curveto
|
|
312
|
+
|
|
313
|
+
Adds a cubic Bézier curve to the path.
|
|
314
|
+
|
|
315
|
+
**Syntax:**
|
|
316
|
+
|
|
317
|
+
[source,postscript]
|
|
318
|
+
----
|
|
319
|
+
x1 y1 x2 y2 x3 y3 curveto -
|
|
320
|
+
----
|
|
321
|
+
|
|
322
|
+
**Where:**
|
|
323
|
+
|
|
324
|
+
* Current point (x0, y0) - curve start
|
|
325
|
+
* (x1, y1) - first control point
|
|
326
|
+
* (x2, y2) - second control point
|
|
327
|
+
* (x3, y3) - curve end (new current point)
|
|
328
|
+
|
|
329
|
+
**Bézier Curve Mathematics:**
|
|
330
|
+
|
|
331
|
+
The curve is defined by:
|
|
332
|
+
|
|
333
|
+
[source]
|
|
334
|
+
----
|
|
335
|
+
x(t) = (1-t)³·x0 + 3(1-t)²t·x1 + 3(1-t)t²·x2 + t³·x3
|
|
336
|
+
y(t) = (1-t)³·y0 + 3(1-t)²t·y1 + 3(1-t)t²·y2 + t³·y3
|
|
337
|
+
|
|
338
|
+
where t ranges from 0 to 1
|
|
339
|
+
----
|
|
340
|
+
|
|
341
|
+
.Bézier Curve Geometry
|
|
342
|
+
[source]
|
|
343
|
+
----
|
|
344
|
+
(x1,y1) (x2,y2)
|
|
345
|
+
● ●
|
|
346
|
+
╱ ╲ ╱ ╲
|
|
347
|
+
╱ ╲___ ___╱ ╲
|
|
348
|
+
╱ ╲ ╱ ╲
|
|
349
|
+
(x0,y0)● ● ●(x3,y3)
|
|
350
|
+
start curve end
|
|
351
|
+
|
|
352
|
+
Properties:
|
|
353
|
+
- Curve starts at (x0,y0), ends at (x3,y3)
|
|
354
|
+
- Tangent at start points toward (x1,y1)
|
|
355
|
+
- Tangent at end points from (x2,y2)
|
|
356
|
+
- Curve contained in convex hull of 4 points
|
|
357
|
+
----
|
|
358
|
+
|
|
359
|
+
.curveto Examples
|
|
360
|
+
[example]
|
|
361
|
+
====
|
|
362
|
+
[source,postscript]
|
|
363
|
+
----
|
|
364
|
+
% Simple S-curve
|
|
365
|
+
100 100 moveto
|
|
366
|
+
150 150 250 150 300 100 curveto
|
|
367
|
+
stroke
|
|
368
|
+
|
|
369
|
+
% Smooth curve chain
|
|
370
|
+
100 100 moveto
|
|
371
|
+
150 50 200 50 250 100 curveto % First curve
|
|
372
|
+
300 150 350 150 400 100 curveto % Second curve
|
|
373
|
+
stroke
|
|
374
|
+
|
|
375
|
+
% Approximate circle (4 curves)
|
|
376
|
+
% Using magic number 0.5522847498 for 90° arcs
|
|
377
|
+
/r 50 def
|
|
378
|
+
/k 0.5522847498 def
|
|
379
|
+
200 200 moveto
|
|
380
|
+
200 r k mul add 200 r add 200 k mul add 200 r add 200 200 r add curveto
|
|
381
|
+
200 r sub k mul 200 r add 200 r sub 200 r add 200 k mul sub 200 r add curveto
|
|
382
|
+
200 r sub 200 r add k mul sub 200 r sub k mul 200 r add 200 r sub curveto
|
|
383
|
+
200 k mul 200 r sub 200 200 r sub k mul add 200 r sub 200 200 r sub curveto
|
|
384
|
+
closepath
|
|
385
|
+
stroke
|
|
386
|
+
----
|
|
387
|
+
====
|
|
388
|
+
|
|
389
|
+
**Implementation:**
|
|
390
|
+
|
|
391
|
+
The [`Curveto`](../../lib/postsvg/commands/path/curveto.rb:61) command:
|
|
392
|
+
|
|
393
|
+
[source,ruby]
|
|
394
|
+
----
|
|
395
|
+
def execute(context)
|
|
396
|
+
y3 = context.pop_number
|
|
397
|
+
x3 = context.pop_number
|
|
398
|
+
y2 = context.pop_number
|
|
399
|
+
x2 = context.pop_number
|
|
400
|
+
y1 = context.pop_number
|
|
401
|
+
x1 = context.pop_number
|
|
402
|
+
context.path_builder.curve_to(x1, y1, x2, y2, x3, y3)
|
|
403
|
+
context.current_x = x3
|
|
404
|
+
context.current_y = y3
|
|
405
|
+
end
|
|
406
|
+
----
|
|
407
|
+
|
|
408
|
+
=== rcurveto
|
|
409
|
+
|
|
410
|
+
Relative curveto - curve with relative coordinates.
|
|
411
|
+
|
|
412
|
+
**Syntax:**
|
|
413
|
+
|
|
414
|
+
[source,postscript]
|
|
415
|
+
----
|
|
416
|
+
dx1 dy1 dx2 dy2 dx3 dy3 rcurveto -
|
|
417
|
+
----
|
|
418
|
+
|
|
419
|
+
All coordinates are relative to the current point.
|
|
420
|
+
|
|
421
|
+
=== closepath
|
|
422
|
+
|
|
423
|
+
Closes the current subpath by adding a line back to the subpath's starting point.
|
|
424
|
+
|
|
425
|
+
**Syntax:**
|
|
426
|
+
|
|
427
|
+
[source,postscript]
|
|
428
|
+
----
|
|
429
|
+
- closepath -
|
|
430
|
+
----
|
|
431
|
+
|
|
432
|
+
**Behavior:**
|
|
433
|
+
|
|
434
|
+
* Adds straight line from current point to subpath start
|
|
435
|
+
* Marks subpath as closed
|
|
436
|
+
* Sets current point to subpath start
|
|
437
|
+
* Subsequent path operators start a new subpath
|
|
438
|
+
|
|
439
|
+
.closepath Behavior
|
|
440
|
+
[source]
|
|
441
|
+
----
|
|
442
|
+
Without closepath: With closepath:
|
|
443
|
+
100,100 → 200,100 100,100 → 200,100
|
|
444
|
+
↓ ↑ ↓
|
|
445
|
+
100,200 ← 200,200 100,200 ← 200,200
|
|
446
|
+
|
|
447
|
+
Gap at corner Closed corner
|
|
448
|
+
----
|
|
449
|
+
|
|
450
|
+
.closepath Examples
|
|
451
|
+
[example]
|
|
452
|
+
====
|
|
453
|
+
[source,postscript]
|
|
454
|
+
----
|
|
455
|
+
% Triangle without closepath
|
|
456
|
+
newpath
|
|
457
|
+
100 100 moveto
|
|
458
|
+
200 100 lineto
|
|
459
|
+
150 200 lineto
|
|
460
|
+
stroke
|
|
461
|
+
% Shows three lines, not fully closed
|
|
462
|
+
|
|
463
|
+
% Triangle with closepath
|
|
464
|
+
newpath
|
|
465
|
+
100 100 moveto
|
|
466
|
+
200 100 lineto
|
|
467
|
+
150 200 lineto
|
|
468
|
+
closepath
|
|
469
|
+
stroke
|
|
470
|
+
% Shows fully closed triangle
|
|
471
|
+
|
|
472
|
+
% Multiple closed subpaths
|
|
473
|
+
newpath
|
|
474
|
+
% First square
|
|
475
|
+
50 50 moveto
|
|
476
|
+
150 50 lineto
|
|
477
|
+
150 150 lineto
|
|
478
|
+
50 150 lineto
|
|
479
|
+
closepath
|
|
480
|
+
% Second square
|
|
481
|
+
200 200 moveto
|
|
482
|
+
300 200 lineto
|
|
483
|
+
300 300 lineto
|
|
484
|
+
200 300 lineto
|
|
485
|
+
closepath
|
|
486
|
+
fill
|
|
487
|
+
% Fills both squares
|
|
488
|
+
----
|
|
489
|
+
====
|
|
490
|
+
|
|
491
|
+
**Implementation:**
|
|
492
|
+
|
|
493
|
+
The [`ClosePath`](../../lib/postsvg/commands/path/close_path.rb:33) command:
|
|
494
|
+
|
|
495
|
+
[source,ruby]
|
|
496
|
+
----
|
|
497
|
+
def execute(context)
|
|
498
|
+
return unless context.path_length.positive? &&
|
|
499
|
+
!context.path_builder.parts.last&.end_with?("Z")
|
|
500
|
+
|
|
501
|
+
context.path_builder.close
|
|
502
|
+
end
|
|
503
|
+
----
|
|
504
|
+
|
|
505
|
+
== Subpaths
|
|
506
|
+
|
|
507
|
+
=== What are Subpaths?
|
|
508
|
+
|
|
509
|
+
A **subpath** is a continuous sequence of connected segments. A path can contain multiple disconnected subpaths.
|
|
510
|
+
|
|
511
|
+
.Single vs. Multiple Subpaths
|
|
512
|
+
[source]
|
|
513
|
+
----
|
|
514
|
+
Single Subpath:
|
|
515
|
+
moveto → lineto → lineto → closepath
|
|
516
|
+
All segments connected
|
|
517
|
+
|
|
518
|
+
Multiple Subpaths:
|
|
519
|
+
moveto → lineto → lineto → closepath
|
|
520
|
+
moveto → lineto → lineto → closepath
|
|
521
|
+
Two disconnected shapes in one path
|
|
522
|
+
----
|
|
523
|
+
|
|
524
|
+
=== Creating Subpaths
|
|
525
|
+
|
|
526
|
+
Each `moveto` after the first creates a new subpath:
|
|
527
|
+
|
|
528
|
+
.Multiple Subpath Example
|
|
529
|
+
[example]
|
|
530
|
+
====
|
|
531
|
+
[source,postscript]
|
|
532
|
+
----
|
|
533
|
+
newpath
|
|
534
|
+
% First subpath (triangle)
|
|
535
|
+
100 100 moveto
|
|
536
|
+
200 100 lineto
|
|
537
|
+
150 200 lineto
|
|
538
|
+
closepath
|
|
539
|
+
|
|
540
|
+
% Second subpath (square)
|
|
541
|
+
250 100 moveto
|
|
542
|
+
350 100 lineto
|
|
543
|
+
350 200 lineto
|
|
544
|
+
250 200 lineto
|
|
545
|
+
closepath
|
|
546
|
+
|
|
547
|
+
% Paint both subpaths
|
|
548
|
+
fill
|
|
549
|
+
----
|
|
550
|
+
|
|
551
|
+
Both shapes are filled with a single `fill` operation.
|
|
552
|
+
====
|
|
553
|
+
|
|
554
|
+
=== Subpath Use Cases
|
|
555
|
+
|
|
556
|
+
**Multiple Shapes:**
|
|
557
|
+
|
|
558
|
+
[source,postscript]
|
|
559
|
+
----
|
|
560
|
+
% Draw multiple disconnected circles
|
|
561
|
+
newpath
|
|
562
|
+
100 100 50 0 360 arc
|
|
563
|
+
250 100 50 0 360 arc
|
|
564
|
+
400 100 50 0 360 arc
|
|
565
|
+
fill
|
|
566
|
+
% Fills all three circles at once
|
|
567
|
+
----
|
|
568
|
+
|
|
569
|
+
**Compound Paths (Holes):**
|
|
570
|
+
|
|
571
|
+
[source,postscript]
|
|
572
|
+
----
|
|
573
|
+
% Outer square
|
|
574
|
+
newpath
|
|
575
|
+
50 50 moveto
|
|
576
|
+
350 50 lineto
|
|
577
|
+
350 350 lineto
|
|
578
|
+
50 350 lineto
|
|
579
|
+
closepath
|
|
580
|
+
|
|
581
|
+
% Inner square (hole) - use opposite winding
|
|
582
|
+
150 150 moveto
|
|
583
|
+
150 250 lineto
|
|
584
|
+
250 250 lineto
|
|
585
|
+
250 150 lineto
|
|
586
|
+
closepath
|
|
587
|
+
|
|
588
|
+
fill
|
|
589
|
+
% Fills outer square with hole cut out
|
|
590
|
+
----
|
|
591
|
+
|
|
592
|
+
== Path Painting Operations
|
|
593
|
+
|
|
594
|
+
=== stroke
|
|
595
|
+
|
|
596
|
+
Paints the outline of the current path using current line attributes.
|
|
597
|
+
|
|
598
|
+
**Syntax:**
|
|
599
|
+
|
|
600
|
+
[source,postscript]
|
|
601
|
+
----
|
|
602
|
+
- stroke -
|
|
603
|
+
----
|
|
604
|
+
|
|
605
|
+
**Effects:**
|
|
606
|
+
|
|
607
|
+
* Paints path outline with current stroke color
|
|
608
|
+
* Uses current line width, cap, join, dash pattern
|
|
609
|
+
* Clears the current path after painting
|
|
610
|
+
* Resets current point to undefined
|
|
611
|
+
|
|
612
|
+
.stroke Examples
|
|
613
|
+
[example]
|
|
614
|
+
====
|
|
615
|
+
[source,postscript]
|
|
616
|
+
----
|
|
617
|
+
% Simple stroke
|
|
618
|
+
newpath
|
|
619
|
+
100 100 moveto
|
|
620
|
+
200 200 lineto
|
|
621
|
+
1 setlinewidth
|
|
622
|
+
0 0 0 setrgbcolor
|
|
623
|
+
stroke
|
|
624
|
+
|
|
625
|
+
% Styled stroke
|
|
626
|
+
newpath
|
|
627
|
+
50 50 moveto
|
|
628
|
+
350 50 lineto
|
|
629
|
+
5 setlinewidth % Thick line
|
|
630
|
+
1 setlinecap % Round caps
|
|
631
|
+
1 setlinejoin % Round joins
|
|
632
|
+
[10 5] 0 setdash % Dashed pattern
|
|
633
|
+
1 0 0 setrgbcolor % Red color
|
|
634
|
+
stroke
|
|
635
|
+
----
|
|
636
|
+
====
|
|
637
|
+
|
|
638
|
+
**Line Attributes:**
|
|
639
|
+
|
|
640
|
+
See link:graphics-state.adoc#line-attributes[Line Attributes] for details on:
|
|
641
|
+
|
|
642
|
+
* Line width
|
|
643
|
+
* Line cap style
|
|
644
|
+
* Line join style
|
|
645
|
+
* Dash pattern
|
|
646
|
+
* Miter limit
|
|
647
|
+
|
|
648
|
+
=== fill
|
|
649
|
+
|
|
650
|
+
Paints the interior of the current path using the fill color.
|
|
651
|
+
|
|
652
|
+
**Syntax:**
|
|
653
|
+
|
|
654
|
+
[source,postscript]
|
|
655
|
+
----
|
|
656
|
+
- fill -
|
|
657
|
+
----
|
|
658
|
+
|
|
659
|
+
**Effects:**
|
|
660
|
+
|
|
661
|
+
* Paints path interior with current fill color
|
|
662
|
+
* Uses non-zero winding number rule
|
|
663
|
+
* Clears the current path after painting
|
|
664
|
+
* Resets current point to undefined
|
|
665
|
+
|
|
666
|
+
.fill Examples
|
|
667
|
+
[example]
|
|
668
|
+
====
|
|
669
|
+
[source,postscript]
|
|
670
|
+
----
|
|
671
|
+
% Simple fill
|
|
672
|
+
newpath
|
|
673
|
+
100 100 moveto
|
|
674
|
+
200 100 lineto
|
|
675
|
+
200 200 lineto
|
|
676
|
+
100 200 lineto
|
|
677
|
+
closepath
|
|
678
|
+
0.5 setgray
|
|
679
|
+
fill
|
|
680
|
+
|
|
681
|
+
% Multiple subpaths
|
|
682
|
+
newpath
|
|
683
|
+
% Outer rectangle
|
|
684
|
+
50 50 moveto
|
|
685
|
+
350 50 lineto
|
|
686
|
+
350 350 lineto
|
|
687
|
+
50 350 lineto
|
|
688
|
+
closepath
|
|
689
|
+
% Inner rectangle (creates hole)
|
|
690
|
+
150 150 moveto
|
|
691
|
+
150 250 lineto
|
|
692
|
+
250 250 lineto
|
|
693
|
+
250 150 lineto
|
|
694
|
+
closepath
|
|
695
|
+
fill
|
|
696
|
+
----
|
|
697
|
+
====
|
|
698
|
+
|
|
699
|
+
=== eofill
|
|
700
|
+
|
|
701
|
+
Even-odd fill - alternative fill rule.
|
|
702
|
+
|
|
703
|
+
**Syntax:**
|
|
704
|
+
|
|
705
|
+
[source,postscript]
|
|
706
|
+
----
|
|
707
|
+
- eofill -
|
|
708
|
+
----
|
|
709
|
+
|
|
710
|
+
**Fill Rules Comparison:**
|
|
711
|
+
|
|
712
|
+
.Non-Zero Winding vs. Even-Odd
|
|
713
|
+
[source]
|
|
714
|
+
----
|
|
715
|
+
Non-zero winding (fill):
|
|
716
|
+
Counts path windings
|
|
717
|
+
Same direction: fills
|
|
718
|
+
Opposite direction: creates hole
|
|
719
|
+
|
|
720
|
+
Even-odd (eofill):
|
|
721
|
+
Counts path crossings
|
|
722
|
+
Odd crossings: fill
|
|
723
|
+
Even crossings: no fill
|
|
724
|
+
----
|
|
725
|
+
|
|
726
|
+
.eofill Example
|
|
727
|
+
[example]
|
|
728
|
+
====
|
|
729
|
+
[source,postscript]
|
|
730
|
+
----
|
|
731
|
+
% Star with even-odd fill
|
|
732
|
+
newpath
|
|
733
|
+
% Outer star points (clockwise)
|
|
734
|
+
200 50 moveto
|
|
735
|
+
220 150 lineto
|
|
736
|
+
320 150 lineto
|
|
737
|
+
240 210 lineto
|
|
738
|
+
270 310 lineto
|
|
739
|
+
200 250 lineto
|
|
740
|
+
130 310 lineto
|
|
741
|
+
160 210 lineto
|
|
742
|
+
80 150 lineto
|
|
743
|
+
180 150 lineto
|
|
744
|
+
closepath
|
|
745
|
+
eofill
|
|
746
|
+
% Creates filled star with hollow center
|
|
747
|
+
----
|
|
748
|
+
====
|
|
749
|
+
|
|
750
|
+
=== clip
|
|
751
|
+
|
|
752
|
+
Uses the current path as a clipping path.
|
|
753
|
+
|
|
754
|
+
**Syntax:**
|
|
755
|
+
|
|
756
|
+
[source,postscript]
|
|
757
|
+
----
|
|
758
|
+
- clip -
|
|
759
|
+
----
|
|
760
|
+
|
|
761
|
+
**Behavior:**
|
|
762
|
+
|
|
763
|
+
* Sets current path as clipping region
|
|
764
|
+
* Does NOT paint the path
|
|
765
|
+
* Does NOT clear the path (use `newpath` after)
|
|
766
|
+
* All subsequent operations clipped to this region
|
|
767
|
+
|
|
768
|
+
.clip Example
|
|
769
|
+
[example]
|
|
770
|
+
====
|
|
771
|
+
[source,postscript]
|
|
772
|
+
----
|
|
773
|
+
% Clip to circle
|
|
774
|
+
newpath
|
|
775
|
+
200 200 100 0 360 arc
|
|
776
|
+
clip
|
|
777
|
+
newpath % Clear clip path without painting
|
|
778
|
+
|
|
779
|
+
% All drawing now clipped to circle
|
|
780
|
+
0 0 moveto
|
|
781
|
+
400 400 lineto
|
|
782
|
+
stroke
|
|
783
|
+
% Line only visible inside circle
|
|
784
|
+
----
|
|
785
|
+
====
|
|
786
|
+
|
|
787
|
+
== Path to SVG Conversion
|
|
788
|
+
|
|
789
|
+
=== SVG Path Data Format
|
|
790
|
+
|
|
791
|
+
PostScript paths map directly to SVG path data:
|
|
792
|
+
|
|
793
|
+
.Path Command Mapping
|
|
794
|
+
[source]
|
|
795
|
+
----
|
|
796
|
+
PostScript SVG Path Data
|
|
797
|
+
───────────────────────────────────────
|
|
798
|
+
moveto x y → M x y
|
|
799
|
+
lineto x y → L x y
|
|
800
|
+
curveto x1 y1 → C x1 y1 x2 y2 x3 y3
|
|
801
|
+
x2 y2 x3 y3
|
|
802
|
+
closepath → Z
|
|
803
|
+
----
|
|
804
|
+
|
|
805
|
+
.Conversion Example
|
|
806
|
+
[example]
|
|
807
|
+
====
|
|
808
|
+
**PostScript:**
|
|
809
|
+
|
|
810
|
+
[source,postscript]
|
|
811
|
+
----
|
|
812
|
+
newpath
|
|
813
|
+
100 100 moveto
|
|
814
|
+
200 100 lineto
|
|
815
|
+
200 200 lineto
|
|
816
|
+
100 200 lineto
|
|
817
|
+
closepath
|
|
818
|
+
fill
|
|
819
|
+
----
|
|
820
|
+
|
|
821
|
+
**SVG:**
|
|
822
|
+
|
|
823
|
+
[source,xml]
|
|
824
|
+
----
|
|
825
|
+
<path d="M 100 100 L 200 100 L 200 200 L 100 200 Z"
|
|
826
|
+
fill="#808080"
|
|
827
|
+
stroke="none"/>
|
|
828
|
+
----
|
|
829
|
+
====
|
|
830
|
+
|
|
831
|
+
=== PathBuilder Implementation
|
|
832
|
+
|
|
833
|
+
The [`PathBuilder`](../../lib/postsvg/path_builder.rb:7) class constructs SVG path data:
|
|
834
|
+
|
|
835
|
+
[source,ruby]
|
|
836
|
+
----
|
|
837
|
+
# Create path builder
|
|
838
|
+
builder = PathBuilder.new
|
|
839
|
+
|
|
840
|
+
# Add segments
|
|
841
|
+
builder.move_to(100, 100)
|
|
842
|
+
builder.line_to(200, 100)
|
|
843
|
+
builder.line_to(200, 200)
|
|
844
|
+
builder.close
|
|
845
|
+
|
|
846
|
+
# Generate SVG path data
|
|
847
|
+
path_data = builder.to_path
|
|
848
|
+
# => "M 100 100 L 200 100 L 200 200 Z"
|
|
849
|
+
----
|
|
850
|
+
|
|
851
|
+
=== Coordinate Transformation
|
|
852
|
+
|
|
853
|
+
Paths are built in PostScript coordinates, then transformed for SVG:
|
|
854
|
+
|
|
855
|
+
[source,ruby]
|
|
856
|
+
----
|
|
857
|
+
# PostScript coordinates (Y up)
|
|
858
|
+
builder.move_to(50, 50) # Near bottom
|
|
859
|
+
builder.line_to(50, 150) # Near top
|
|
860
|
+
|
|
861
|
+
# SVG applies transform
|
|
862
|
+
# <g transform="translate(0 200) scale(1 -1)">
|
|
863
|
+
# <path d="M 50 50 L 50 150"/>
|
|
864
|
+
# </g>
|
|
865
|
+
# Renders correctly with Y-axis flip
|
|
866
|
+
----
|
|
867
|
+
|
|
868
|
+
== Advanced Path Techniques
|
|
869
|
+
|
|
870
|
+
=== Smooth Curves
|
|
871
|
+
|
|
872
|
+
Create smooth curves by aligning control points:
|
|
873
|
+
|
|
874
|
+
.Smooth Curve Chain
|
|
875
|
+
[example]
|
|
876
|
+
====
|
|
877
|
+
[source,postscript]
|
|
878
|
+
----
|
|
879
|
+
% First curve
|
|
880
|
+
100 100 moveto
|
|
881
|
+
150 50 200 50 250 100 curveto
|
|
882
|
+
|
|
883
|
+
% Second curve - control point aligned
|
|
884
|
+
% Previous end: (250,100)
|
|
885
|
+
% Previous control: (200,50)
|
|
886
|
+
% Reflected control: (300,150) = 2×250 - 200, 2×100 - 50
|
|
887
|
+
300 150 350 150 400 100 curveto
|
|
888
|
+
stroke
|
|
889
|
+
----
|
|
890
|
+
====
|
|
891
|
+
|
|
892
|
+
=== Approximating Arcs
|
|
893
|
+
|
|
894
|
+
Use Bézier curves to approximate circular arcs:
|
|
895
|
+
|
|
896
|
+
.Quarter Circle Approximation
|
|
897
|
+
[example]
|
|
898
|
+
====
|
|
899
|
+
[source,postscript]
|
|
900
|
+
----
|
|
901
|
+
% Magic number for 90° arc: κ = 4(√2-1)/3 ≈ 0.5522847498
|
|
902
|
+
/r 50 def % Radius
|
|
903
|
+
/k 0.5522847498 def % Control point distance
|
|
904
|
+
|
|
905
|
+
% Quarter circle from (r,0) to (0,r)
|
|
906
|
+
r 0 moveto
|
|
907
|
+
r k r mul 0 k r mul r 0 r curveto
|
|
908
|
+
stroke
|
|
909
|
+
----
|
|
910
|
+
|
|
911
|
+
The control points are at distance `κ × r` from the endpoints.
|
|
912
|
+
====
|
|
913
|
+
|
|
914
|
+
=== Path Winding
|
|
915
|
+
|
|
916
|
+
Path direction affects filling with holes:
|
|
917
|
+
|
|
918
|
+
.Winding Direction
|
|
919
|
+
[example]
|
|
920
|
+
====
|
|
921
|
+
[source,postscript]
|
|
922
|
+
----
|
|
923
|
+
% Outer square (clockwise)
|
|
924
|
+
newpath
|
|
925
|
+
100 100 moveto
|
|
926
|
+
300 100 lineto
|
|
927
|
+
300 300 lineto
|
|
928
|
+
100 300 lineto
|
|
929
|
+
closepath
|
|
930
|
+
|
|
931
|
+
% Inner square (counter-clockwise for hole)
|
|
932
|
+
200 200 moveto
|
|
933
|
+
200 150 lineto
|
|
934
|
+
150 150 lineto
|
|
935
|
+
150 200 lineto
|
|
936
|
+
closepath
|
|
937
|
+
|
|
938
|
+
fill
|
|
939
|
+
% Outer filled, inner hollow
|
|
940
|
+
----
|
|
941
|
+
====
|
|
942
|
+
|
|
943
|
+
== Common Path Patterns
|
|
944
|
+
|
|
945
|
+
=== Rectangle
|
|
946
|
+
|
|
947
|
+
[source,postscript]
|
|
948
|
+
----
|
|
949
|
+
x y width height rectfill
|
|
950
|
+
|
|
951
|
+
% Or manually:
|
|
952
|
+
x y moveto
|
|
953
|
+
x width add y lineto
|
|
954
|
+
x width add y height add lineto
|
|
955
|
+
x y height add lineto
|
|
956
|
+
closepath
|
|
957
|
+
fill
|
|
958
|
+
----
|
|
959
|
+
|
|
960
|
+
=== Circle
|
|
961
|
+
|
|
962
|
+
[source,postscript]
|
|
963
|
+
----
|
|
964
|
+
x y radius 0 360 arc
|
|
965
|
+
fill
|
|
966
|
+
|
|
967
|
+
% Using 4 Bézier curves for perfect circle
|
|
968
|
+
----
|
|
969
|
+
|
|
970
|
+
=== Rounded Rectangle
|
|
971
|
+
|
|
972
|
+
[source,postscript]
|
|
973
|
+
----
|
|
974
|
+
/roundrect { % x y width height radius
|
|
975
|
+
/r exch def
|
|
976
|
+
/h exch def
|
|
977
|
+
/w exch def
|
|
978
|
+
/y exch def
|
|
979
|
+
/x exch def
|
|
980
|
+
|
|
981
|
+
newpath
|
|
982
|
+
x r add y moveto
|
|
983
|
+
x w add r sub y x w add y r add r arcto
|
|
984
|
+
x w add y h add r sub x w add r sub y h add r arcto
|
|
985
|
+
x r add y h add x y h add r sub r arcto
|
|
986
|
+
x y r add x r add y r arcto
|
|
987
|
+
closepath
|
|
988
|
+
} def
|
|
989
|
+
|
|
990
|
+
100 100 200 150 20 roundrect
|
|
991
|
+
fill
|
|
992
|
+
----
|
|
993
|
+
|
|
994
|
+
== Troubleshooting Path Issues
|
|
995
|
+
|
|
996
|
+
=== No Current Point Error
|
|
997
|
+
|
|
998
|
+
**Problem:** Operator requires current point but none exists
|
|
999
|
+
|
|
1000
|
+
**Cause:**
|
|
1001
|
+
|
|
1002
|
+
[source,postscript]
|
|
1003
|
+
----
|
|
1004
|
+
newpath
|
|
1005
|
+
lineto 100 100 % ERROR: no current point
|
|
1006
|
+
----
|
|
1007
|
+
|
|
1008
|
+
**Solution:**
|
|
1009
|
+
|
|
1010
|
+
[source,postscript]
|
|
1011
|
+
----
|
|
1012
|
+
newpath
|
|
1013
|
+
moveto 50 50 % Set current point first
|
|
1014
|
+
lineto 100 100 % Now works
|
|
1015
|
+
----
|
|
1016
|
+
|
|
1017
|
+
=== Path Not Closing
|
|
1018
|
+
|
|
1019
|
+
**Problem:** Shape has gap at corner
|
|
1020
|
+
|
|
1021
|
+
**Cause:** Missing `closepath`
|
|
1022
|
+
|
|
1023
|
+
**Solution:**
|
|
1024
|
+
|
|
1025
|
+
[source,postscript]
|
|
1026
|
+
----
|
|
1027
|
+
% Wrong
|
|
1028
|
+
100 100 moveto
|
|
1029
|
+
200 100 lineto
|
|
1030
|
+
200 200 lineto
|
|
1031
|
+
100 200 lineto
|
|
1032
|
+
stroke % Gap at (100,100)
|
|
1033
|
+
|
|
1034
|
+
% Correct
|
|
1035
|
+
100 100 moveto
|
|
1036
|
+
200 100 lineto
|
|
1037
|
+
200 200 lineto
|
|
1038
|
+
100 200 lineto
|
|
1039
|
+
closepath
|
|
1040
|
+
stroke % Fully closed
|
|
1041
|
+
----
|
|
1042
|
+
|
|
1043
|
+
=== Empty SVG Path
|
|
1044
|
+
|
|
1045
|
+
**Problem:** Path not appearing in SVG
|
|
1046
|
+
|
|
1047
|
+
**Cause:** Path not painted before end
|
|
1048
|
+
|
|
1049
|
+
**Solution:**
|
|
1050
|
+
|
|
1051
|
+
[source,postscript]
|
|
1052
|
+
----
|
|
1053
|
+
newpath
|
|
1054
|
+
100 100 moveto
|
|
1055
|
+
200 200 lineto
|
|
1056
|
+
stroke % Must paint!
|
|
1057
|
+
% Without stroke/fill, path is lost
|
|
1058
|
+
----
|
|
1059
|
+
|
|
1060
|
+
== Next Steps
|
|
1061
|
+
|
|
1062
|
+
* Review link:svg-generation.adoc[SVG Generation] for output details
|
|
1063
|
+
* Explore link:graphics-state.adoc[Graphics State] for path state
|
|
1064
|
+
* See link:coordinate-systems.adoc[Coordinate Systems] for transformations
|
|
1065
|
+
* Check link:../api-reference.adoc[API Reference] for path commands
|
|
1066
|
+
* Read link:conversion-pipeline.adoc[Conversion Pipeline] for the full process
|
|
1067
|
+
|
|
1068
|
+
== Bibliography
|
|
1069
|
+
|
|
1070
|
+
* link:svg-generation.adoc[SVG Generation Details]
|
|
1071
|
+
* link:graphics-state.adoc[Graphics State Management]
|
|
1072
|
+
* link:coordinate-systems.adoc[Coordinate Systems]
|
|
1073
|
+
* link:../architecture.adoc[Architecture Overview]
|
|
1074
|
+
* link:https://www.adobe.com/jp/print/postscript/pdfs/PLRM.pdf[PostScript Language Reference Manual] - Chapter 4: Graphics
|
|
1075
|
+
* link:https://www.w3.org/TR/SVG/paths.html[SVG Paths Specification]
|
|
1076
|
+
* link:https://pomax.github.io/bezierinfo/[A Primer on Bézier Curves]
|