Class: Postsvg::Serializer
- Inherits:
-
Object
- Object
- Postsvg::Serializer
- Defined in:
- lib/postsvg/serializer.rb
Overview
Model::Program -> PostScript source text. Walks the program's header (DSC comments) and body (Model records), emitting each as PS source. Used by the SVG -> PS direction and by tests.
OCP: adding serialization for a new operator class means adding
one emit_<keyword> method (or falling through to the default
keyword+operands emitter). No switch edits.
Constant Summary collapse
Instance Attribute Summary collapse
-
#creator ⇒ Object
readonly
Returns the value of attribute creator.
-
#eps ⇒ Object
readonly
Returns the value of attribute eps.
-
#program ⇒ Object
readonly
Returns the value of attribute program.
Class Method Summary collapse
Instance Method Summary collapse
- #call ⇒ Object
- #emit_anchorsearch(_op, buf) ⇒ Object
- #emit_arc(op, buf) ⇒ Object
- #emit_arcn(op, buf) ⇒ Object
- #emit_astore(_op, buf) ⇒ Object
- #emit_body(buffer) ⇒ Object
- #emit_charpath(_op, buf) ⇒ Object
- #emit_clip(_op, buf) ⇒ Object
- #emit_closepath(_op, buf) ⇒ Object
- #emit_concat(op, buf) ⇒ Object
- #emit_countdictstack(_op, buf) ⇒ Object
-
#emit_currentdict(_op, buf) ⇒ Object
Dictionary operators (currentdict etc.).
- #emit_curveto(op, buf) ⇒ Object
- #emit_cvs(_op, buf) ⇒ Object
- #emit_dictstack(_op, buf) ⇒ Object
- #emit_eoclip(_op, buf) ⇒ Object
- #emit_eofill(_op, buf) ⇒ Object
- #emit_fill(_op, buf) ⇒ Object
-
#emit_findfont(op, buf) ⇒ Object
Font / text.
- #emit_forall(_op, buf) ⇒ Object
- #emit_generic_operator(operator, buffer) ⇒ Object
- #emit_get(_op, buf) ⇒ Object
- #emit_getinterval(_op, buf) ⇒ Object
- #emit_grestore(_op, buf) ⇒ Object
- #emit_grestoreall(_op, buf) ⇒ Object
-
#emit_gsave(_op, buf) ⇒ Object
Graphics state.
-
#emit_header(buffer) ⇒ Object
All methods below this point are public.
- #emit_inline(statement, buffer) ⇒ Object
-
#emit_length(_op, buf) ⇒ Object
Container operators.
- #emit_lineto(op, buf) ⇒ Object
- #emit_maxlength(_op, buf) ⇒ Object
-
#emit_moveto(op, buf) ⇒ Object
Path.
- #emit_newpath(_op, buf) ⇒ Object
-
#emit_operator(operator, buffer) ⇒ Object
Dispatch table: emit_
. - #emit_put(_op, buf) ⇒ Object
- #emit_putinterval(_op, buf) ⇒ Object
- #emit_rcurveto(op, buf) ⇒ Object
- #emit_rlineto(op, buf) ⇒ Object
- #emit_rmoveto(op, buf) ⇒ Object
- #emit_rotate(op, buf) ⇒ Object
- #emit_scale(op, buf) ⇒ Object
- #emit_scalefont(op, buf) ⇒ Object
- #emit_search(_op, buf) ⇒ Object
- #emit_setcmykcolor(op, buf) ⇒ Object
- #emit_setdash(op, buf) ⇒ Object
- #emit_setfont(_op, buf) ⇒ Object
- #emit_setgray(op, buf) ⇒ Object
- #emit_sethsbcolor(op, buf) ⇒ Object
- #emit_setlinecap(op, buf) ⇒ Object
- #emit_setlinejoin(op, buf) ⇒ Object
- #emit_setlinewidth(op, buf) ⇒ Object
- #emit_setmiterlimit(op, buf) ⇒ Object
-
#emit_setrgbcolor(op, buf) ⇒ Object
Color.
- #emit_show(op, buf) ⇒ Object
- #emit_statement(statement, buffer) ⇒ Object
- #emit_string(_op, buf) ⇒ Object
- #emit_stringwidth(_op, buf) ⇒ Object
-
#emit_stroke(_op, buf) ⇒ Object
Painting.
- #emit_token(_op, buf) ⇒ Object
-
#emit_translate(op, buf) ⇒ Object
Transformations.
- #emit_xyshow(_op, buf) ⇒ Object
- #escape_string(text) ⇒ Object
- #font_name_value(value) ⇒ Object
-
#initialize(program, eps:, creator:) ⇒ Serializer
constructor
A new instance of Serializer.
- #string_value(value) ⇒ Object
Constructor Details
#initialize(program, eps:, creator:) ⇒ Serializer
Returns a new instance of Serializer.
21 22 23 24 25 26 27 |
# File 'lib/postsvg/serializer.rb', line 21 def initialize(program, eps:, creator:) raise ArgumentError, "program must be a Model::Program" unless program.is_a?(Model::Program) @program = program @eps = eps @creator = creator end |
Instance Attribute Details
#creator ⇒ Object (readonly)
Returns the value of attribute creator.
19 20 21 |
# File 'lib/postsvg/serializer.rb', line 19 def creator @creator end |
#eps ⇒ Object (readonly)
Returns the value of attribute eps.
19 20 21 |
# File 'lib/postsvg/serializer.rb', line 19 def eps @eps end |
#program ⇒ Object (readonly)
Returns the value of attribute program.
19 20 21 |
# File 'lib/postsvg/serializer.rb', line 19 def program @program end |
Class Method Details
.call(program, eps: false, creator: DEFAULT_CREATOR, **_options) ⇒ Object
15 16 17 |
# File 'lib/postsvg/serializer.rb', line 15 def self.call(program, eps: false, creator: DEFAULT_CREATOR, **) new(program, eps: eps, creator: creator).call end |
Instance Method Details
#call ⇒ Object
29 30 31 32 33 34 35 36 |
# File 'lib/postsvg/serializer.rb', line 29 def call buffer = String.new(capacity: 4096) emit_header(buffer) emit_body(buffer) buffer << "showpage\n" buffer << "%%EOF\n" buffer end |
#emit_anchorsearch(_op, buf) ⇒ Object
296 |
# File 'lib/postsvg/serializer.rb', line 296 def emit_anchorsearch(_op, buf) = buf << "anchorsearch\n" |
#emit_arc(op, buf) ⇒ Object
153 154 155 156 157 |
# File 'lib/postsvg/serializer.rb', line 153 def emit_arc(op, buf) buf << "#{FormatNumber.call(op.x)} #{FormatNumber.call(op.y)} " \ "#{FormatNumber.call(op.radius)} " \ "#{FormatNumber.call(op.angle1)} #{FormatNumber.call(op.angle2)} arc\n" end |
#emit_arcn(op, buf) ⇒ Object
159 160 161 162 163 |
# File 'lib/postsvg/serializer.rb', line 159 def emit_arcn(op, buf) buf << "#{FormatNumber.call(op.x)} #{FormatNumber.call(op.y)} " \ "#{FormatNumber.call(op.radius)} " \ "#{FormatNumber.call(op.angle1)} #{FormatNumber.call(op.angle2)} arcn\n" end |
#emit_astore(_op, buf) ⇒ Object
294 |
# File 'lib/postsvg/serializer.rb', line 294 def emit_astore(_op, buf) = buf << "astore\n" |
#emit_body(buffer) ⇒ Object
57 58 59 60 61 |
# File 'lib/postsvg/serializer.rb', line 57 def emit_body(buffer) program.body.each do |statement| emit_statement(statement, buffer) end end |
#emit_charpath(_op, buf) ⇒ Object
283 284 285 |
# File 'lib/postsvg/serializer.rb', line 283 def emit_charpath(_op, buf) buf << "% charpath: requires font metrics\n" end |
#emit_clip(_op, buf) ⇒ Object
177 |
# File 'lib/postsvg/serializer.rb', line 177 def emit_clip(_op, buf) = buf << "clip\n" |
#emit_closepath(_op, buf) ⇒ Object
165 166 167 |
# File 'lib/postsvg/serializer.rb', line 165 def emit_closepath(_op, buf) buf << "closepath\n" end |
#emit_concat(op, buf) ⇒ Object
243 244 245 246 247 |
# File 'lib/postsvg/serializer.rb', line 243 def emit_concat(op, buf) m = op.matrix arr = m.is_a?(Array) ? m : [m.a, m.b, m.c, m.d, m.e, m.f] buf << "[#{arr.map { |v| FormatNumber.call(v.to_f) }.join(' ')}] concat\n" end |
#emit_countdictstack(_op, buf) ⇒ Object
303 |
# File 'lib/postsvg/serializer.rb', line 303 def emit_countdictstack(_op, buf) = buf << "countdictstack\n" |
#emit_currentdict(_op, buf) ⇒ Object
Dictionary operators (currentdict etc.)
302 |
# File 'lib/postsvg/serializer.rb', line 302 def emit_currentdict(_op, buf) = buf << "currentdict\n" |
#emit_curveto(op, buf) ⇒ Object
141 142 143 144 145 |
# File 'lib/postsvg/serializer.rb', line 141 def emit_curveto(op, buf) buf << "#{FormatNumber.call(op.x1)} #{FormatNumber.call(op.y1)} " \ "#{FormatNumber.call(op.x2)} #{FormatNumber.call(op.y2)} " \ "#{FormatNumber.call(op.x3)} #{FormatNumber.call(op.y3)} curveto\n" end |
#emit_cvs(_op, buf) ⇒ Object
299 |
# File 'lib/postsvg/serializer.rb', line 299 def emit_cvs(_op, buf) = buf << "cvs\n" |
#emit_dictstack(_op, buf) ⇒ Object
304 |
# File 'lib/postsvg/serializer.rb', line 304 def emit_dictstack(_op, buf) = buf << "dictstack\n" |
#emit_eoclip(_op, buf) ⇒ Object
178 |
# File 'lib/postsvg/serializer.rb', line 178 def emit_eoclip(_op, buf) = buf << "eoclip\n" |
#emit_eofill(_op, buf) ⇒ Object
176 |
# File 'lib/postsvg/serializer.rb', line 176 def emit_eofill(_op, buf) = buf << "eofill\n" |
#emit_fill(_op, buf) ⇒ Object
175 |
# File 'lib/postsvg/serializer.rb', line 175 def emit_fill(_op, buf) = buf << "fill\n" |
#emit_findfont(op, buf) ⇒ Object
Font / text
258 259 260 261 |
# File 'lib/postsvg/serializer.rb', line 258 def emit_findfont(op, buf) name = font_name_value(op.name) buf << "/#{name} findfont\n" end |
#emit_forall(_op, buf) ⇒ Object
293 |
# File 'lib/postsvg/serializer.rb', line 293 def emit_forall(_op, buf) = buf << "forall\n" |
#emit_generic_operator(operator, buffer) ⇒ Object
120 121 122 |
# File 'lib/postsvg/serializer.rb', line 120 def emit_generic_operator(operator, buffer) buffer << operator.keyword << "\n" end |
#emit_get(_op, buf) ⇒ Object
289 |
# File 'lib/postsvg/serializer.rb', line 289 def emit_get(_op, buf) = buf << "get\n" |
#emit_getinterval(_op, buf) ⇒ Object
291 |
# File 'lib/postsvg/serializer.rb', line 291 def emit_getinterval(_op, buf) = buf << "getinterval\n" |
#emit_grestore(_op, buf) ⇒ Object
201 |
# File 'lib/postsvg/serializer.rb', line 201 def emit_grestore(_op, buf) = buf << "grestore\n" |
#emit_grestoreall(_op, buf) ⇒ Object
202 |
# File 'lib/postsvg/serializer.rb', line 202 def emit_grestoreall(_op, buf) = buf << "grestoreall\n" |
#emit_gsave(_op, buf) ⇒ Object
Graphics state
200 |
# File 'lib/postsvg/serializer.rb', line 200 def emit_gsave(_op, buf) = buf << "gsave\n" |
#emit_header(buffer) ⇒ Object
All methods below this point are public. They are intentionally
exposed (prefixed emit_) so dispatch can reach them via
method_defined? without respond_to?.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/postsvg/serializer.rb', line 41 def emit_header(buffer) buffer << "%!PS-Adobe-#{DEFAULT_LANGUAGE_LEVEL}.0" buffer << " EPSF-3.0" if eps buffer << "\n" buffer << "%%Creator: #{creator}\n" bbox = program.header.bounding_box if bbox && !bbox.empty? buffer << "%%BoundingBox: " << bbox.map { |v| FormatNumber.call(v) }.join(" ") << "\n" end if program.header.title buffer << "%%Title: #{program.header.title}\n" end buffer << "%%LanguageLevel: #{DEFAULT_LANGUAGE_LEVEL}\n" buffer << "%%EndComments\n" end |
#emit_inline(statement, buffer) ⇒ Object
99 100 101 102 103 104 105 106 107 |
# File 'lib/postsvg/serializer.rb', line 99 def emit_inline(statement, buffer) case statement when Model::Literals::Number then buffer << FormatNumber.call(statement.value) when Model::Literals::Name then buffer << (statement.literal? ? "/" : "") << statement.value when Model::Literals::StringLiteral then buffer << escape_string(statement.value) when Model::Literals::HexLiteral then buffer << "<" << statement.value << ">" else buffer << statement.class.name.to_s end end |
#emit_length(_op, buf) ⇒ Object
Container operators
288 |
# File 'lib/postsvg/serializer.rb', line 288 def emit_length(_op, buf) = buf << "length\n" |
#emit_lineto(op, buf) ⇒ Object
133 134 135 |
# File 'lib/postsvg/serializer.rb', line 133 def emit_lineto(op, buf) buf << "#{FormatNumber.call(op.x)} #{FormatNumber.call(op.y)} lineto\n" end |
#emit_maxlength(_op, buf) ⇒ Object
305 |
# File 'lib/postsvg/serializer.rb', line 305 def emit_maxlength(_op, buf) = buf << "maxlength\n" |
#emit_moveto(op, buf) ⇒ Object
Path
125 126 127 |
# File 'lib/postsvg/serializer.rb', line 125 def emit_moveto(op, buf) buf << "#{FormatNumber.call(op.x)} #{FormatNumber.call(op.y)} moveto\n" end |
#emit_newpath(_op, buf) ⇒ Object
169 170 171 |
# File 'lib/postsvg/serializer.rb', line 169 def emit_newpath(_op, buf) buf << "newpath\n" end |
#emit_operator(operator, buffer) ⇒ Object
Dispatch table: emit_
111 112 113 114 115 116 117 118 |
# File 'lib/postsvg/serializer.rb', line 111 def emit_operator(operator, buffer) method = :"emit_#{operator.visit_name}" if self.class.public_method_defined?(method) public_send(method, operator, buffer) else emit_generic_operator(operator, buffer) end end |
#emit_put(_op, buf) ⇒ Object
290 |
# File 'lib/postsvg/serializer.rb', line 290 def emit_put(_op, buf) = buf << "put\n" |
#emit_putinterval(_op, buf) ⇒ Object
292 |
# File 'lib/postsvg/serializer.rb', line 292 def emit_putinterval(_op, buf) = buf << "putinterval\n" |
#emit_rcurveto(op, buf) ⇒ Object
147 148 149 150 151 |
# File 'lib/postsvg/serializer.rb', line 147 def emit_rcurveto(op, buf) buf << "#{FormatNumber.call(op.dx1)} #{FormatNumber.call(op.dy1)} " \ "#{FormatNumber.call(op.dx2)} #{FormatNumber.call(op.dy2)} " \ "#{FormatNumber.call(op.dx3)} #{FormatNumber.call(op.dy3)} rcurveto\n" end |
#emit_rlineto(op, buf) ⇒ Object
137 138 139 |
# File 'lib/postsvg/serializer.rb', line 137 def emit_rlineto(op, buf) buf << "#{FormatNumber.call(op.dx)} #{FormatNumber.call(op.dy)} rlineto\n" end |
#emit_rmoveto(op, buf) ⇒ Object
129 130 131 |
# File 'lib/postsvg/serializer.rb', line 129 def emit_rmoveto(op, buf) buf << "#{FormatNumber.call(op.dx)} #{FormatNumber.call(op.dy)} rmoveto\n" end |
#emit_rotate(op, buf) ⇒ Object
239 240 241 |
# File 'lib/postsvg/serializer.rb', line 239 def emit_rotate(op, buf) buf << "#{FormatNumber.call(op.angle)} rotate\n" end |
#emit_scale(op, buf) ⇒ Object
235 236 237 |
# File 'lib/postsvg/serializer.rb', line 235 def emit_scale(op, buf) buf << "#{FormatNumber.call(op.sx)} #{FormatNumber.call(op.sy)} scale\n" end |
#emit_scalefont(op, buf) ⇒ Object
263 264 265 |
# File 'lib/postsvg/serializer.rb', line 263 def emit_scalefont(op, buf) buf << "#{FormatNumber.call(op.size)} scalefont\n" end |
#emit_search(_op, buf) ⇒ Object
295 |
# File 'lib/postsvg/serializer.rb', line 295 def emit_search(_op, buf) = buf << "search\n" |
#emit_setcmykcolor(op, buf) ⇒ Object
189 190 191 192 |
# File 'lib/postsvg/serializer.rb', line 189 def emit_setcmykcolor(op, buf) buf << "#{FormatNumber.call(op.cyan)} #{FormatNumber.call(op.magenta)} " \ "#{FormatNumber.call(op.yellow)} #{FormatNumber.call(op.key)} setcmykcolor\n" end |
#emit_setdash(op, buf) ⇒ Object
220 221 222 223 224 225 226 227 228 |
# File 'lib/postsvg/serializer.rb', line 220 def emit_setdash(op, buf) pattern_str = case op.pattern when Array then "[#{op.pattern.map { |v| FormatNumber.call(v.to_f) }.join(' ')}]" when Numeric then FormatNumber.call(op.pattern.to_f) else "[]" end buf << "#{pattern_str} #{FormatNumber.call(op.offset)} setdash\n" end |
#emit_setfont(_op, buf) ⇒ Object
267 268 269 |
# File 'lib/postsvg/serializer.rb', line 267 def emit_setfont(_op, buf) buf << "setfont\n" end |
#emit_setgray(op, buf) ⇒ Object
185 186 187 |
# File 'lib/postsvg/serializer.rb', line 185 def emit_setgray(op, buf) buf << "#{FormatNumber.call(op.gray)} setgray\n" end |
#emit_sethsbcolor(op, buf) ⇒ Object
194 195 196 197 |
# File 'lib/postsvg/serializer.rb', line 194 def emit_sethsbcolor(op, buf) buf << "#{FormatNumber.call(op.hue)} #{FormatNumber.call(op.saturation)} " \ "#{FormatNumber.call(op.brightness)} sethsbcolor\n" end |
#emit_setlinecap(op, buf) ⇒ Object
208 209 210 |
# File 'lib/postsvg/serializer.rb', line 208 def emit_setlinecap(op, buf) buf << "#{op.cap_code} setlinecap\n" end |
#emit_setlinejoin(op, buf) ⇒ Object
212 213 214 |
# File 'lib/postsvg/serializer.rb', line 212 def emit_setlinejoin(op, buf) buf << "#{op.join_code} setlinejoin\n" end |
#emit_setlinewidth(op, buf) ⇒ Object
204 205 206 |
# File 'lib/postsvg/serializer.rb', line 204 def emit_setlinewidth(op, buf) buf << "#{FormatNumber.call(op.width)} setlinewidth\n" end |
#emit_setmiterlimit(op, buf) ⇒ Object
216 217 218 |
# File 'lib/postsvg/serializer.rb', line 216 def emit_setmiterlimit(op, buf) buf << "#{FormatNumber.call(op.limit)} setmiterlimit\n" end |
#emit_setrgbcolor(op, buf) ⇒ Object
Color
181 182 183 |
# File 'lib/postsvg/serializer.rb', line 181 def emit_setrgbcolor(op, buf) buf << "#{FormatNumber.call(op.red)} #{FormatNumber.call(op.green)} #{FormatNumber.call(op.blue)} setrgbcolor\n" end |
#emit_show(op, buf) ⇒ Object
271 272 273 |
# File 'lib/postsvg/serializer.rb', line 271 def emit_show(op, buf) buf << "#{escape_string(string_value(op.text))} show\n" end |
#emit_statement(statement, buffer) ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/postsvg/serializer.rb', line 63 def emit_statement(statement, buffer) case statement when Model::Literals::Number buffer << FormatNumber.call(statement.value) << "\n" when Model::Literals::Name prefix = statement.literal? ? "/" : "" buffer << prefix << statement.value << "\n" when Model::Literals::StringLiteral buffer << escape_string(statement.value) << "\n" when Model::Literals::HexLiteral buffer << "<" << statement.value << ">\n" when Model::Literals::ArrayLiteral buffer << "[ " statement.elements.each { |e| emit_inline(e, buffer); buffer << " " } buffer << "]\n" when Model::Literals::Procedure buffer << "{\n" statement.body.each { |s| emit_statement(s, buffer) } buffer << "}\n" when Model::Literals::Dictionary buffer << "<<\n" statement.entries.each do |k, v| buffer << "/#{k} " emit_inline(v, buffer) buffer << "\n" end buffer << ">>\n" when Model::Operator emit_operator(statement, buffer) when Model::UnknownOperator buffer << "% unhandled operator: #{statement.keyword}\n" when Model::InvokeProcedure buffer << statement.name << "\n" end end |
#emit_string(_op, buf) ⇒ Object
298 |
# File 'lib/postsvg/serializer.rb', line 298 def emit_string(_op, buf) = buf << "string\n" |
#emit_stringwidth(_op, buf) ⇒ Object
279 280 281 |
# File 'lib/postsvg/serializer.rb', line 279 def emit_stringwidth(_op, buf) buf << "% stringwidth: no runtime result captured\n" end |
#emit_stroke(_op, buf) ⇒ Object
Painting
174 |
# File 'lib/postsvg/serializer.rb', line 174 def emit_stroke(_op, buf) = buf << "stroke\n" |
#emit_token(_op, buf) ⇒ Object
297 |
# File 'lib/postsvg/serializer.rb', line 297 def emit_token(_op, buf) = buf << "token\n" |
#emit_translate(op, buf) ⇒ Object
Transformations
231 232 233 |
# File 'lib/postsvg/serializer.rb', line 231 def emit_translate(op, buf) buf << "#{FormatNumber.call(op.tx)} #{FormatNumber.call(op.ty)} translate\n" end |
#emit_xyshow(_op, buf) ⇒ Object
275 276 277 |
# File 'lib/postsvg/serializer.rb', line 275 def emit_xyshow(_op, buf) buf << "% xyshow: per-glyph advances not serialized\n" end |
#escape_string(text) ⇒ Object
249 250 251 252 253 254 255 |
# File 'lib/postsvg/serializer.rb', line 249 def escape_string(text) escaped = text.to_s.gsub(/[\(\)\\]/) { |c| "\\#{c}" } .gsub("\n", "\\n") .gsub("\r", "\\r") .gsub("\t", "\\t") "(#{escaped})" end |
#font_name_value(value) ⇒ Object
307 308 309 310 311 312 313 314 |
# File 'lib/postsvg/serializer.rb', line 307 def font_name_value(value) case value when Model::Literals::Name then value.value when Model::Literals::StringLiteral then value.value when String then value else value.to_s end end |
#string_value(value) ⇒ Object
316 317 318 319 320 321 322 323 |
# File 'lib/postsvg/serializer.rb', line 316 def string_value(value) case value when Model::Literals::StringLiteral then value.value when Model::Literals::HexLiteral then value.bytes when String then value else value.to_s end end |