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,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Postsvg
4
+ module Model
5
+ module Operators
6
+ module Color
7
+ class Setgray < Operator
8
+ register_as "setgray"
9
+ attr_reader :gray
10
+ def initialize(gray:)
11
+ @gray = gray
12
+ freeze
13
+ end
14
+ def self.from_operands(stack)
15
+ new(gray: stack.pop_number)
16
+ end
17
+ end
18
+
19
+ class Setrgbcolor < Operator
20
+ register_as "setrgbcolor"
21
+ attr_reader :red, :green, :blue
22
+ def initialize(red:, green:, blue:)
23
+ @red = red
24
+ @green = green
25
+ @blue = blue
26
+ freeze
27
+ end
28
+ def self.from_operands(stack)
29
+ blue = stack.pop_number
30
+ green = stack.pop_number
31
+ red = stack.pop_number
32
+ new(red: red, green: green, blue: blue)
33
+ end
34
+ end
35
+
36
+ class Setcmykcolor < Operator
37
+ register_as "setcmykcolor"
38
+ attr_reader :cyan, :magenta, :yellow, :key
39
+ def initialize(cyan:, magenta:, yellow:, key:)
40
+ @cyan = cyan
41
+ @magenta = magenta
42
+ @yellow = yellow
43
+ @key = key
44
+ freeze
45
+ end
46
+ def self.from_operands(stack)
47
+ key = stack.pop_number
48
+ yellow = stack.pop_number
49
+ magenta = stack.pop_number
50
+ cyan = stack.pop_number
51
+ new(cyan: cyan, magenta: magenta, yellow: yellow, key: key)
52
+ end
53
+ end
54
+
55
+ class Sethsbcolor < Operator
56
+ register_as "sethsbcolor"
57
+ attr_reader :hue, :saturation, :brightness
58
+ def initialize(hue:, saturation:, brightness:)
59
+ @hue = hue
60
+ @saturation = saturation
61
+ @brightness = brightness
62
+ freeze
63
+ end
64
+ def self.from_operands(stack)
65
+ brightness = stack.pop_number
66
+ saturation = stack.pop_number
67
+ hue = stack.pop_number
68
+ new(hue: hue, saturation: saturation, brightness: brightness)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Postsvg
4
+ module Model
5
+ module Operators
6
+ module Container
7
+ class Length < Operator
8
+ register_as "length", consumes: 1, produces: 1
9
+ attr_reader :operand
10
+ def initialize(operand:)
11
+ @operand = operand
12
+ freeze
13
+ end
14
+ def self.from_operands(stack)
15
+ new(operand: stack.pop)
16
+ end
17
+ end
18
+
19
+ class Get < Operator
20
+ register_as "get", consumes: 2, produces: 1
21
+ attr_reader :operand, :key
22
+ def initialize(operand:, key:)
23
+ @operand = operand
24
+ @key = key
25
+ freeze
26
+ end
27
+ def self.from_operands(stack)
28
+ key = stack.pop
29
+ coll = stack.pop
30
+ new(operand: coll, key: key)
31
+ end
32
+ end
33
+
34
+ class Put < Operator
35
+ register_as "put", consumes: 3, produces: 0
36
+ attr_reader :operand, :key, :value
37
+ def initialize(operand:, key:, value:)
38
+ @operand = operand
39
+ @key = key
40
+ @value = value
41
+ freeze
42
+ end
43
+ def self.from_operands(stack)
44
+ value = stack.pop
45
+ key = stack.pop
46
+ coll = stack.pop
47
+ new(operand: coll, key: key, value: value)
48
+ end
49
+ end
50
+
51
+ class Getinterval < Operator
52
+ register_as "getinterval", consumes: 3, produces: 1
53
+ attr_reader :operand, :start, :count
54
+ def initialize(operand:, start:, count:)
55
+ @operand = operand
56
+ @start = start
57
+ @count = count
58
+ freeze
59
+ end
60
+ def self.from_operands(stack)
61
+ count = stack.pop_number.to_i
62
+ start = stack.pop_number.to_i
63
+ coll = stack.pop
64
+ new(operand: coll, start: start, count: count)
65
+ end
66
+ end
67
+
68
+ class Putinterval < Operator
69
+ register_as "putinterval", consumes: 3, produces: 0
70
+ attr_reader :operand, :start, :source
71
+ def initialize(operand:, start:, source:)
72
+ @operand = operand
73
+ @start = start
74
+ @source = source
75
+ freeze
76
+ end
77
+ def self.from_operands(stack)
78
+ source = stack.pop
79
+ start = stack.pop_number.to_i
80
+ coll = stack.pop
81
+ new(operand: coll, start: start, source: source)
82
+ end
83
+ end
84
+
85
+ class Forall < Operator
86
+ register_as "forall", consumes: 2, produces: 0
87
+ attr_reader :collection, :body
88
+ def initialize(collection:, body:)
89
+ @collection = collection
90
+ @body = body
91
+ freeze
92
+ end
93
+ def self.from_operands(stack)
94
+ body = stack.pop
95
+ coll = stack.pop
96
+ new(collection: coll, body: body)
97
+ end
98
+ end
99
+
100
+ class Astore < Operator
101
+ register_as "astore", consumes: 1, produces: 1
102
+ attr_reader :array, :length
103
+ def initialize(array:, length:)
104
+ @array = array
105
+ @length = length
106
+ freeze
107
+ end
108
+ def self.from_operands(stack)
109
+ arr = stack.pop
110
+ length = stack.pop_number.to_i
111
+ new(array: arr, length: length)
112
+ end
113
+ end
114
+
115
+ class Search < Operator
116
+ register_as "search", consumes: 2, produces: 1
117
+ attr_reader :target, :pattern
118
+ def initialize(target:, pattern:)
119
+ @target = target
120
+ @pattern = pattern
121
+ freeze
122
+ end
123
+ def self.from_operands(stack)
124
+ pattern = stack.pop
125
+ target = stack.pop
126
+ new(target: target, pattern: pattern)
127
+ end
128
+ end
129
+
130
+ class Anchorsearch < Operator
131
+ register_as "anchorsearch", consumes: 2, produces: 1
132
+ attr_reader :target, :pattern
133
+ def initialize(target:, pattern:)
134
+ @target = target
135
+ @pattern = pattern
136
+ freeze
137
+ end
138
+ def self.from_operands(stack)
139
+ pattern = stack.pop
140
+ target = stack.pop
141
+ new(target: target, pattern: pattern)
142
+ end
143
+ end
144
+
145
+ class Token < Operator
146
+ register_as "token", consumes: 1, produces: 1
147
+ attr_reader :target
148
+ def initialize(target:)
149
+ @target = target
150
+ freeze
151
+ end
152
+ def self.from_operands(stack)
153
+ new(target: stack.pop)
154
+ end
155
+ end
156
+
157
+ class String < Operator
158
+ register_as "string", consumes: 1, produces: 1
159
+ attr_reader :count
160
+ def initialize(count:)
161
+ @count = count
162
+ freeze
163
+ end
164
+ def self.from_operands(stack)
165
+ new(count: stack.pop_number.to_i)
166
+ end
167
+ end
168
+
169
+ class Cvs < Operator
170
+ register_as "cvs", consumes: 2, produces: 1
171
+ attr_reader :value, :target
172
+ def initialize(value:, target:)
173
+ @value = value
174
+ @target = target
175
+ freeze
176
+ end
177
+ def self.from_operands(stack)
178
+ target = stack.pop
179
+ value = stack.pop
180
+ new(value: value, target: target)
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Postsvg
4
+ module Model
5
+ module Operators
6
+ module ControlFlow
7
+ class If < Operator
8
+ register_as "if"
9
+ attr_reader :condition, :body
10
+ def initialize(condition:, body:)
11
+ @condition = condition
12
+ @body = body
13
+ freeze
14
+ end
15
+ def self.from_operands(stack)
16
+ body = stack.pop
17
+ condition = stack.pop
18
+ new(condition: condition, body: body)
19
+ end
20
+ end
21
+
22
+ class Ifelse < Operator
23
+ register_as "ifelse"
24
+ attr_reader :condition, :if_body, :else_body
25
+ def initialize(condition:, if_body:, else_body:)
26
+ @condition = condition
27
+ @if_body = if_body
28
+ @else_body = else_body
29
+ freeze
30
+ end
31
+ def self.from_operands(stack)
32
+ else_body = stack.pop
33
+ if_body = stack.pop
34
+ condition = stack.pop
35
+ new(condition: condition, if_body: if_body, else_body: else_body)
36
+ end
37
+ end
38
+
39
+ class Repeat < Operator
40
+ register_as "repeat"
41
+ attr_reader :count, :body
42
+ def initialize(count:, body:)
43
+ @count = count
44
+ @body = body
45
+ freeze
46
+ end
47
+ def self.from_operands(stack)
48
+ body = stack.pop
49
+ count = stack.pop_number.to_i
50
+ new(count: count, body: body)
51
+ end
52
+ end
53
+
54
+ class Loop < Operator
55
+ register_as "loop"
56
+ attr_reader :body
57
+ def initialize(body:)
58
+ @body = body
59
+ freeze
60
+ end
61
+ def self.from_operands(stack)
62
+ new(body: stack.pop)
63
+ end
64
+ end
65
+
66
+ class For < Operator
67
+ register_as "for"
68
+ attr_reader :initial, :increment, :limit, :body
69
+ def initialize(initial:, increment:, limit:, body:)
70
+ @initial = initial
71
+ @increment = increment
72
+ @limit = limit
73
+ @body = body
74
+ freeze
75
+ end
76
+ def self.from_operands(stack)
77
+ body = stack.pop
78
+ limit = stack.pop_number
79
+ increment = stack.pop_number
80
+ initial = stack.pop_number
81
+ new(initial: initial, increment: increment, limit: limit, body: body)
82
+ end
83
+ end
84
+
85
+ class Exit < Operator
86
+ register_as "exit"
87
+ end
88
+
89
+ class Quit < Operator
90
+ register_as "quit"
91
+ end
92
+
93
+ class Exec < Operator
94
+ register_as "exec"
95
+ attr_reader :operand
96
+ def initialize(operand:)
97
+ @operand = operand
98
+ freeze
99
+ end
100
+ def self.from_operands(stack)
101
+ new(operand: stack.pop)
102
+ end
103
+ end
104
+
105
+ class Stopped < Operator
106
+ register_as "stopped"
107
+ attr_reader :body
108
+ def initialize(body:)
109
+ @body = body
110
+ freeze
111
+ end
112
+ def self.from_operands(stack)
113
+ new(body: stack.pop)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Postsvg
4
+ module Model
5
+ module Operators
6
+ module Device
7
+ class Showpage < Operator
8
+ register_as "showpage"
9
+ end
10
+
11
+ class Copypage < Operator
12
+ register_as "copypage"
13
+ end
14
+
15
+ class Nulldevice < Operator
16
+ register_as "nulldevice"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Postsvg
4
+ module Model
5
+ module Operators
6
+ # Dictionary-specific operators. Note that +get+, +put+,
7
+ # +length+, +getinterval+, +putinterval+ live in the +Container+
8
+ # module because they dispatch on operand type (string, array,
9
+ # or dictionary) at runtime.
10
+ module Dictionary
11
+ class Dict < Operator
12
+ register_as "dict", consumes: 1, produces: 1
13
+ def self.from_operands(stack)
14
+ stack.pop
15
+ new
16
+ end
17
+ end
18
+
19
+ class Begin < Operator
20
+ register_as "begin", consumes: 1, produces: 0
21
+ def self.from_operands(stack)
22
+ stack.pop
23
+ new
24
+ end
25
+ end
26
+
27
+ class End < Operator
28
+ register_as "end", consumes: 0, produces: 0
29
+ end
30
+
31
+ class Def < Operator
32
+ register_as "def", consumes: 2, produces: 0
33
+ attr_reader :key, :value
34
+ def initialize(key:, value:)
35
+ @key = key
36
+ @value = value
37
+ freeze
38
+ end
39
+ def self.from_operands(stack)
40
+ value = stack.pop
41
+ key = stack.pop
42
+ new(key: key, value: value)
43
+ end
44
+ end
45
+
46
+ class Load < Operator
47
+ register_as "load", consumes: 1, produces: 1
48
+ attr_reader :key
49
+ def initialize(key:)
50
+ @key = key
51
+ freeze
52
+ end
53
+ def self.from_operands(stack)
54
+ new(key: stack.pop)
55
+ end
56
+ end
57
+
58
+ class Store < Operator
59
+ register_as "store", consumes: 2, produces: 0
60
+ attr_reader :key, :value
61
+ def initialize(key:, value:)
62
+ @key = key
63
+ @value = value
64
+ freeze
65
+ end
66
+ def self.from_operands(stack)
67
+ value = stack.pop
68
+ key = stack.pop
69
+ new(key: key, value: value)
70
+ end
71
+ end
72
+
73
+ class Known < Operator
74
+ register_as "known", consumes: 2, produces: 1
75
+ def self.from_operands(stack)
76
+ key = stack.pop
77
+ dict = stack.pop
78
+ new(key: key, dict: dict)
79
+ end
80
+ attr_reader :key, :dict
81
+ def initialize(key:, dict:)
82
+ @key = key
83
+ @dict = dict
84
+ freeze
85
+ end
86
+ end
87
+
88
+ class Currentdict < Operator
89
+ register_as "currentdict", consumes: 0, produces: 1
90
+ end
91
+
92
+ class Countdictstack < Operator
93
+ register_as "countdictstack", consumes: 0, produces: 1
94
+ end
95
+
96
+ class Dictstack < Operator
97
+ register_as "dictstack", consumes: 1, produces: 1
98
+ def self.from_operands(stack)
99
+ stack.pop
100
+ new
101
+ end
102
+ end
103
+
104
+ class Maxlength < Operator
105
+ register_as "maxlength", consumes: 1, produces: 1
106
+ def self.from_operands(stack)
107
+ new(operand: stack.pop)
108
+ end
109
+ attr_reader :operand
110
+ def initialize(operand:)
111
+ @operand = operand
112
+ freeze
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Postsvg
4
+ module Model
5
+ module Operators
6
+ # Font and text operators. These cover the subset of PLRM §5
7
+ # needed to round-trip basic SVG `<text>` content; richer
8
+ # font metrics (kerning, ligatures, glyph substitution) are
9
+ # tracked in TODO.roadmap/20-font-and-text.md.
10
+ module Font
11
+ # Pushes a font dictionary onto the stack by name. +name+ is
12
+ # the PostScript font name (e.g. "Helvetica"). The visitor
13
+ # pushes a Font value object.
14
+ class Findfont < Operator
15
+ register_as "findfont", consumes: 1, produces: 1
16
+ attr_reader :name
17
+ def initialize(name:)
18
+ @name = name
19
+ freeze
20
+ end
21
+ def self.from_operands(stack)
22
+ new(name: stack.pop)
23
+ end
24
+ end
25
+
26
+ # Returns a new font dictionary scaled to the given size.
27
+ class Scalefont < Operator
28
+ register_as "scalefont", consumes: 2, produces: 1
29
+ attr_reader :font, :size
30
+ def initialize(font:, size:)
31
+ @font = font
32
+ @size = size
33
+ freeze
34
+ end
35
+ def self.from_operands(stack)
36
+ size = stack.pop_number
37
+ font = stack.pop
38
+ new(font: font, size: size)
39
+ end
40
+ end
41
+
42
+ # Installs the font dictionary as the current font.
43
+ class Setfont < Operator
44
+ register_as "setfont", consumes: 1, produces: 0
45
+ attr_reader :font
46
+ def initialize(font:)
47
+ @font = font
48
+ freeze
49
+ end
50
+ def self.from_operands(stack)
51
+ new(font: stack.pop)
52
+ end
53
+ end
54
+
55
+ # Renders +text+ at the current point using the current font.
56
+ class Show < Operator
57
+ register_as "show", consumes: 1, produces: 0
58
+ attr_reader :text
59
+ def initialize(text:)
60
+ @text = text
61
+ freeze
62
+ end
63
+ def self.from_operands(stack)
64
+ new(text: stack.pop)
65
+ end
66
+ end
67
+
68
+ # Moves to (x, y) and renders +text+ (PLRM §5.5 xyshow
69
+ # variant not implemented here).
70
+ class Xyshow < Operator
71
+ register_as "xyshow"
72
+ attr_reader :text, :dx, :dy
73
+ def initialize(text:, dx:, dy:)
74
+ @text = text
75
+ @dx = dx
76
+ @dy = dy
77
+ freeze
78
+ end
79
+ def self.from_operands(stack)
80
+ dy = stack.pop
81
+ dx = stack.pop
82
+ text = stack.pop
83
+ new(text: text, dx: dx, dy: dy)
84
+ end
85
+ end
86
+
87
+ # Returns the width of +text+ under the current font (in
88
+ # thousandths of a unit, divided by 1000). The visitor
89
+ # pushes [x, y] onto the stack.
90
+ class Stringwidth < Operator
91
+ register_as "stringwidth"
92
+ attr_reader :text
93
+ def initialize(text:)
94
+ @text = text
95
+ freeze
96
+ end
97
+ def self.from_operands(stack)
98
+ new(text: stack.pop)
99
+ end
100
+ end
101
+
102
+ # Appends glyph outlines for +text+ to the current path
103
+ # (PLRM charpath).
104
+ class Charpath < Operator
105
+ register_as "charpath"
106
+ attr_reader :text, :stroke
107
+ def initialize(text:, stroke:)
108
+ @text = text
109
+ @stroke = stroke
110
+ freeze
111
+ end
112
+ def self.from_operands(stack)
113
+ stroke = stack.pop
114
+ text = stack.pop
115
+ new(text: text, stroke: stroke)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end