sandal / prawn

Fast, Nimble PDF Writer for Ruby

This URL has Read+Write access

prawn / lib / prawn / document.rb
aa633b6e » yob 2008-07-02 add content type tags to al... 1 # encoding: utf-8
2
062f5fc2 » sandal 2008-04-30 Gettin' all legal and shiznit 3 # document.rb : Implements PDF document generation for Prawn
4 #
5 # Copyright April 2008, Gregory Brown. All Rights Reserved.
6 #
7 # This is free software. Please see the LICENSE and COPYING files for details.
8
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 9 require "stringio"
61501180 » sandal 2008-05-09 I hope you bastards like He... 10 require "prawn/document/page_geometry"
19d6b864 » Gregory Brown 2008-05-18 Initial work on bounding boxen 11 require "prawn/document/bounding_box"
61501180 » sandal 2008-05-09 I hope you bastards like He... 12 require "prawn/document/text"
3318a51b » sandal 2008-06-09 Starting out on table stuff... 13 require "prawn/document/table"
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 14
412a5348 » sandal 2008-04-23 Basic reference implementation 15 module Prawn
0fcf9898 » sandal 2008-04-29 page size and layout... nee... 16 class Document
17
bc3fe6de » Gregory Brown 2008-05-18 Making Graphics its own mod... 18 include Prawn::Graphics
61501180 » sandal 2008-05-09 I hope you bastards like He... 19 include Text
0fcf9898 » sandal 2008-04-29 page size and layout... nee... 20 include PageGeometry
21
549af3d8 » sandal 2008-06-25 Give better access to margi... 22 attr_accessor :page_size, :page_layout, :y, :margin_box
cca72f34 » sandal 2008-07-02 Low level support for multi... 23 attr_reader :margins
9c5cdb13 » sandal 2008-06-19 Cleaning up the default fon... 24
dfa491df » sandal 2008-05-12 What's that I smell? Sexy ... 25
a3a99869 » sandal 2008-05-26 Filling in the documentatio... 26 # Creates and renders a PDF document.
27 #
dfa491df » sandal 2008-05-12 What's that I smell? Sexy ... 28 # The explicit receiver argument is necessary only when you need to make
29 # use of a closure.
30 #
6cc14ce7 » sandal 2008-06-06 Make the rdoc pretty, so we... 31 # # Using implicit block form and rendering to a file
32 # Prawn::Document.generate "foo.pdf" do
33 # font "Times-Roman"
34 # text "Hello World", :at => [200,720], :size => 32
35 # end
36 #
37 # # Using explicit block form and rendering to a file
38 # content = "Hello World"
39 # Prawn::Document.generate "foo.pdf" do |pdf|
40 # pdf.font "Times-Roman"
41 # pdf.text content, :at => [200,720], :size => 32
42 # end
dfa491df » sandal 2008-05-12 What's that I smell? Sexy ... 43 #
a3a99869 » sandal 2008-05-26 Filling in the documentatio... 44 def self.generate(filename,options={},&block)
45 pdf = Prawn::Document.new(options)
dfa491df » sandal 2008-05-12 What's that I smell? Sexy ... 46 block.arity < 1 ? pdf.instance_eval(&block) : yield(pdf)
a3a99869 » sandal 2008-05-26 Filling in the documentatio... 47 pdf.render_file(filename)
dfa491df » sandal 2008-05-12 What's that I smell? Sexy ... 48 end
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 49
50 # Creates a new PDF Document. The following options are available:
51 #
6cc14ce7 » sandal 2008-06-06 Make the rdoc pretty, so we... 52 # <tt>:page_size</tt>:: One of the Document::PageGeometry::SIZES [LETTER]
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 53 # <tt>:page_layout</tt>:: Either <tt>:portrait</tt> or <tt>:landscape</tt>
54 # <tt>:on_page_start</tt>:: Optional proc run at each page start
a3a99869 » sandal 2008-05-26 Filling in the documentatio... 55 # <tt>:on_page_stop</tt>:: Optional proc run at each page stop
6cc14ce7 » sandal 2008-06-06 Make the rdoc pretty, so we... 56 # <tt>:left_margin</tt>:: Sets the left margin in points [ 0.5 inch]
57 # <tt>:right_margin</tt>:: Sets the right margin in points [ 0.5 inch]
58 # <tt>:top_margin</tt>:: Sets the top margin in points [ 0.5 inch]
59 # <tt>:bottom_margin</tt>:: Sets the bottom margin in points [0.5 inch]
a3a99869 » sandal 2008-05-26 Filling in the documentatio... 60 #
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 61 #
6cc14ce7 » sandal 2008-06-06 Make the rdoc pretty, so we... 62 # # New document, US Letter paper, portrait orientation
63 # pdf = Prawn::Document.new
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 64 #
6cc14ce7 » sandal 2008-06-06 Make the rdoc pretty, so we... 65 # # New document, A4 paper, landscaped
66 # pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape)
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 67 #
9c5cdb13 » sandal 2008-06-19 Cleaning up the default fon... 68 # # New document, draws a line at the start of each new page
6cc14ce7 » sandal 2008-06-06 Make the rdoc pretty, so we... 69 # pdf = Prawn::Document.new(:on_page_start =>
70 # lambda { |doc| doc.line [0,100], [300,100] } )
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 71 #
a3cae07e » sandal 2008-04-26 #7 on_page_start / on_page_... 72 def initialize(options={})
79dfa633 » sandal 2008-04-23 Beginning work on Document ... 73 @objects = []
74 @info = ref(:Creator => "Prawn", :Producer => "Prawn")
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 75 @pages = ref(:Type => :Pages, :Count => 0, :Kids => [])
79dfa633 » sandal 2008-04-23 Beginning work on Document ... 76 @root = ref(:Type => :Catalog, :Pages => @pages)
a3cae07e » sandal 2008-04-26 #7 on_page_start / on_page_... 77 @page_start_proc = options[:on_page_start]
0fcf9898 » sandal 2008-04-29 page size and layout... nee... 78 @page_stop_proc = options[:on_page_end]
79 @page_size = options[:page_size] || "LETTER"
80 @page_layout = options[:page_layout] || :portrait
d7ceab15 » sandal 2008-05-19 Working our way towards fun... 81
cca72f34 » sandal 2008-07-02 Low level support for multi... 82 @margins = { :left => options[:left_margin] || 36,
83 :right => options[:right_margin] || 36,
84 :top => options[:top_margin] || 36,
85 :bottom => options[:bottom_margin] || 36 }
82e24d6f » sandal 2008-05-19 working on bounding box int... 86
cca72f34 » sandal 2008-07-02 Low level support for multi... 87 generate_margin_box
82e24d6f » sandal 2008-05-19 working on bounding box int... 88
89 @bounding_box = @margin_box
e0f09072 » sandal 2008-05-12 Cleanup for text stuff, rem... 90
91 start_new_page
cca72f34 » sandal 2008-07-02 Low level support for multi... 92 end
93
94 def generate_margin_box
95 old_margin_box = @margin_box
96 @margin_box = BoundingBox.new(
97 self,
98 [ @margins[:left], page_dimensions[-1] - @margins[:top] ] ,
99 :width => page_dimensions[-2] - (@margins[:left] + @margins[:right]),
100 :height => page_dimensions[-1] - (@margins[:top] + @margins[:bottom])
101 )
102
103 # update bounding box if not flowing from the previous page
a22c3b56 » sandal 2008-07-21 Cleanup more minor issues 104 # TODO: This may have a bug where the old margin is restored
105 # when the bounding box exits.
cca72f34 » sandal 2008-07-02 Low level support for multi... 106 @bounding_box = @margin_box if old_margin_box == @bounding_box
107 end
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 108
109 # Creates and advances to a new page in the document.
110 # Runs the <tt>:on_page_start</tt> lambda if one was provided at
21d71157 » sandal 2008-06-26 Begin work on API docs (Doc... 111 # document creation time (See Document.new).
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 112 #
79dfa633 » sandal 2008-04-23 Beginning work on Document ... 113 def start_new_page
cca72f34 » sandal 2008-07-02 Low level support for multi... 114 finish_page_content if @page_content
115 generate_margin_box
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 116 @page_content = ref(:Length => 0)
79dfa633 » sandal 2008-04-23 Beginning work on Document ... 117
e0f09072 » sandal 2008-05-12 Cleanup for text stuff, rem... 118 @current_page = ref(:Type => :Page,
119 :Parent => @pages,
120 :MediaBox => page_dimensions,
121 :Contents => @page_content,
122 :ProcSet => font_proc,
123 :Resources => { :Font => {} } )
a9eeef30 » sandal 2008-05-13 Fix a bug with colors, need... 124 set_current_font
125 update_colors
79dfa633 » sandal 2008-04-23 Beginning work on Document ... 126 @pages.data[:Kids] << @current_page
127 @pages.data[:Count] += 1
128
129 add_content "q"
7a2c295f » sandal 2008-05-21 Text flow starting to be us... 130
131 @y = @margin_box.absolute_top
a3cae07e » sandal 2008-04-26 #7 on_page_start / on_page_... 132 @page_start_proc[self] if @page_start_proc
0fcf9898 » sandal 2008-04-29 page size and layout... nee... 133 end
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 134
135 # Returns the number of pages in the document
136 #
6cc14ce7 » sandal 2008-06-06 Make the rdoc pretty, so we... 137 # pdf = Prawn::Document.new
138 # pdf.page_count #=> 1
139 # 3.times { pdf.start_new_page }
140 # pdf.page_count #=> 4
e13def82 » sandal 2008-06-19 Forgot to close ticket (tes... 141 #
e8991fe4 » sandal 2008-04-25 Adding support for Document... 142 def page_count
143 @pages.data[:Count]
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 144 end
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 145
21d71157 » sandal 2008-06-26 Begin work on API docs (Doc... 146 # Renders the PDF document to string
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 147 #
148 def render
24791d9c » yob 2008-07-02 Ensure correct output encod... 149 output = StringIO.new
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 150 finish_page_content
151
3a8ae305 » sandal 2008-04-28 More refactorimng recommend... 152 render_header(output)
153 render_body(output)
154 render_xref(output)
155 render_trailer(output)
24791d9c » yob 2008-07-02 Ensure correct output encod... 156 str = output.string
157 str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
158 str
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 159 end
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 160
161 # Renders the PDF document to file.
162 #
6cc14ce7 » sandal 2008-06-06 Make the rdoc pretty, so we... 163 # pdf.render_file "foo.pdf"
cf4c7990 » sandal 2008-04-30 Documentation for all curre... 164 #
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 165 def render_file(filename)
24791d9c » yob 2008-07-02 Ensure correct output encod... 166 Kernel.const_defined?("Encoding") ? mode = "wb:ASCII-8BIT" : mode = "wb"
167 File.open(filename,mode) { |f| f << render }
70330126 » sandal 2008-05-19 Continued work on flow. So... 168 end
169
a3a99869 » sandal 2008-05-26 Filling in the documentatio... 170 # Returns the current BoundingBox object, which is by default
171 # the box represented by the margin box. When called from within
172 # a <tt>bounding_box</tt> block, the box defined by that call will
173 # be used.
174 #
70330126 » sandal 2008-05-19 Continued work on flow. So... 175 def bounds
176 @bounding_box
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 177 end
3318a51b » sandal 2008-06-09 Starting out on table stuff... 178
21d71157 » sandal 2008-06-26 Begin work on API docs (Doc... 179 # Moves up the document by n points
180 #
7d3f02d7 » sandal 2008-06-09 Some helpers stolen from Ru... 181 def move_up(n)
182 self.y += n
183 end
184
21d71157 » sandal 2008-06-26 Begin work on API docs (Doc... 185 # Moves down the document by n point
186 #
7d3f02d7 » sandal 2008-06-09 Some helpers stolen from Ru... 187 def move_down(n)
188 self.y -= n
189 end
190
21d71157 » sandal 2008-06-26 Begin work on API docs (Doc... 191
192 # Moves down the document and then executes a block.
193 #
194 # pdf.text "some text"
195 # pdf.pad_top(100) do
196 # pdf.text "This is 100 points below the previous line of text"
197 # end
198 # pdf.text "This text appears right below the previous line of text"
199 #
7d3f02d7 » sandal 2008-06-09 Some helpers stolen from Ru... 200 def pad_top(y)
201 move_down(y)
202 yield
203 end
204
21d71157 » sandal 2008-06-26 Begin work on API docs (Doc... 205 # Executes a block then moves down the document
206 #
207 # pdf.text "some text"
208 # pdf.pad_bottom(100) do
209 # pdf.text "This text appears right below the previous line of text"
210 # end
211 # pdf.text "This is 100 points below the previous line of text"
212 #
7d3f02d7 » sandal 2008-06-09 Some helpers stolen from Ru... 213 def pad_bottom(y)
214 yield
215 move_down(y)
216 end
217
21d71157 » sandal 2008-06-26 Begin work on API docs (Doc... 218 # Moves down the document by y, executes a block, then moves down the
219 # document by y again.
220 #
221 # pdf.text "some text"
222 # pdf.pad(100) do
223 # pdf.text "This is 100 points below the previous line of text"
224 # end
225 # pdf.text "This is 100 points below the previous line of text"
226 #
7d3f02d7 » sandal 2008-06-09 Some helpers stolen from Ru... 227 def pad(y)
228 move_down(y)
229 yield
230 move_down(y)
231 end
232
21d71157 » sandal 2008-06-26 Begin work on API docs (Doc... 233
9440fb87 » sandal 2008-06-26 Move table shortcut into ta... 234 def mask(*fields) # :nodoc:
21d71157 » sandal 2008-06-26 Begin work on API docs (Doc... 235 # Stores the current state of the named attributes, executes the block, and
236 # then restores the original values after the block has executed.
237 # -- I will remove the nodoc if/when this feature is a little less hacky
50f85072 » sandal 2008-06-11 Allow border sizes to be sp... 238 stored = {}
239 fields.each { |f| stored[f] = send(f) }
240 yield
241 fields.each { |f| send("#{f}=", stored[f]) }
242 end
7a2c295f » sandal 2008-05-21 Text flow starting to be us... 243
e8991fe4 » sandal 2008-04-25 Adding support for Document... 244 private
50f85072 » sandal 2008-06-11 Allow border sizes to be sp... 245
79dfa633 » sandal 2008-04-23 Beginning work on Document ... 246 def ref(data)
b9c6f20c » sandal 2008-04-25 Optimizing my stroking pote... 247 @objects.push(Prawn::Reference.new(@objects.size + 1, data)).last
0fcf9898 » sandal 2008-04-29 page size and layout... nee... 248 end
79dfa633 » sandal 2008-04-23 Beginning work on Document ... 249
250 def add_content(str)
251 @page_content << str << "\n"
252 end
253
b9c6f20c » sandal 2008-04-25 Optimizing my stroking pote... 254 def finish_page_content
a3cae07e » sandal 2008-04-26 #7 on_page_start / on_page_... 255 @page_stop_proc[self] if @page_stop_proc
79dfa633 » sandal 2008-04-23 Beginning work on Document ... 256 add_content "Q"
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 257 @page_content.data[:Length] = @page_content.stream.size
79dfa633 » sandal 2008-04-23 Beginning work on Document ... 258 end
412a5348 » sandal 2008-04-23 Basic reference implementation 259
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 260 # Write out the PDF Header, as per spec 3.4.1
3a8ae305 » sandal 2008-04-28 More refactorimng recommend... 261 def render_header(output)
b9c6f20c » sandal 2008-04-25 Optimizing my stroking pote... 262 # pdf version
f1804995 » yob 2008-06-06 fixed Type0 fonts on Acroba... 263 output << "%PDF-1.3\n"
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 264
265 # 4 binary chars, as recommended by the spec
3a8ae305 » sandal 2008-04-28 More refactorimng recommend... 266 output << "\xFF\xFF\xFF\xFF\n"
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 267 end
268
269 # Write out the PDF Body, as per spec 3.4.2
3a8ae305 » sandal 2008-04-28 More refactorimng recommend... 270 def render_body(output)
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 271 @objects.each do |ref|
3a8ae305 » sandal 2008-04-28 More refactorimng recommend... 272 ref.offset = output.size
273 output << ref.object
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 274 end
275 end
276
277 # Write out the PDF Cross Reference Table, as per spec 3.4.3
3a8ae305 » sandal 2008-04-28 More refactorimng recommend... 278 def render_xref(output)
279 @xref_offset = output.size
280 output << "xref\n"
281 output << "0 #{@objects.size + 1}\n"
282 output << "0000000000 65535 f \n"
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 283 @objects.each do |ref|
3a8ae305 » sandal 2008-04-28 More refactorimng recommend... 284 output.printf("%010d", ref.offset)
285 output << " 00000 n \n"
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 286 end
287 end
288
289 # Write out the PDF Body, as per spec 3.4.4
3a8ae305 » sandal 2008-04-28 More refactorimng recommend... 290 def render_trailer(output)
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 291 trailer_hash = {:Size => @objects.size + 1,
292 :Root => @root,
293 :Info => @info}
294
3a8ae305 » sandal 2008-04-28 More refactorimng recommend... 295 output << "trailer\n"
296 output << Prawn::PdfObject(trailer_hash) << "\n"
297 output << "startxref\n"
298 output << @xref_offset << "\n"
299 output << "%%EOF"
c4989f4c » sandal 2008-04-24 Finally got a PDF generatin... 300 end
412a5348 » sandal 2008-04-23 Basic reference implementation 301 end
a3cae07e » sandal 2008-04-26 #7 on_page_start / on_page_... 302 end