pdf-wrapper 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,30 @@
1
+ v0.2.0 (15th September 2008)
2
+ - API INCOMPATIBLE CHANGE: render() and render_file() are now deprecated.
3
+ - Use the output parameter of the constructor instead
4
+ - In versions before this, we automatically rendered to a StringIO object. This kept
5
+ things simple, but isn't great for scalability. Better to give the user the option
6
+ of bypassing memory and rendering direct to disk.
7
+ - Particuarly useful in rails apps to avoid the memory footprint of mongrels
8
+ blowing out
9
+ - Added Wrapper#open()
10
+
11
+ v0.1.5 (Unreleased)
12
+ - Fix text wrapping that crosses page boundaries
13
+ - when a new page is started as a result of a text() call, the text
14
+ should continue at the top of the next page, not the y value the
15
+ text started at
16
+
17
+ v0.1.4 (Unreleased)
18
+ - Wrapper#font_size now accepts a block for temporary font size changes
19
+ - Allow the page size to be changed when starting a new page
20
+ - Added support for manually setting table column widths
21
+ - prevents the column widths from growing or shrinking automatically
22
+ - Deprecated Table#table_options in favour of passing options to Table#new
23
+ - Thanks to Zubin Henner for the suggestion
24
+ - Deprecated Table#header_options and Table#headers= in favour of a new method
25
+ that can be used to specify both the content and options of the header row.
26
+ - Thanks to Zubin Henner for the suggestion
27
+
1
28
  v0.1.3 (24th July 2008)
2
29
  - Require the gem version of cairo
3
30
  - >= 1.5. However I recommend >= 1.6.3 to avoid FATAL error under 1.8.7
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rake/testtask'
6
6
  require "rake/gempackagetask"
7
7
  require 'spec/rake/spectask'
8
8
 
9
- PKG_VERSION = "0.1.3"
9
+ PKG_VERSION = "0.2.0"
10
10
  PKG_NAME = "pdf-wrapper"
11
11
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
12
12
 
data/TODO CHANGED
@@ -1,2 +1,4 @@
1
- All TODO's are currently marked inline in the source code
1
+ - Add a method that will start a new page if there isn't X space left on the current page.
2
+ - Wrapper#required_space(50) {}
2
3
 
4
+ There's plenty more TODO's marked inline in the source code
data/examples/cell.rb CHANGED
@@ -5,6 +5,6 @@ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
5
 
6
6
  require 'pdf/wrapper'
7
7
 
8
- pdf = PDF::Wrapper.new(:paper => :A4)
8
+ pdf = PDF::Wrapper.new("wrapper-cell.pdf", :paper => :A4)
9
9
  pdf.cell("Given an index within a layout, determines the positions that of the strong and weak cursors if the insertion point is at that index. The position of each cursor is stored as a zero-width rectangle. The strong cursor location is the location where characters of the directionality equal to the base direction of the layout are inserted. The weak cursor location is the location where characters of the directionality opposite to the base direction of the layout are inserted.", 100, 100, 100, 200, {:border => "", :color => :black, :radius => 10})
10
- pdf.render_to_file("wrapper-cell.pdf")
10
+ pdf.finish
data/examples/image.rb CHANGED
@@ -5,12 +5,14 @@ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
5
 
6
6
  require 'pdf/wrapper'
7
7
 
8
- pdf = PDF::Wrapper.new(:paper => :A4)
8
+ pdf = PDF::Wrapper.new("image.pdf", :paper => :A4)
9
9
  pdf.font("Sans Serif")
10
10
  pdf.color(:black)
11
11
  pdf.text("PDF::Wrapper Supports Images", :alignment => :center)
12
12
  pdf.image(File.dirname(__FILE__) + "/../specs/data/zits.gif")
13
+ pdf.start_new_page
13
14
  pdf.image(File.dirname(__FILE__) + "/../specs/data/google.png", :left => 100, :top => 350)
15
+ pdf.start_new_page
14
16
  pdf.image(File.dirname(__FILE__) + "/../specs/data/stef.jpg", :left => 200, :top => 500)
15
17
  pdf.start_new_page
16
18
  pdf.image(File.dirname(__FILE__) + "/../specs/data/orc.svg", :left => pdf.margin_left, :top => pdf.margin_top, :width => pdf.body_width, :height => pdf.body_height)
@@ -18,4 +20,4 @@ pdf.start_new_page
18
20
  pdf.image(File.dirname(__FILE__) + "/../specs/data/utf8-long.pdf", :left => pdf.margin_left, :top => pdf.margin_top, :width => pdf.body_width/2, :height => pdf.body_height/2)
19
21
  pdf.color(:red)
20
22
 
21
- pdf.render_to_file("image.pdf")
23
+ pdf.finish
data/examples/markup.rb CHANGED
@@ -5,8 +5,8 @@ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
5
 
6
6
  require 'pdf/wrapper'
7
7
 
8
- pdf = PDF::Wrapper.new(:paper => :A4)
8
+ pdf = PDF::Wrapper.new("markup.pdf", :paper => :A4)
9
9
  pdf.font("Sans Serif")
10
10
  pdf.color(:black)
11
11
  pdf.text "<i>James Healy</i>", :font => "Monospace", :font_size => 16, :alignment => :center, :markup => :pango
12
- pdf.render_to_file("markup.pdf")
12
+ pdf.finish
@@ -5,7 +5,7 @@ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
5
 
6
6
  require 'pdf/wrapper'
7
7
 
8
- pdf = PDF::Wrapper.new(:paper => :A4)
8
+ pdf = PDF::Wrapper.new("repeating.pdf", :paper => :A4)
9
9
 
10
10
  pdf.repeating_element(:all) do
11
11
  pdf.text("Page #{pdf.page}!", :left => pdf.margin_left, :top => pdf.margin_top, :font_size => 18, :alignment => :center)
@@ -35,4 +35,4 @@ pdf.start_new_page
35
35
  pdf.start_new_page
36
36
  pdf.start_new_page
37
37
 
38
- pdf.render_to_file("repeating.pdf")
38
+ pdf.finish
@@ -5,10 +5,10 @@ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
5
 
6
6
  require 'pdf/wrapper'
7
7
 
8
- pdf = PDF::Wrapper.new(:paper => :A4)
8
+ pdf = PDF::Wrapper.new("scaled_image.pdf", :paper => :A4)
9
9
  pdf.image(File.dirname(__FILE__) + "/../specs/data/zits.gif", :top => 100, :height => 200, :width => 200, :proportional => true, :center => true)
10
10
  pdf.rectangle(pdf.margin_left, 100, 200, 200)
11
11
  pdf.image(File.dirname(__FILE__) + "/../specs/data/windmill.jpg", :top => 400, :height => 200, :width => 200, :proportional => true, :center => true)
12
12
  pdf.rectangle(pdf.margin_left, 400, 200, 200)
13
13
 
14
- pdf.render_to_file("scaled_image.pdf")
14
+ pdf.finish
data/examples/shapes.rb CHANGED
@@ -5,9 +5,9 @@ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
5
 
6
6
  require 'pdf/wrapper'
7
7
 
8
- pdf = PDF::Wrapper.new(:paper => :A4)
8
+ pdf = PDF::Wrapper.new("shapes.pdf", :paper => :A4)
9
9
  pdf.rectangle(30,30,100,100, :fill_color => :red)
10
10
  pdf.circle(100,300,30)
11
11
  pdf.line(100, 350, 400, 150)
12
12
  pdf.rectangle(300,300, 200, 200, :fill_color => :green, :radius => 10)
13
- pdf.render_to_file("shapes.pdf")
13
+ pdf.finish
data/examples/table.rb CHANGED
@@ -5,7 +5,7 @@ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
5
 
6
6
  require 'pdf/wrapper'
7
7
 
8
- pdf = PDF::Wrapper.new(:paper => :A4)
8
+ pdf = PDF::Wrapper.new("table.pdf", :paper => :A4)
9
9
  pdf.text File.read(File.dirname(__FILE__) + "/../specs/data/utf8.txt").strip, :alignment => :centre
10
10
  pdf.pad 5
11
11
  headers = %w{one two three four}
@@ -20,11 +20,9 @@ data << [[], "j", "a", "m"]
20
20
  data << %w{1 2 3 4}
21
21
  end
22
22
 
23
- table = PDF::Wrapper::Table.new do |t|
23
+ table = PDF::Wrapper::Table.new(:font_size => 10) do |t|
24
24
  t.data = data
25
- t.headers = headers
26
- t.table_options :font_size => 10
27
- t.header_options :color => :white, :fill_color => :black
25
+ t.headers headers, {:color => :white, :fill_color => :black}
28
26
  t.row_options 6, {:border => "t"}
29
27
  t.row_options :even, {:fill_color => :gray}
30
28
  t.col_options 0, {:border => "tb"}
@@ -36,4 +34,4 @@ table = PDF::Wrapper::Table.new do |t|
36
34
  end
37
35
 
38
36
  pdf.table(table)
39
- pdf.render_to_file("table.pdf")
37
+ pdf.finish
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
+
6
+ require 'pdf/wrapper'
7
+
8
+ pdf = PDF::Wrapper.new("table-fixed.pdf", :paper => :A4)
9
+ pdf.text File.read(File.dirname(__FILE__) + "/../specs/data/utf8.txt").strip, :alignment => :centre
10
+ pdf.pad 5
11
+ headers = %w{one two three four}
12
+
13
+ data = []
14
+ data << ["This is some longer text to ensure...",2,3,4]
15
+ data << ["This is some longer text to ensure...",2,3,4]
16
+
17
+ table = PDF::Wrapper::Table.new do |t|
18
+ t.data = data
19
+ t.headers = headers
20
+ t.table_options :font_size => 10
21
+ t.header_options :color => :white, :fill_color => :black
22
+ t.manual_col_width [1,2,3], 25
23
+ end
24
+
25
+ pdf.table(table)
26
+ pdf.finish
@@ -5,7 +5,7 @@ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
5
 
6
6
  require 'pdf/wrapper'
7
7
 
8
- @pdf = PDF::Wrapper.new(:paper => :A4)
8
+ @pdf = PDF::Wrapper.new("translate.pdf", :paper => :A4)
9
9
 
10
10
  def captioned_image(filename, caption, x, y)
11
11
  @pdf.translate(x, y) do
@@ -18,4 +18,4 @@ captioned_image(File.dirname(__FILE__) + "/../specs/data/orc.svg", "One", 100, 1
18
18
  captioned_image(File.dirname(__FILE__) + "/../specs/data/orc.svg", "Two", 250, 300)
19
19
  captioned_image(File.dirname(__FILE__) + "/../specs/data/orc.svg", "Three", 400, 500)
20
20
 
21
- @pdf.render_to_file("translate.pdf")
21
+ @pdf.finish
@@ -5,7 +5,7 @@ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
5
 
6
6
  require 'pdf/wrapper'
7
7
 
8
- pdf = PDF::Wrapper.new(:paper => :A4)
8
+ pdf = PDF::Wrapper.new("utf8-long.pdf", :paper => :A4)
9
9
  pdf.font("Sans Serif")
10
- pdf.text File.read(File.dirname(__FILE__) + "/../specs/data/utf8-long.txt"), :font => "Monospace", :font_size => 8
11
- pdf.render_to_file("utf8-long.pdf")
10
+ pdf.text File.read(File.dirname(__FILE__) + "/../specs/data/utf8-long.txt"), :font => "Monospace", :font_size => 8, :top => 300
11
+ pdf.finish
data/examples/utf8.rb CHANGED
@@ -5,7 +5,7 @@ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
5
 
6
6
  require 'pdf/wrapper'
7
7
 
8
- pdf = PDF::Wrapper.new(:paper => :A4)
8
+ pdf = PDF::Wrapper.new("wrapper.pdf", :paper => :A4)
9
9
  pdf.font("Sans Serif")
10
10
  pdf.text File.read(File.dirname(__FILE__) + "/../specs/data/utf8.txt"), :font => "Monospace", :font_size => 8, :alignment => :center
11
- pdf.render_to_file("wrapper.pdf")
11
+ pdf.finish
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
5
+
6
+ require 'pdf/wrapper'
7
+
8
+ PDF::Wrapper.open("page_sizes.pdf", :paper => :A4) do |pdf|
9
+ pdf.rectangle(30,30,100,100, :fill_color => :red)
10
+ pdf.circle(100,300,30)
11
+
12
+ pdf.start_new_page(:orientation => :landscape)
13
+ pdf.line(100, 350, 400, 150)
14
+ pdf.rectangle(300,300, 200, 200, :fill_color => :green, :radius => 10)
15
+ end
data/lib/pdf/wrapper.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  require 'stringio'
4
4
  require 'pdf/core'
5
5
  require 'pdf/errors'
6
+ require 'tempfile'
7
+ require 'fileutils'
6
8
 
7
9
  require File.dirname(__FILE__) + "/wrapper/graphics"
8
10
  require File.dirname(__FILE__) + "/wrapper/images"
@@ -18,28 +20,49 @@ require 'cairo'
18
20
  module PDF
19
21
  # Create PDF files by using the cairo and pango libraries.
20
22
  #
21
- # Rendering to a file:
23
+ # == Rendering to a file
22
24
  #
23
25
  # require 'pdf/wrapper'
24
- # pdf = PDF::Wrapper.new(:paper => :A4)
26
+ # pdf = PDF::Wrapper.new("somefile.pdf", :paper => :A4)
25
27
  # pdf.text "Hello World"
26
- # pdf.render_to_file("wrapper.pdf")
28
+ # pdf.finish
27
29
  #
28
- # Rendering to a string:
30
+ # == Rendering to a file (alternative)
29
31
  #
30
32
  # require 'pdf/wrapper'
31
- # pdf = PDF::Wrapper.new(:paper => :A4)
33
+ # File.open("somefile.pdf", "w") do |output|
34
+ # pdf = PDF::Wrapper.new(output, :paper => :A4)
35
+ # pdf.text "Hello World"
36
+ # pdf.finish
37
+ # end
38
+ #
39
+ # == Rendering to a string
40
+ #
41
+ # require 'pdf/wrapper'
42
+ # output = StringIO.new
43
+ # pdf = PDF::Wrapper.new(output, :paper => :A4)
32
44
  # pdf.text "Hello World", :font_size => 16
33
- # puts pdf.render
45
+ # pdf.finish
46
+ # puts output.string
47
+ #
48
+ # == Block format
34
49
  #
35
- # Changing the default font:
50
+ # Avoid the need to call finish()
36
51
  #
37
52
  # require 'pdf/wrapper'
38
- # pdf = PDF::Wrapper.new(:paper => :A4)
53
+ # PDF::Wrapper.open("somefile.pdf", :paper => :A4)
54
+ # pdf.text "Hello World", :font_size => 16
55
+ # end
56
+ #
57
+ # == Changing the default font
58
+ #
59
+ # require 'pdf/wrapper'
60
+ # pdf = PDF::Wrapper.new("file.pdf", :paper => :A4)
39
61
  # pdf.font("Monospace")
40
62
  # pdf.text "Hello World", :font => "Sans Serif", :font_size => 18
41
63
  # pdf.text "Pretend this is a code sample"
42
- # puts pdf.render
64
+ # pdf.finish
65
+ #
43
66
  class Wrapper
44
67
 
45
68
  attr_reader :page
@@ -74,6 +97,9 @@ module PDF
74
97
  }
75
98
 
76
99
  # create a new PDF::Wrapper class to compose a PDF document
100
+ # Params:
101
+ # <tt>output</tt>:: Where to render the PDF to. Can be a string containing a filename,
102
+ # or an IO object (File, StringIO, etc)
77
103
  # Options:
78
104
  # <tt>:paper</tt>:: The paper size to use (default :A4)
79
105
  # <tt>:orientation</tt>:: :portrait (default) or :landscape
@@ -85,14 +111,14 @@ module PDF
85
111
  # <tt>:template</tt>:: The path to an image file. If specified, the first page of the document will use the specified image as a template.
86
112
  # The page will be sized to match the template size. The use templates on subsequent pages, see the options for
87
113
  # start_new_page.
88
- def initialize(opts={})
114
+ def initialize(*args)
89
115
  # TODO: Investigate ways of using the cairo transform/translate/scale functionality to
90
116
  # reduce the amount of irritating co-ordinate maths the user of PDF::Wrapper (ie. me!)
91
117
  # is required to do.
92
118
  # - translate the pdf body width so that it's 1.0 wide and 1.0 high?
93
119
  # TODO: find a way to add metadata (title, author, subject, etc) to the output file
94
120
  # currently no way to specify this in cairo.
95
- # tentatively scheduled for cairo 1.8 - see:
121
+ # tentatively scheduled for cairo 1.10 - see:
96
122
  # - http://cairographics.org/roadmap/
97
123
  # - http://lists.cairographics.org/archives/cairo/2007-September/011441.html
98
124
  # - http://lists.freedesktop.org/archives/cairo/2006-April/006809.html
@@ -100,6 +126,25 @@ module PDF
100
126
  # ensure we have recentish cairo bindings
101
127
  raise "Ruby Cairo bindings version #{Cairo::BINDINGS_VERSION.join(".")} is too low. At least 1.5 is required" if Cairo::BINDINGS_VERSION.to_s < "150"
102
128
 
129
+ if args.size == 0
130
+ opts = {}
131
+ output = StringIO.new
132
+ warn "WARNING: deprecated call to PDF::Wrapper constructor. Check API documentation on new compulsory parameter"
133
+ elsif args.size == 1
134
+ if args.first.kind_of?(Hash)
135
+ opts = *args
136
+ output = StringIO.new
137
+ warn "WARNING: deprecated call to PDF::Wrapper constructor. Check API documentation on new compulsory parameter"
138
+ else
139
+ output = args.first
140
+ opts = {}
141
+ end
142
+ elsif args.size == 2
143
+ output, opts = *args
144
+ else
145
+ raise ArgumentError, 'Invalid parameters passed to constructor'
146
+ end
147
+
103
148
  options = {:paper => :A4,
104
149
  :orientation => :portrait,
105
150
  :background_color => :white
@@ -107,20 +152,10 @@ module PDF
107
152
  options.merge!(opts)
108
153
 
109
154
  # test for invalid options
110
- options.assert_valid_keys(:paper, :orientation, :background_color, :margin_left, :margin_right, :margin_top, :margin_bottom, :template)
111
- options[:paper] = options[:paper].to_sym
112
- raise ArgumentError, "Invalid paper option" unless PAGE_SIZES.include?(options[:paper])
155
+ options.assert_valid_keys(:paper, :orientation, :background_color, :margin_left, :margin_right,
156
+ :margin_top, :margin_bottom, :io, :template)
113
157
 
114
- # set page dimensions
115
- if options[:orientation].eql?(:portrait)
116
- @page_width = PAGE_SIZES[options[:paper]][0]
117
- @page_height = PAGE_SIZES[options[:paper]][1]
118
- elsif options[:orientation].eql?(:landscape)
119
- @page_width = PAGE_SIZES[options[:paper]][1]
120
- @page_height = PAGE_SIZES[options[:paper]][0]
121
- else
122
- raise ArgumentError, "Invalid orientation"
123
- end
158
+ set_dimensions(options[:orientation], options[:paper])
124
159
 
125
160
  # set page margins and dimensions of usable canvas
126
161
  @margin_left = options[:margin_left] || (@page_width * 0.05).ceil
@@ -129,7 +164,7 @@ module PDF
129
164
  @margin_bottom = options[:margin_bottom] || (@page_height * 0.05).ceil
130
165
 
131
166
  # initialize some cairo objects to draw on
132
- @output = StringIO.new
167
+ @output = output
133
168
  @surface = Cairo::PDFSurface.new(@output, @page_width, @page_height)
134
169
  @context = Cairo::Context.new(@surface)
135
170
 
@@ -158,6 +193,21 @@ module PDF
158
193
  reset_cursor
159
194
  end
160
195
 
196
+ # convenience method, takes the same arguments as the constructor along with a block,
197
+ # and automatically finishes the PDF for you.
198
+ #
199
+ #= Usage
200
+ #
201
+ # PDF::Wrapper.open("somefile.pdf") do |pdf|
202
+ # pdf.text "hi!"
203
+ # end
204
+ #
205
+ def self.open(output, options = {}, &block)
206
+ pdf = PDF::Wrapper.new(output, options)
207
+ yield pdf
208
+ pdf.finish
209
+ end
210
+
161
211
  #####################################################
162
212
  # Functions relating to calculating various page dimensions
163
213
  #####################################################
@@ -231,6 +281,14 @@ module PDF
231
281
  @context.current_point
232
282
  end
233
283
 
284
+ def x
285
+ @context.current_point.first
286
+ end
287
+
288
+ def y
289
+ @context.current_point.last
290
+ end
291
+
234
292
  def margin_bottom
235
293
  device_y_to_user_y(@margin_bottom).to_i
236
294
  end
@@ -305,27 +363,30 @@ module PDF
305
363
 
306
364
  # render the PDF and return it as a string
307
365
  def render
308
- # finalise the document, then convert the StringIO object it was rendered to
309
- # into a string
366
+ # TODO: remove this method at some point. Deprecation started on 15th September 2008
367
+ warn "WARNING: render() is deprecated. See documentation for PDF::Wrapper#initialize for more information"
310
368
  finish
311
- return @output.string
312
- end
313
-
314
- def render_to_file(filename) #nodoc
315
- # TODO: remove this at some point
316
- warn "WARNING: render_to_file() is deprecated, please use render_file()"
317
- render_file filename
369
+ case @output
370
+ when StringIO then return @output.string
371
+ when File then return File.read(@output.path)
372
+ else
373
+ return File.read(@output)
374
+ end
318
375
  end
319
376
 
320
377
  # save the rendered PDF to a file
321
378
  def render_file(filename)
379
+ # TODO: remove this method at some point. Deprecation started on 15th September 2008
380
+ warn "WARNING: render_file() is deprecated. See documentation for PDF::Wrapper#initialize for more information"
322
381
  finish
323
-
324
- # write each line from the StringIO object it was rendered to into the
325
- # requested file
326
- File.open(filename, "w") do |of|
327
- @output.rewind
328
- @output.each_line { |line| of.write(line) }
382
+ case @output
383
+ when StringIO then
384
+ File.open(filename, "w") do |of|
385
+ of.write(@output.string)
386
+ end
387
+ when File then return FileUtils.cp(@output.path, filename)
388
+ else
389
+ return FileUtils.cp(@output, filename)
329
390
  end
330
391
  end
331
392
 
@@ -449,13 +510,30 @@ module PDF
449
510
  @context.move_to(margin_left,margin_top)
450
511
  end
451
512
 
513
+ def finish
514
+ # finalise the document
515
+ @context.show_page
516
+ @context.target.finish
517
+ #@output.close if io_output?
518
+ @surface.finish
519
+ #@surface.destroy
520
+ #@context.destroy
521
+ self
522
+ rescue Cairo::SurfaceFinishedError
523
+ # do nothing, we're happy that the surfaced has been finished
524
+ end
525
+
452
526
  # returns true if the PDF has already been rendered, false if it hasn't.
453
527
  # Due to limitations of the underlying libraries, content cannot be
454
528
  # added to a PDF once it has been rendered.
455
529
  #
456
530
  def finished?
457
- @output.seek(@output.size - 6)
458
- bytes = @output.read(6)
531
+ if io_output?
532
+ @output.seek(@output.size - 6)
533
+ bytes = @output.read(6)
534
+ else
535
+ bytes = @output[-6,6]
536
+ end
459
537
  bytes == "%%EOF\n" ? true : false
460
538
  end
461
539
 
@@ -488,11 +566,15 @@ module PDF
488
566
  # move to the next page
489
567
  #
490
568
  # options:
569
+ # <tt>:paper</tt>:: The paper size to use (default: same as the previous page)
570
+ # <tt>:orientation</tt>:: :portrait or :landscape (default: same as the previous page)
491
571
  # <tt>:pageno</tt>:: If specified, the current page number will be set to that. By default, the page number will just increment.
492
572
  # <tt>:template</tt>:: The path to an image file. If specified, the new page will use the specified image as a template. The page will be sized to match the template size
493
573
  #
494
574
  def start_new_page(opts = {})
495
- opts.assert_valid_keys(:pageno, :template)
575
+ opts.assert_valid_keys(:paper, :orientation, :pageno, :template)
576
+
577
+ set_dimensions(opts[:orientation], opts[:paper])
496
578
 
497
579
  @context.show_page
498
580
 
@@ -522,12 +604,31 @@ module PDF
522
604
 
523
605
  private
524
606
 
525
- def finish
526
- # finalise the document
527
- @context.show_page
528
- @context.target.finish
529
- rescue Cairo::SurfaceFinishedError
530
- # do nothing, we're happy that the surfaced has been finished
607
+ def set_dimensions(orientation, paper)
608
+ # use the defaults if none were provided
609
+ orientation ||= @orientation
610
+ paper ||= @paper
611
+
612
+ # safety check
613
+ orientation = orientation.to_sym
614
+ paper = paper.to_sym
615
+
616
+ raise ArgumentError, "Unrecognised paper size (#{paper})" if PAGE_SIZES[paper].nil?
617
+
618
+ # set page dimensions
619
+ if orientation.eql?(:portrait)
620
+ @page_width = PAGE_SIZES[paper][0]
621
+ @page_height = PAGE_SIZES[paper][1]
622
+ elsif orientation.eql?(:landscape)
623
+ @page_width = PAGE_SIZES[paper][1]
624
+ @page_height = PAGE_SIZES[paper][0]
625
+ else
626
+ raise ArgumentError, "Invalid orientation"
627
+ end
628
+
629
+ # make the new values the defaults
630
+ @orientation = orientation
631
+ @paper = paper
531
632
  end
532
633
 
533
634
  # runs the code in block, passing it a hash of options that might be
@@ -557,6 +658,14 @@ module PDF
557
658
  }
558
659
  end
559
660
 
661
+ def io_output?
662
+ if @output.respond_to?(:write) && @output.respond_to?(:read)
663
+ true
664
+ else
665
+ false
666
+ end
667
+ end
668
+
560
669
  # save and restore the cursor position around a block
561
670
  def save_coords(&block)
562
671
  origx, origy = current_point