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.



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

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.



15
16
17
# File 'lib/pdf/core/renderer.rb', line 15

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("%.3f %.3f m" % [ x1, y1 ])  # move
pdf.add_content("%.3f %.3f l" % [ x2, y2 ])  # draw path
pdf.add_content("S") # stroke


54
55
56
57
# File 'lib/pdf/core/renderer.rb', line 54

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.



75
76
77
# File 'lib/pdf/core/renderer.rb', line 75

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

#close_graphics_stateObject



227
228
229
# File 'lib/pdf/core/renderer.rb', line 227

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)


243
244
245
# File 'lib/pdf/core/renderer.rb', line 243

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().



42
43
44
# File 'lib/pdf/core/renderer.rb', line 42

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

#finalize_all_page_contentsObject



129
130
131
132
133
134
135
136
137
# File 'lib/pdf/core/renderer.rb', line 129

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(k) ⇒ 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.



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

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

#graphic_stackObject



258
259
260
# File 'lib/pdf/core/renderer.rb', line 258

def graphic_stack
  state.page.stack
end

#graphic_stateObject



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

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.



143
144
145
# File 'lib/pdf/core/renderer.rb', line 143

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.



63
64
65
# File 'lib/pdf/core/renderer.rb', line 63

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)


69
70
71
# File 'lib/pdf/core/renderer.rb', line 69

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.



81
82
83
84
85
86
87
# File 'lib/pdf/core/renderer.rb', line 81

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

#open_graphics_stateObject



223
224
225
# File 'lib/pdf/core/renderer.rb', line 223

def open_graphics_state
  add_content "q"
end

#page_countObject



115
116
117
# File 'lib/pdf/core/renderer.rb', line 115

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::PdfObject() can convert.

Returns the identifier which points to the reference in the ObjectStore



22
23
24
# File 'lib/pdf/core/renderer.rb', line 22

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



34
35
36
# File 'lib/pdf/core/renderer.rb', line 34

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.



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/pdf/core/renderer.rb', line 150

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



191
192
193
# File 'lib/pdf/core/renderer.rb', line 191

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

#render_file(filename) ⇒ Object

Renders the PDF document to file.

pdf.render_file "foo.pdf"


173
174
175
# File 'lib/pdf/core/renderer.rb', line 173

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



179
180
181
182
183
184
185
186
187
# File 'lib/pdf/core/renderer.rb', line 179

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



210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/pdf/core/renderer.rb', line 210

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::PdfObject(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



197
198
199
200
201
202
203
204
205
206
# File 'lib/pdf/core/renderer.rb', line 197

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



249
250
251
252
253
254
255
256
# File 'lib/pdf/core/renderer.rb', line 249

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



231
232
233
234
235
236
237
238
# File 'lib/pdf/core/renderer.rb', line 231

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



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/pdf/core/renderer.rb', line 89

def start_new_page(options = {})
  if last_page = state.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
    new_graphic_state = last_page.graphic_state.dup  if last_page.graphic_state

    #erase the color space so that it gets reset on new page for fussy pdf-readers
    new_graphic_state.color_space = {} if new_graphic_state
    page_options.merge!(: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