Class: PDF::Core::Renderer

Inherits:
Object
  • Object
show all
Defined in:
lib/pdf/core/renderer.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(state) ⇒ Renderer

Returns a new instance of Renderer.



8
9
10
11
12
13
14
15
# File 'lib/pdf/core/renderer.rb', line 8

def initialize(state)
  @state = state
  @state.populate_pages_from_store(self)

  min_version(state.store.min_version) if state.store.min_version

  @page_number = 0
end

Instance Attribute Details

#stateObject (readonly)

Returns the value of attribute state.



17
18
19
# File 'lib/pdf/core/renderer.rb', line 17

def state
  @state
end

Instance Method Details

#add_content(str) ⇒ Object

Appends a raw string to the current page content.

# Raw line drawing example:
x1,y1,x2,y2 = 100,500,300,550

pdf.add_content("#{PDF::Core.real_params([x1, y1])} m")   # move
pdf.add_content("#{PDF::Core.real_params([ x2, y2 ])} l") # draw path
pdf.add_content('S') # stroke


57
58
59
60
# File 'lib/pdf/core/renderer.rb', line 57

def add_content(str)
  save_graphics_state if graphic_state.nil?
  state.page.content << str << "\n"
end

#before_render(&block) ⇒ Object

Defines a block to be called just before the document is rendered.



78
79
80
# File 'lib/pdf/core/renderer.rb', line 78

def before_render(&block)
  state.before_render_callbacks << block
end

#close_graphics_stateObject



239
240
241
# File 'lib/pdf/core/renderer.rb', line 239

def close_graphics_state
  add_content 'Q'
end

#compression_enabled?Boolean

Returns true if content streams will be compressed before rendering, false otherwise

Returns:

  • (Boolean)


255
256
257
# File 'lib/pdf/core/renderer.rb', line 255

def compression_enabled?
  state.compress
end

#deref(obj) ⇒ Object

At any stage in the object tree an object can be replaced with an indirect reference. To get access to the object safely, regardless of if it’s hidden behind a Prawn::Reference, wrap it in deref().



44
45
46
# File 'lib/pdf/core/renderer.rb', line 44

def deref(obj)
  obj.is_a?(PDF::Core::Reference) ? obj.data : obj
end

#finalize_all_page_contentsObject



139
140
141
142
143
144
145
146
147
# File 'lib/pdf/core/renderer.rb', line 139

def finalize_all_page_contents
  (1..page_count).each do |i|
    go_to_page i
    while graphic_stack.present?
      restore_graphics_state
    end
    state.page.finalize
  end
end

#go_to_page(page_number) ⇒ Object

Re-opens the page with the given (1-based) page number so that you can draw on it.

See Prawn::Document#number_pages for a sample usage of this capability.



134
135
136
137
# File 'lib/pdf/core/renderer.rb', line 134

def go_to_page(page_number)
  @page_number = page_number
  state.page = state.pages[page_number - 1]
end

#graphic_stackObject



270
271
272
# File 'lib/pdf/core/renderer.rb', line 270

def graphic_stack
  state.page.stack
end

#graphic_stateObject



274
275
276
277
# File 'lib/pdf/core/renderer.rb', line 274

def graphic_state
  save_graphics_state unless graphic_stack.current_state
  graphic_stack.current_state
end

#min_version(min) ⇒ Object

raise the PDF version of the file we’re going to generate. A private method, designed for internal use when the user adds a feature to their document that requires a particular version.



153
154
155
# File 'lib/pdf/core/renderer.rb', line 153

def min_version(min)
  state.version = min if min > state.version
end

#namesObject

The Name dictionary (PDF spec 3.6.3) for this document. It is lazily initialized, so that documents that do not need a name dictionary do not incur the additional overhead.



66
67
68
# File 'lib/pdf/core/renderer.rb', line 66

def names
  state.store.root.data[:Names] ||= ref!(Type: :Names)
end

#names?Boolean

Returns true if the Names dictionary is in use for this document.

Returns:

  • (Boolean)


72
73
74
# File 'lib/pdf/core/renderer.rb', line 72

def names?
  state.store.root.data[:Names]
end

#on_page_create(&block) ⇒ Object

Defines a block to be called just before a new page is started.



84
85
86
87
88
89
# File 'lib/pdf/core/renderer.rb', line 84

def on_page_create(&block)
  state.on_page_create_callback =
    if block_given?
      block
    end
end

#open_graphics_stateObject



235
236
237
# File 'lib/pdf/core/renderer.rb', line 235

def open_graphics_state
  add_content 'q'
end

#page_countObject



125
126
127
# File 'lib/pdf/core/renderer.rb', line 125

def page_count
  state.page_count
end

#ref(data) ⇒ Object

Creates a new Reference and adds it to the Document’s object list. The data argument is anything that Prawn.pdf_object() can convert.

Returns the identifier which points to the reference in the ObjectStore



24
25
26
# File 'lib/pdf/core/renderer.rb', line 24

def ref(data)
  ref!(data).identifier
end

#ref!(data) ⇒ Object

Like ref, but returns the actual reference instead of its identifier.

While you can use this to build up nested references within the object tree, it is recommended to persist only identifiers, and then provide helper methods to look up the actual references in the ObjectStore if needed. If you take this approach, Document::Snapshot will probably work with your extension



36
37
38
# File 'lib/pdf/core/renderer.rb', line 36

def ref!(data)
  state.store.ref(data)
end

#render(output = StringIO.new) ⇒ Object

Renders the PDF document to string. Pass an open file descriptor to render to file.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/pdf/core/renderer.rb', line 160

def render(output = StringIO.new)
  if output.instance_of?(StringIO)
    output.set_encoding(::Encoding::ASCII_8BIT)
  end
  finalize_all_page_contents

  render_header(output)
  render_body(output)
  render_xref(output)
  render_trailer(output)
  if output.instance_of?(StringIO)
    str = output.string
    str.force_encoding(::Encoding::ASCII_8BIT)
    return str
  else
    return nil
  end
end

#render_body(output) ⇒ Object

Write out the PDF Body, as per spec 3.4.2



201
202
203
# File 'lib/pdf/core/renderer.rb', line 201

def render_body(output)
  state.render_body(output)
end

#render_file(filename) ⇒ Object

Renders the PDF document to file.

pdf.render_file 'foo.pdf'


183
184
185
# File 'lib/pdf/core/renderer.rb', line 183

def render_file(filename)
  File.open(filename, 'wb') { |f| render(f) }
end

#render_header(output) ⇒ Object

Write out the PDF Header, as per spec 3.4.1



189
190
191
192
193
194
195
196
197
# File 'lib/pdf/core/renderer.rb', line 189

def render_header(output)
  state.before_render_actions(self)

  # pdf version
  output << "%PDF-#{state.version}\n"

  # 4 binary chars, as recommended by the spec
  output << "%\xFF\xFF\xFF\xFF\n"
end

#render_trailer(output) ⇒ Object

Write out the PDF Trailer, as per spec 3.4.4



220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/pdf/core/renderer.rb', line 220

def render_trailer(output)
  trailer_hash = {
    Size: state.store.size + 1,
    Root: state.store.root,
    Info: state.store.info
  }
  trailer_hash.merge!(state.trailer) if state.trailer

  output << "trailer\n"
  output << PDF::Core.pdf_object(trailer_hash) << "\n"
  output << "startxref\n"
  output << @xref_offset << "\n"
  output << '%%EOF' << "\n"
end

#render_xref(output) ⇒ Object

Write out the PDF Cross Reference Table, as per spec 3.4.3



207
208
209
210
211
212
213
214
215
216
# File 'lib/pdf/core/renderer.rb', line 207

def render_xref(output)
  @xref_offset = output.size
  output << "xref\n"
  output << "0 #{state.store.size + 1}\n"
  output << "0000000000 65535 f \n"
  state.store.each do |ref|
    output.printf('%010d', ref.offset)
    output << " 00000 n \n"
  end
end

#restore_graphics_stateObject

Pops the last saved graphics state off the graphics state stack and restores the state to those values



261
262
263
264
265
266
267
268
# File 'lib/pdf/core/renderer.rb', line 261

def restore_graphics_state
  if graphic_stack.empty?
    raise PDF::Core::Errors::EmptyGraphicStateStack,
      "\n You have reached the end of the graphic state stack"
  end
  close_graphics_state
  graphic_stack.restore_graphic_state
end

#save_graphics_state(graphic_state = nil) ⇒ Object



243
244
245
246
247
248
249
250
# File 'lib/pdf/core/renderer.rb', line 243

def save_graphics_state(graphic_state = nil)
  graphic_stack.save_graphic_state(graphic_state)
  open_graphics_state
  if block_given?
    yield
    restore_graphics_state
  end
end

#start_new_page(options = {}) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/pdf/core/renderer.rb', line 91

def start_new_page(options = {})
  last_page = state.page
  if last_page
    last_page_size    = last_page.size
    last_page_layout  = last_page.layout
    last_page_margins = last_page.margins
  end

  page_options = {
    size: options[:size] || last_page_size,
    layout: options[:layout] || last_page_layout,
    margins: last_page_margins
  }
  if last_page
    if last_page.graphic_state
      new_graphic_state = last_page.graphic_state.dup
    end

    # Erase the color space so that it gets reset on new page for fussy
    # pdf-readers
    if new_graphic_state
      new_graphic_state.color_space = {}
    end
    page_options[:graphic_state] = new_graphic_state
  end

  state.page = PDF::Core::Page.new(self, page_options)

  state.insert_page(state.page, @page_number)
  @page_number += 1

  state.on_page_create_action(self)
end