lib/pdf/wrapper.rb in pdf-wrapper-0.0.6 vs lib/pdf/wrapper.rb in pdf-wrapper-0.0.7
- old
+ new
@@ -85,10 +85,13 @@
# <tt>:background_color</tt>:: The background colour to use (default :white)
# <tt>:margin_top</tt>:: The size of the default top margin (default 5% of page)
# <tt>:margin_bottom</tt>:: The size of the default bottom margin (default 5% of page)
# <tt>:margin_left</tt>:: The size of the default left margin (default 5% of page)
# <tt>:margin_right</tt>:: The size of the default right margin (default 5% of page)
+ # <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.
+ # The page will be sized to match the template size. The use templates on subsequent pages, see the options for
+ # start_new_page.
def initialize(opts={})
# ensure we have recentish cairo bindings
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"
@@ -97,11 +100,11 @@
:background_color => :white
}
options.merge!(opts)
# test for invalid options
- options.assert_valid_keys(:paper, :orientation, :background_color, :margin_left, :margin_right, :margin_top, :margin_bottom)
+ options.assert_valid_keys(:paper, :orientation, :background_color, :margin_left, :margin_right, :margin_top, :margin_bottom, :template)
options[:paper] = options[:paper].to_sym
raise ArgumentError, "Invalid paper option" unless PAGE_SIZES.include?(options[:paper])
# set page dimensions
if options[:orientation].eql?(:portrait)
@@ -137,10 +140,17 @@
# maintain a count of pages and array of repeating elements to add to each page
@page = 1
@repeating = []
+ # build the first page from a template if required
+ if opts[:template]
+ w, h = image_dimensions(opts[:template])
+ @surface.set_size(w, h)
+ image(opts[:template], :left => 0, :top => 0)
+ end
+
# move the cursor to the top left of the usable canvas
reset_cursor
end
#####################################################
@@ -501,12 +511,12 @@
# restore the cursor position
move_to(origx, origy)
end
- # Adds a cubic Bezier spline to the path from the (x0, y0) to position (x3, y3)
- # in user-space coordinates, using (x1, y1) and (x2, y2) as the control points.
+ # Adds a cubic Bezier spline to the path from the (x0, y0) to position (x3, y3)
+ # in user-space coordinates, using (x1, y1) and (x2, y2) as the control points.
# Options:
# <tt>:color</tt>:: The colour of the line
# <tt>:line_width</tt>:: The width of line. Defaults to 2.0
def curve(x0, y0, x1, y1, x2, y2, x3, y3, opts = {})
options = {:color => @default_color, :line_width => @default_line_width }
@@ -520,12 +530,12 @@
@context.curve_to(x1, y1, x2, y2, x3, y3).stroke
# restore the cursor position
move_to(origx, origy)
end
-
+
# draw a rectangle starting at x,y with w,h dimensions.
# Parameters:
# <tt>:x</tt>:: The x co-ordinate of the top left of the rectangle.
# <tt>:y</tt>:: The y co-ordinate of the top left of the rectangle.
# <tt>:w</tt>:: The width of the rectangle
@@ -612,22 +622,27 @@
# <tt>:left</tt>:: The x co-ordinate of the left-hand side of the image.
# <tt>:top</tt>:: The y co-ordinate of the top of the image.
# <tt>:height</tt>:: The height of the image
# <tt>:width</tt>:: The width of the image
# <tt>:proportional</tt>:: Boolean. Maintain image proportions when scaling. Defaults to false.
+ # <tt>:padding</tt>:: Add some padding between the image and the specified box.
#
# left and top default to the current cursor location
# width and height default to the size of the imported image
+ # padding defaults to 0
def image(filename, opts = {})
# TODO: add some options for justification and padding
- # TODO: add a seperate method for adding arbitary pages from a PDF file to this one. Good for
- # templating, etc. Save a letterhead as a PDF file, then open it and add it to the page
- # as a starting point. Until we call start_new_page, we can add stuff over the top of the
- # imported content
raise ArgumentError, "file #{filename} not found" unless File.file?(filename)
- opts.assert_valid_keys(default_positioning_options.keys + [:proportional])
+ opts.assert_valid_keys(default_positioning_options.keys + [:padding, :proportional])
+ if opts[:padding]
+ opts[:left] += opts[:padding].to_i if opts[:left]
+ opts[:top] += opts[:padding].to_i if opts[:top]
+ opts[:width] -= opts[:padding].to_i * 2 if opts[:width]
+ opts[:height] -= opts[:padding].to_i * 2 if opts[:height]
+ end
+
case detect_image_type(filename)
when :pdf then draw_pdf filename, opts
when :png then draw_png filename, opts
when :svg then draw_svg filename, opts
else
@@ -710,18 +725,29 @@
@repeating << {:spec => spec, :block => block}
end
# move to the next page
#
- # arguments:
- # <tt>pageno</tt>:: If specified, the current page number will be set to that. By default, the page number will just increment.
- def start_new_page(pageno = nil)
+ # options:
+ # <tt>:pageno</tt>:: If specified, the current page number will be set to that. By default, the page number will just increment.
+ # <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
+ def start_new_page(opts = {})
+ opts.assert_valid_keys(:pageno, :template)
+
@context.show_page
+ if opts[:template]
+ w, h = image_dimensions(opts[:template])
+ @surface.set_size(w, h)
+ image(opts[:template], :left => 0, :top => 0)
+ else
+ @surface.set_size(@page_width, @page_height)
+ end
+
# reset or increment the page counter
- if pageno
- @page = pageno.to_i
+ if opts[:pageno]
+ @page = opts[:pageno].to_i
else
@page += 1
end
# move the cursor to the top left of our page body
@@ -975,10 +1001,36 @@
end
return y + row_height
end
+ def image_dimensions(filename)
+ raise ArgumentError, "file #{filename} not found" unless File.file?(filename)
+
+ case detect_image_type(filename)
+ when :pdf then
+ load_libpoppler
+ page = Poppler::Document.new(filename).get_page(1)
+ return page.size
+ when :png then
+ img_surface = Cairo::ImageSurface.from_png(filename)
+ return img_surface.width, img_surface.height
+ when :svg then
+ load_librsvg
+ handle = RSVG::Handle.new_from_file(filename)
+ return handle.width, handle.height
+ else
+ load_libpixbuf
+ begin
+ pixbuf = Gdk::Pixbuf.new(filename)
+ return pixbuf.width, pixbuf.height
+ rescue Gdk::PixbufError
+ raise ArgumentError, "Unrecognised image format (#{filename})"
+ end
+ end
+ end
+
# load libpango if it isn't already loaded.
# This will add some methods to the cairo Context class in addition to providing
# its own classes and constants. A small amount of documentation is available at
# http://ruby-gnome2.sourceforge.jp/fr/hiki.cgi?Cairo%3A%3AContext#Pango+related+APIs
def load_libpango
@@ -1036,13 +1088,13 @@
options = {:auto_new_page => true }
options.merge!(opts)
offset = 0
baseline = 0
-
+
iter = layout.iter
- loop do
+ loop do
line = iter.line
ink_rect, logical_rect = iter.line_extents
if y + (baseline - offset) >= (y + h)
# our text is using the maximum amount of vertical space we want it to
if options[:auto_new_page]
@@ -1063,10 +1115,10 @@
# draw the line on the canvas
@context.show_pango_layout_line(line)
break unless iter.next_line!
- end
+ end
# return the y co-ord we finished on
return y + baseline - offset
end