Class: Pdfrb::Content::Canvas

Inherits:
Object
  • Object
show all
Defined in:
lib/pdfrb/content/canvas.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream, document: nil) ⇒ Canvas

Returns a new instance of Canvas.



8
9
10
11
12
13
14
15
16
# File 'lib/pdfrb/content/canvas.rb', line 8

def initialize(stream, document: nil)
  Pdfrb::Content::Operator.eager_load!
  @stream = stream
  @document = document || stream.document
  @serializer = Pdfrb::Serializer.new
  @used_fonts = {}
  @used_xobjects = {}
  ensure_stream_payload
end

Instance Attribute Details

#documentObject (readonly)

Returns the value of attribute document.



6
7
8
# File 'lib/pdfrb/content/canvas.rb', line 6

def document
  @document
end

#serializerObject (readonly)

Returns the value of attribute serializer.



6
7
8
# File 'lib/pdfrb/content/canvas.rb', line 6

def serializer
  @serializer
end

#streamObject (readonly)

Returns the value of attribute stream.



6
7
8
# File 'lib/pdfrb/content/canvas.rb', line 6

def stream
  @stream
end

#used_fontsObject (readonly)

Returns the value of attribute used_fonts.



6
7
8
# File 'lib/pdfrb/content/canvas.rb', line 6

def used_fonts
  @used_fonts
end

#used_xobjectsObject (readonly)

Returns the value of attribute used_xobjects.



6
7
8
# File 'lib/pdfrb/content/canvas.rb', line 6

def used_xobjects
  @used_xobjects
end

Instance Method Details

#artifact(type = nil) ⇒ Object



311
312
313
314
315
316
317
# File 'lib/pdfrb/content/canvas.rb', line 311

def artifact(type = nil, &)
  if type
    marked_content(:Artifact, { Type: type }, &)
  else
    marked_content(:Artifact, &)
  end
end

#blend_mode=(mode) ⇒ Object



154
155
156
157
# File 'lib/pdfrb/content/canvas.rb', line 154

def blend_mode=(mode)
  emit_op(Pdfrb::Content::Operator::ApplyExtGState,
          create_ext_g_state(BM: mode.to_s))
end

#clipObject



112
113
114
115
116
# File 'lib/pdfrb/content/canvas.rb', line 112

def clip
  emit_op(Pdfrb::Content::Operator::ClipNonZero)
  emit_op(Pdfrb::Content::Operator::EndPath)
  self
end

#clip_even_oddObject



118
119
120
121
122
# File 'lib/pdfrb/content/canvas.rb', line 118

def clip_even_odd
  emit_op(Pdfrb::Content::Operator::ClipEvenOdd)
  emit_op(Pdfrb::Content::Operator::EndPath)
  self
end

#close_pathObject



81
82
83
84
# File 'lib/pdfrb/content/canvas.rb', line 81

def close_path
  emit_op(Pdfrb::Content::Operator::ClosePath)
  self
end

#concat(a, b, c, d, e, f) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/pdfrb/content/canvas.rb', line 45

def concat(a, b, c, d, e, f, &)
  emit_op Pdfrb::Content::Operator::ConcatMatrix, a, b, c, d, e, f
  return self unless block_given?

  begin
    yield self
  ensure
    emit_op Pdfrb::Content::Operator::SaveGraphicsState
  end
  self
end

#curve_to(c1x, c1y, c2x, c2y, x, y) ⇒ Object



71
72
73
74
# File 'lib/pdfrb/content/canvas.rb', line 71

def curve_to(c1x, c1y, c2x, c2y, x, y)
  emit_op(Pdfrb::Content::Operator::CurveTo, c1x, c1y, c2x, c2y, x, y)
  self
end

#dash_pattern=(spec) ⇒ Object



277
278
279
280
# File 'lib/pdfrb/content/canvas.rb', line 277

def dash_pattern=(spec)
  array, phase = spec.is_a?(::Array) ? spec : [spec, 0]
  emit_op(Pdfrb::Content::Operator::DashPattern, array, phase)
end

#draw_form_xobject(name, at: [0, 0], matrix: nil) ⇒ Object

Draw a Form XObject by resource name at (x, y) with an optional matrix transform. Public API for page stamping, form flattening, and appearance-stream embedding.

Parameters:

  • name (Symbol)

    the resource name in /Resources /XObject.

  • at (Array<Numeric>) (defaults to: [0, 0])

    x, y translation.

  • matrix (Array<Numeric>, nil) (defaults to: nil)

    6-element transform matrix.



203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/pdfrb/content/canvas.rb', line 203

def draw_form_xobject(name, at: [0, 0], matrix: nil)
  @used_xobjects[name] = true
  save_graphics_state do
    if matrix
      a, b, c, d, e, f = matrix
      concat(a, b, c, d, e, f)
    elsif at
      translate(at[0], at[1])
    end
    emit_op(Pdfrb::Content::Operator::InvokeXObject, name)
  end
  self
end

#draw_image(name, at: nil, width: nil, height: nil, matrix: nil) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/pdfrb/content/canvas.rb', line 172

def draw_image(name, at: nil, width: nil, height: nil, matrix: nil)
  @used_xobjects[name] = true
  save_graphics_state do
    if matrix
      a, b, c, d, e, f = matrix
      concat(a, b, c, d, e, f)
      emit_op(Pdfrb::Content::Operator::InvokeXObject, name)
    else
      translate(at[0], at[1])
      concat(width, 0, 0, height, 0, 0)
      append(" /#{name} Do\n")
    end
  end
  self
end

#draw_image_matrix(name, a:, b:, c:, d:, e:, f:) ⇒ Object



188
189
190
191
192
193
194
195
# File 'lib/pdfrb/content/canvas.rb', line 188

def draw_image_matrix(name, a:, b:, c:, d:, e:, f:)
  @used_xobjects[name] = true
  save_graphics_state do
    concat(a, b, c, d, e, f)
    emit_op(Pdfrb::Content::Operator::InvokeXObject, name)
  end
  self
end

#emit_op(op_class, *operands) ⇒ Object



335
336
337
338
# File 'lib/pdfrb/content/canvas.rb', line 335

def emit_op(op_class, *operands)
  bytes = op_class.serialize(@serializer, *operands)
  append(bytes)
end

#end_marked_contentObject



299
300
301
302
# File 'lib/pdfrb/content/canvas.rb', line 299

def end_marked_content
  emit_op(Pdfrb::Content::Operator::EndMarkedContent)
  self
end

#end_pathObject



107
108
109
110
# File 'lib/pdfrb/content/canvas.rb', line 107

def end_path
  emit_op(Pdfrb::Content::Operator::EndPath)
  self
end

#fill(rule: :nonzero) ⇒ Object



91
92
93
94
95
96
97
# File 'lib/pdfrb/content/canvas.rb', line 91

def fill(rule: :nonzero)
  op = rule == :even_odd ?
         Pdfrb::Content::Operator::FillEvenOdd :
         Pdfrb::Content::Operator::FillNonZero
  emit_op(op)
  self
end

#fill_color(color) ⇒ Object



129
130
131
132
133
134
135
136
137
# File 'lib/pdfrb/content/canvas.rb', line 129

def fill_color(color)
  case color
  in [Symbol | String => family, *rest]
    emit_color_op(true, family.to_sym, rest)
  else
    emit_color_op(true, :gray, [color])
  end
  self
end

#fill_shading(name) ⇒ Object



124
125
126
127
# File 'lib/pdfrb/content/canvas.rb', line 124

def fill_shading(name)
  append(" /#{name} sh\n")
  self
end

#fill_stroke(rule: :nonzero) ⇒ Object



99
100
101
102
103
104
105
# File 'lib/pdfrb/content/canvas.rb', line 99

def fill_stroke(rule: :nonzero)
  op = rule == :even_odd ?
         Pdfrb::Content::Operator::FillStrokeEvenOdd :
         Pdfrb::Content::Operator::FillStrokeNonZero
  emit_op(op)
  self
end

#line(x1, y1, x2, y2) ⇒ Object



67
68
69
# File 'lib/pdfrb/content/canvas.rb', line 67

def line(x1, y1, x2, y2)
  move_to(x1, y1).line_to(x2, y2)
end

#line_cap=(n) ⇒ Object



265
266
267
# File 'lib/pdfrb/content/canvas.rb', line 265

def line_cap=(n)
  emit_op(Pdfrb::Content::Operator::LineCap, n)
end

#line_join=(n) ⇒ Object



269
270
271
# File 'lib/pdfrb/content/canvas.rb', line 269

def line_join=(n)
  emit_op(Pdfrb::Content::Operator::LineJoin, n)
end

#line_to(x, y) ⇒ Object



62
63
64
65
# File 'lib/pdfrb/content/canvas.rb', line 62

def line_to(x, y)
  emit_op(Pdfrb::Content::Operator::LineTo, x, y)
  self
end

#line_width=(n) ⇒ Object



261
262
263
# File 'lib/pdfrb/content/canvas.rb', line 261

def line_width=(n)
  emit_op(Pdfrb::Content::Operator::LineWidth, n)
end

#marked_content(tag, properties = nil) ⇒ Object



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/pdfrb/content/canvas.rb', line 282

def marked_content(tag, properties = nil, &)
  if properties
    emit_op(Pdfrb::Content::Operator::BeginMarkedContentWithProperties,
            tag, properties)
  else
    emit_op(Pdfrb::Content::Operator::BeginMarkedContent, tag)
  end
  return self unless block_given?

  begin
    yield self
  ensure
    emit_op(Pdfrb::Content::Operator::EndMarkedContent)
  end
  self
end

#miter_limit=(n) ⇒ Object



273
274
275
# File 'lib/pdfrb/content/canvas.rb', line 273

def miter_limit=(n)
  emit_op(Pdfrb::Content::Operator::MiterLimit, n)
end

#move_to(x, y) ⇒ Object



57
58
59
60
# File 'lib/pdfrb/content/canvas.rb', line 57

def move_to(x, y)
  emit_op(Pdfrb::Content::Operator::MoveTo, x, y)
  self
end

#opacity=(alpha) ⇒ Object



149
150
151
152
# File 'lib/pdfrb/content/canvas.rb', line 149

def opacity=(alpha)
  emit_op(Pdfrb::Content::Operator::ApplyExtGState,
          create_ext_g_state(ca: alpha, CA: alpha))
end

#populate_resources!(page) ⇒ Object



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/pdfrb/content/canvas.rb', line 319

def populate_resources!(page)
  r = page.value[:Resources]
  r = {} unless r.is_a?(::Hash)
  unless @used_fonts.empty?
    fd = r[:Font] || {}
    @used_fonts.each_key { |n| fd[n] = fd[n] || n }
    r[:Font] = fd
  end
  unless @used_xobjects.empty?
    xd = r[:XObject] || {}
    @used_xobjects.each_key { |n| xd[n] = xd[n] || n }
    r[:XObject] = xd
  end
  page.value[:Resources] = r
end

#rectangle(x, y, width, height) ⇒ Object



76
77
78
79
# File 'lib/pdfrb/content/canvas.rb', line 76

def rectangle(x, y, width, height)
  emit_op(Pdfrb::Content::Operator::Rectangle, x, y, width, height)
  self
end

#rotate(angle_in_radians) ⇒ Object



39
40
41
42
43
# File 'lib/pdfrb/content/canvas.rb', line 39

def rotate(angle_in_radians, &)
  c = Math.cos(angle_in_radians)
  s = Math.sin(angle_in_radians)
  concat(c, s, -s, c, 0, 0, &)
end

#save_graphics_stateObject Also known as: with_graphics_state



18
19
20
21
22
23
24
25
26
27
28
# File 'lib/pdfrb/content/canvas.rb', line 18

def save_graphics_state(&)
  emit_op Pdfrb::Content::Operator::SaveGraphicsState
  if block_given?
    begin
      yield self
    ensure
      emit_op Pdfrb::Content::Operator::RestoreGraphicsState
    end
  end
  self
end

#scale(sx, sy = sx) ⇒ Object



35
36
37
# File 'lib/pdfrb/content/canvas.rb', line 35

def scale(sx, sy = sx, &)
  concat(sx, 0, 0, sy, 0, 0, &)
end

#strokeObject



86
87
88
89
# File 'lib/pdfrb/content/canvas.rb', line 86

def stroke
  emit_op(Pdfrb::Content::Operator::Stroke)
  self
end

#stroke_color(color) ⇒ Object



139
140
141
142
143
144
145
146
147
# File 'lib/pdfrb/content/canvas.rb', line 139

def stroke_color(color)
  case color
  in [Symbol | String => family, *rest]
    emit_color_op(false, family.to_sym, rest)
  else
    emit_color_op(false, :gray, [color])
  end
  self
end

#tagged(tag, mcid: nil, **props) ⇒ Object



304
305
306
307
308
309
# File 'lib/pdfrb/content/canvas.rb', line 304

def tagged(tag, mcid: nil, **props, &)
  p = props.dup
  p[:MCID] = mcid if mcid
  p = nil if p.empty?
  marked_content(tag, p, &)
end

#text(str, at:, font:, size:, char_spacing: nil, word_spacing: nil) ⇒ Object



217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/pdfrb/content/canvas.rb', line 217

def text(str, at:, font:, size:, char_spacing: nil, word_spacing: nil)
  @used_fonts[font] = size
  encoded = encode_for_font(str.to_s, font)
  emit_op(Pdfrb::Content::Operator::BeginText)
  emit_op(Pdfrb::Content::Operator::SetTextMatrix, 1, 0, 0, 1, at[0], at[1])
  emit_op(Pdfrb::Content::Operator::Font, font, size)
  emit_op(Pdfrb::Content::Operator::CharSpacing, char_spacing) if char_spacing
  emit_op(Pdfrb::Content::Operator::WordSpacing, word_spacing) if word_spacing
  emit_op(Pdfrb::Content::Operator::ShowText, encoded)
  emit_op(Pdfrb::Content::Operator::EndText)
  self
end

#text_lines(lines, font:, size:, at:, leading: nil, char_spacing: nil, word_spacing: nil) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
# File 'lib/pdfrb/content/canvas.rb', line 230

def text_lines(lines, font:, size:, at:, leading: nil, char_spacing: nil,
               word_spacing: nil)
  lead = leading || (size * 1.2)
  x, y = at
  lines.each do |line|
    text(line, at: [x, y], font: font, size: size,
               char_spacing: char_spacing, word_spacing: word_spacing)
    y -= lead
  end
  self
end

#text_rich(runs, at:) ⇒ Object



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/pdfrb/content/canvas.rb', line 242

def text_rich(runs, at:)
  emit_op(Pdfrb::Content::Operator::BeginText)
  cx, cy = at
  runs.each do |run|
    @used_fonts[run[:font]] = run[:size]
    emit_op(Pdfrb::Content::Operator::SetTextMatrix, 1, 0, 0, 1, cx, cy)
    emit_op(Pdfrb::Content::Operator::Font, run[:font], run[:size])
    fill_color(run[:color]) if run[:color]
    emit_op(Pdfrb::Content::Operator::ShowText,
            encode_for_font(run[:text].to_s, run[:font]))
    advance = @document&.fonts&.measure_text(
      run[:text], font: run[:font], size: run[:size]
    ) || 0
    cx += advance
  end
  emit_op(Pdfrb::Content::Operator::EndText)
  self
end

#translate(tx, ty) ⇒ Object



31
32
33
# File 'lib/pdfrb/content/canvas.rb', line 31

def translate(tx, ty, &)
  concat(1, 0, 0, 1, tx, ty, &)
end

#with_transparency(opacity: 1.0, blend_mode: nil) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/pdfrb/content/canvas.rb', line 159

def with_transparency(opacity: 1.0, blend_mode: nil)
  save_graphics_state
  self.opacity = opacity if opacity < 1.0
  self.blend_mode = blend_mode if blend_mode

  begin
    yield self
  ensure
    emit_op(Pdfrb::Content::Operator::RestoreGraphicsState)
  end
  self
end