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



237
238
239
# File 'lib/pdf/core/renderer.rb', line 237

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)


253
254
255
# File 'lib/pdf/core/renderer.rb', line 253

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



268
269
270
# File 'lib/pdf/core/renderer.rb', line 268

def graphic_stack
  state.page.stack
end

#graphic_stateObject



272
273
274
275
# File 'lib/pdf/core/renderer.rb', line 272

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



233
234
235
# File 'lib/pdf/core/renderer.rb', line 233

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
# 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)
    str
  end
end

#render_body(output) ⇒ Object

Write out the PDF Body, as per spec 3.4.2



199
200
201
# File 'lib/pdf/core/renderer.rb', line 199

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

#render_file(filename) ⇒ Object

Renders the PDF document to file.

pdf.render_file 'foo.pdf'


181
182
183
# File 'lib/pdf/core/renderer.rb', line 181

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



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

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



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

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



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

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('%<offset>010d', offset: 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



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

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



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

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