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