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,158 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
# Manages PostScript graphics state including path, color, transforms, etc.
|
|
5
|
+
class GraphicsState
|
|
6
|
+
attr_accessor :line_width, :current_x, :current_y
|
|
7
|
+
attr_reader :current_path, :fill_color, :stroke_color
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@current_path = []
|
|
11
|
+
@current_x = 0
|
|
12
|
+
@current_y = 0
|
|
13
|
+
@line_width = 1.0
|
|
14
|
+
@fill_color = { r: 0, g: 0, b: 0 }
|
|
15
|
+
@stroke_color = { r: 0, g: 0, b: 0 }
|
|
16
|
+
@state_stack = []
|
|
17
|
+
@transform_matrix = [1, 0, 0, 1, 0, 0] # Identity matrix [a, b, c, d, e, f]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Path construction operations
|
|
21
|
+
def moveto(x, y)
|
|
22
|
+
coords = transform_point(x, y)
|
|
23
|
+
@current_x = coords[:x]
|
|
24
|
+
@current_y = coords[:y]
|
|
25
|
+
@current_path << { type: :moveto, x: @current_x, y: @current_y }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def lineto(x, y)
|
|
29
|
+
coords = transform_point(x, y)
|
|
30
|
+
@current_x = coords[:x]
|
|
31
|
+
@current_y = coords[:y]
|
|
32
|
+
@current_path << { type: :lineto, x: @current_x, y: @current_y }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def rlineto(dx, dy)
|
|
36
|
+
# Relative line to
|
|
37
|
+
lineto(@current_x + dx, @current_y + dy)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def curveto(x1, y1, x2, y2, x3, y3)
|
|
41
|
+
coords1 = transform_point(x1, y1)
|
|
42
|
+
coords2 = transform_point(x2, y2)
|
|
43
|
+
coords3 = transform_point(x3, y3)
|
|
44
|
+
|
|
45
|
+
@current_path << {
|
|
46
|
+
type: :curveto,
|
|
47
|
+
x1: coords1[:x], y1: coords1[:y],
|
|
48
|
+
x2: coords2[:x], y2: coords2[:y],
|
|
49
|
+
x3: coords3[:x], y3: coords3[:y]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@current_x = coords3[:x]
|
|
53
|
+
@current_y = coords3[:y]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def closepath
|
|
57
|
+
@current_path << { type: :closepath }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def newpath
|
|
61
|
+
@current_path = []
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Color operations
|
|
65
|
+
def set_rgb_color(r, g, b)
|
|
66
|
+
color = { r: r, g: g, b: b }
|
|
67
|
+
@fill_color = color
|
|
68
|
+
@stroke_color = color
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def set_gray(gray)
|
|
72
|
+
set_rgb_color(gray, gray, gray)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Graphics state stack
|
|
76
|
+
def save
|
|
77
|
+
@state_stack.push({
|
|
78
|
+
path: @current_path.dup,
|
|
79
|
+
x: @current_x,
|
|
80
|
+
y: @current_y,
|
|
81
|
+
line_width: @line_width,
|
|
82
|
+
fill_color: @fill_color.dup,
|
|
83
|
+
stroke_color: @stroke_color.dup,
|
|
84
|
+
transform_matrix: @transform_matrix.dup,
|
|
85
|
+
})
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def restore
|
|
89
|
+
return if @state_stack.empty?
|
|
90
|
+
|
|
91
|
+
state = @state_stack.pop
|
|
92
|
+
@current_path = state[:path]
|
|
93
|
+
@current_x = state[:x]
|
|
94
|
+
@current_y = state[:y]
|
|
95
|
+
@line_width = state[:line_width]
|
|
96
|
+
@fill_color = state[:fill_color]
|
|
97
|
+
@stroke_color = state[:stroke_color]
|
|
98
|
+
@transform_matrix = state[:transform_matrix]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Transformation operations
|
|
102
|
+
def translate(tx, ty)
|
|
103
|
+
# Modify transformation matrix for translation
|
|
104
|
+
@transform_matrix[4] += tx
|
|
105
|
+
@transform_matrix[5] += ty
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def scale(sx, sy)
|
|
109
|
+
# Modify transformation matrix for scaling
|
|
110
|
+
@transform_matrix[0] *= sx
|
|
111
|
+
@transform_matrix[1] *= sx
|
|
112
|
+
@transform_matrix[2] *= sy
|
|
113
|
+
@transform_matrix[3] *= sy
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def rotate(angle)
|
|
117
|
+
# Convert angle from degrees to radians
|
|
118
|
+
rad = angle * Math::PI / 180.0
|
|
119
|
+
cos_a = Math.cos(rad)
|
|
120
|
+
sin_a = Math.sin(rad)
|
|
121
|
+
|
|
122
|
+
# Create rotation matrix and multiply with current matrix
|
|
123
|
+
a, b, c, d, e, f = @transform_matrix
|
|
124
|
+
@transform_matrix = [
|
|
125
|
+
(a * cos_a) + (c * sin_a),
|
|
126
|
+
(b * cos_a) + (d * sin_a),
|
|
127
|
+
(-a * sin_a) + (c * cos_a),
|
|
128
|
+
(-b * sin_a) + (d * cos_a),
|
|
129
|
+
e,
|
|
130
|
+
f,
|
|
131
|
+
]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Helper methods
|
|
135
|
+
def rgb_to_hex(r, g, b)
|
|
136
|
+
format("#%02x%02x%02x", (r * 255).to_i, (g * 255).to_i, (b * 255).to_i)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def fill_color_hex
|
|
140
|
+
rgb_to_hex(fill_color[:r], fill_color[:g], fill_color[:b])
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def stroke_color_hex
|
|
144
|
+
rgb_to_hex(stroke_color[:r], stroke_color[:g], stroke_color[:b])
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
def transform_point(x, y)
|
|
150
|
+
# Apply transformation matrix to point
|
|
151
|
+
a, b, c, d, e, f = @transform_matrix
|
|
152
|
+
{
|
|
153
|
+
x: (a * x) + (c * y) + e,
|
|
154
|
+
y: (b * x) + (d * y) + f,
|
|
155
|
+
}
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|